PL SQL replace conditionally suggestion - sql

I need to replace the entire word with 0 if the word has any non-digit character. For example, if digital_word='22B4' then replace with 0, else if digital_word='224' then do not replace.
SELECT replace_funtion(digital_word,'has non numeric character pattern',0,digital_word)
FROM dual;
I tried decode, regexp_instr, regexp_replace but could not come up with the right solution.
Please advise.
Thank you.

the idea is simple - you need check if the value is numeric or not
script:
with nums as
(
select '123' as num from dual union all
select '456' as num from dual union all
select '7A9' as num from dual union all
select '098' as num from dual
)
select n.*
,nvl2(LENGTH(TRIM(TRANSLATE(num, ' +-.0123456789', ' '))),'0',num)
from nums n
result
1 123 123
2 456 456
3 7A9 0
4 098 098
see more articles below to see which way is better to you
How can I determine if a string is numeric in SQL?
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:15321803936685
How to tell if a value is not numeric in Oracle?

You might try the following:
SELECT CASE WHEN REGEXP_LIKE(digital_word, '\D') THEN '0' ELSE digital_word END
FROM dual;
The regular expression class \D matches any non-digit character. You could also use [^0-9] to the same effect:
SELECT CASE WHEN REGEXP_LIKE(digital_word, '\D') THEN '0' ELSE digital_word END
FROM dual;
Alternately you could see if the value of digital_word is made up of nothing but digits:
SELECT CASE WHEN REGEXP_LIKE(digital_word, '^\d+$') THEN digital_word ELSE '0' END
FROM dual;
Hope this helps.

The fastest way is to replace all digits with null (to simply delete them) and see if anything is left. You don't need regular expressions (slow!) for this, you just need the standard string function TRANSLATE().
Unfortunately, Oracle has to work around their own inconsistent treatment of NULL - sometimes as empty string, sometimes not. In the case of the TRANSLATE() function, you can't simply translate every digit to nothing; you must also translate a non-digit character to itself, so that the third argument is not an empty string (which is treated as a real NULL, as in relational theory). See the Oracle documentation for the TRANSLATE() function. https://docs.oracle.com/cd/E11882_01/server.112/e41084/functions216.htm#SQLRF06145
Then, the result can be obtained with a CASE expression (or various forms of NULL handling functions; I prefer CASE, which is SQL Standard):
with
nums ( num ) as (
select '123' from dual union all
select '-56' from dual union all
select '7A9' from dual union all
select '0.9' from dual
)
-- End of simulated inputs (for testing only, not part of the solution).
-- SQL query begins BELOW THIS LINE. Use your own table and column names.
select num,
case when translate(num, 'z0123456789', 'z') is null
then num
else '0'
end as result
from nums
;
NUM RESULT
--- ------
123 123
-56 0
7A9 0
0.9 0
Note: everything here is in varchar2 data type (or some other kind of string data type). If the results should be converted to number, wrap the entire case expression within TO_NUMBER(). Note also that the strings '-56' and '0.9' are not all-digits (they contain non-digits), so the result is '0' for both. If this is not what you needed, you must correct the problem statement in the original post.

Something like the following update query will help you:
update [table] set [col] = '0'
where REGEXP_LIKE([col], '.*\D.*', 'i')

Related

Remove only first character from the field if it is 0

How to write sql query which will show values skipping first character if it is 0 (only the first character). All values are 3 characters long.
Examples:
numbers
123
023
003
102
should display as follows (after executing the query)
numbers
123
23
03
102
I used the following solution, but it removes all 0's, not just the first. How to fix it so that it only removes the first character if it is 0.
SUBSTRING(numbers, PATINDEX('%[^0]%', numbers+'.'), LEN(numbers))
I will be grateful for your help.
You can use CASE expression:
SELECT CASE WHEN LEFT(numbers, 1) = '0' THEN RIGHT(numbers, 2) ELSE numbers END AS FormattedNumbers
why not using simple substr() ?
select case when substr(mycol,1,1)='0' then substr(mycol,2) else mycol end
from my table
you did not mention your DB so i assumed its oracle. This will work in any RDBMS.
You can use charindex and substring methods to do string manipulation :)
select
case when charindex('0', number) = 1
then substring(number, 2, len(number))
else number end
from (
select '123' number
union all
select '023'
union all
select '003'
union all
select '102'
) a

Number pattern matching for a range in SQL

I want to select columns in Oracle: Code and Number based on the condition,
If number is any of the following return ā€œEā€: ###7890001 through ###7890999
where #(first 3 digits) could be any digit. These numbers are saved as string values so I need to cast them in appropriate data type too.
The data returned would look like this:
Code Number
E 2347890001
E 9567890456
E 5647890999
Thanks in adavnce!!
Assuming your problem description is correct, your inputs are always 10-digit strings, and you must return code E if and only if the four digits in the middle are 7890. Right? If so, the simplest solution is to use the LIKE condition. Note that number is a reserved keyword, so it can't be used as column name (I hope your column name is not really number, is it?)
The string in the LIKE condition is hard to read; it consists of three underscore characters, then 7890, then three more underscore characters. The underscore stands for exactly one character (any character).
with
simulated_data (num) as (
select '1234567890' from dual union all
select '3337890456' from dual union all
select '7897890455' from dual union all
select '9998887774' from dual
)
select num, case when num like '___7890___' then 'E' end as code
from simulated_data
order by num
;
NUM CODE
---------- ----
1234567890
3337890456 E
7897890455 E
9998887774
You can use to_number with mod and on conversion error (oracle 12.2 or higher) as following:
Select 'E' as code,
number
From your_table
Where MOD(TO_NUMBER(number DEFAULT -1 ON CONVERSION ERROR), 10000000)
between 7890001 and 7890999;
Cheers!!
This should work:
where code = 'E' and
substr(number, -7) between '7890001' and '7890999'
EDIT:
select (case when substr(number, -7) between '7890001' and '7890999' then 'E' end) as code

Trim leading zeroes if it is numeric and not trim zeroes if it is alphanumeric

In a column, there are numeric and alphanumeric values starting with '0'.
How to trim leading zeroes if it is numeric and should not trim zeroes if it is alphanumeric in Oracle.
I need to use it in WHERE Condition.
Ex.
000012345 should be 12345.
012321 should be 12321.
00012JY12 should be 00012JY12.
This is what I tried:
SELECT COUNT(*)
FROM <TABLE 1> ONN, <TABLE 2> SV
WHERE SV.CSA_SHP_VISIT_STG_SEQ_ID=ONN.CSA_SHOP_VIST_SEQ_ID
AND EXISTS (SELECT '1' FROM <TABLE 3> TMP
WHERE TRIM(SV.WORK_ORDER_NUM) = TRIM(TMP.WORK_ORDER_NUM)
AND PLANT IN ('EMA')
AND regexp_replace(TRIM(ONN.INSTLD_PART), '^0+([[:digit:]]+)$',
'\1')=TRIM(TMP.INSTLD_PART) AND
TRIM(ONN.INSTLD_PART_SERIAL_NUM)=TRIM(TMP.INSTLD_PART_SERIAL_NUM) AND
nvl(to_number(TRIM(ONN.INSTLD_PART_CSN)),0)=
nvl(to_number(TRIM(TMP.INSTLD_PART_CSN)),0)
and REGEXP_LIKE(tmp.INSTLD_PART_CSN, '^-?\d+(\.\d+)?$'))
Whenever possible (in this case it is), use standard string functions, such as SUBSTR, INSTR, TRANSLATE, etc. instead of regular expression functions. Regular expressions are much more powerful, but also much more time consuming (precisely for that reason), so they should be used only when really needed.
If the column name is str, then:
case when translate(str, 'z0123456789', 'z') is null
then ltrim(str, '0')
else str end
TRANSLATE will translate z to itself, all the digits to NULL, and all other characters to themselves. (Alas, the z, or SOME non-digit character, is needed.)
The input is all-digits if and only if the result of TRANSLATE is NULL.
Demo:
select str, case when translate(str, 'z0123456789', 'z') is null
then ltrim(str, '0')
else str
end as new_str
from
(
select '000012345' as str from dual union all
select '012321' as str from dual union all
select '00012JY12' as str from dual
);
STR NEW_STR
--------- ---------
000012345 12345
012321 12321
00012JY12 00012JY12
Use regexp_replace(col, '^0+([[:digit:]]+)$', '\1').
This replaces strings that only consist of leading zeros and trailing digits with the trailing digits alone, thus removing the zeros.
Sample query:
select col, regexp_replace(col, '^0+([[:digit:]]+)$', '\1') as newvalue
from
(
select '000012345' as col from dual
union all
select '012321' as col from dual
union all
select '00012JY12' as col from dual
);
Result:
COL | NEWVALUE
----------+----------
000012345 | 12345
012321 | 12321
00012JY12 | 00012JY12
If you are using Oracle 12.2, you could use the error handling of the CAST expression.
https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/CAST.html#GUID-5A70235E-1209-4281-8521-B94497AAEF75
Along that:
CASE WHEN CAST(<expression> AS NUMERIC DEFAULT NULL ON CONVERSION ERROR) IS NULL
THEN <expression>
ELSE TRIM(LEADING '0' FROM <expression>)
END
Replace <expression> with your column name (or whatever). I'm using null as magic value in the default null on conversion error clause but this does no harm as null input will then just match the THEN clause and pass the null through.
You can create a function that check if it is numeric or not, see this link for the function sample,
check if "it's a number" function in Oracle
You can TRIM the zeros of numeric value just by adding 0 to it,
Sample code:
--get the IS_NUMERIC function from the link
SELECT DECODE(IS_NUMERIC(col1), 'Y', col1+0, 'N', col1)
FROM your_table;

to_number from char sql

I have to select only the IDs which have only even digits (an ID looks like: p19 ,p20 etc). That is, p20 is good (both 2 and 0 are even digits); p18 is not.
I thought to use substr to get each number from the IDs and then see if it's even .
select from profs
where to_number(substr(id_prof,2,2))%2=0 and to_number(substr(id_prof,3,2))%2=0;
IF you need all rows consist of 'p' in beginning and even digits on tail It should look like:
select *
from profs
where regexp_like (id_prof, '^p[24680]+$');
with
profs ( prof_id ) as (
select 'p18' from dual union all
select 'p24' from dual union all
select 'p53' from dual
)
-- End of test data; what is above this line is NOT part of the solution.
-- The solution (SQL query) begins here.
select *
from profs
where length(prof_id) = length(translate(prof_id, '013579', '0'));
PROF_ID
-------
p24
This solution should work faster than anything using regular expressions. All it does is to replace 0 with itself and DELETE all odd digits from the input string. (The '0' is included due to a strange but documented behavior of translate() - the third argument can't be empty). If the length of the input string doesn't change after the translation, that means the input string didn't have any odd digits.
where mod(to_number(regexp_replace(id_prof, '[^[:digit:]]', '')),2) = 0

Finding rows that don't contain numeric data in Oracle

I am trying to locate some problematic records in a very large Oracle table. The column should contain all numeric data even though it is a varchar2 column. I need to find the records which don't contain numeric data (The to_number(col_name) function throws an error when I try to call it on this column).
I was thinking you could use a regexp_like condition and use the regular expression to find any non-numerics. I hope this might help?!
SELECT * FROM table_with_column_to_search WHERE REGEXP_LIKE(varchar_col_with_non_numerics, '[^0-9]+');
To get an indicator:
DECODE( TRANSLATE(your_number,' 0123456789',' ')
e.g.
SQL> select DECODE( TRANSLATE('12345zzz_not_numberee',' 0123456789',' '), NULL, 'number','contains char')
2 from dual
3 /
"contains char"
and
SQL> select DECODE( TRANSLATE('12345',' 0123456789',' '), NULL, 'number','contains char')
2 from dual
3 /
"number"
and
SQL> select DECODE( TRANSLATE('123405',' 0123456789',' '), NULL, 'number','contains char')
2 from dual
3 /
"number"
Oracle 11g has regular expressions so you could use this to get the actual number:
SQL> SELECT colA
2 FROM t1
3 WHERE REGEXP_LIKE(colA, '[[:digit:]]');
COL1
----------
47845
48543
12
...
If there is a non-numeric value like '23g' it will just be ignored.
In contrast to SGB's answer, I prefer doing the regexp defining the actual format of my data and negating that. This allows me to define values like $DDD,DDD,DDD.DD
In the OPs simple scenario, it would look like
SELECT *
FROM table_with_column_to_search
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^[0-9]+$');
which finds all non-positive integers. If you wau accept negatiuve integers also, it's an easy change, just add an optional leading minus.
SELECT *
FROM table_with_column_to_search
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^-?[0-9]+$');
accepting floating points...
SELECT *
FROM table_with_column_to_search
WHERE NOT REGEXP_LIKE(varchar_col_with_non_numerics, '^-?[0-9]+(\.[0-9]+)?$');
Same goes further with any format. Basically, you will generally already have the formats to validate input data, so when you will desire to find data that does not match that format ... it's simpler to negate that format than come up with another one; which in case of SGB's approach would be a bit tricky to do if you want more than just positive integers.
Use this
SELECT *
FROM TableToSearch
WHERE NOT REGEXP_LIKE(ColumnToSearch, '^-?[0-9]+(\.[0-9]+)?$');
After doing some testing, i came up with this solution, let me know in case it helps.
Add this below 2 conditions in your query and it will find the records which don't contain numeric data
and REGEXP_LIKE(<column_name>, '\D') -- this selects non numeric data
and not REGEXP_LIKE(column_name,'^[-]{1}\d{1}') -- this filters out negative(-) values
Starting with Oracle 12.2 the function to_number has an option ON CONVERSION ERROR clause, that can catch the exception and provide default value.
This can be used for the test of number values. Simple set NULL when the conversion fails and filer all not NULL values.
Example
with num as (
select '123' vc_col from dual union all
select '1,23' from dual union all
select 'RV12P2000' from dual union all
select null from dual)
select
vc_col
from num
where /* filter numbers */
vc_col is not null and
to_number(vc_col DEFAULT NULL ON CONVERSION ERROR) is not null
;
VC_COL
---------
123
1,23
From http://www.dba-oracle.com/t_isnumeric.htm
LENGTH(TRIM(TRANSLATE(, ' +-.0123456789', ' '))) is null
If there is anything left in the string after the TRIM it must be non-numeric characters.
I've found this useful:
select translate('your string','_0123456789','_') from dual
If the result is NULL, it's numeric (ignoring floating point numbers.)
However, I'm a bit baffled why the underscore is needed. Without it the following also returns null:
select translate('s123','0123456789', '') from dual
There is also one of my favorite tricks - not perfect if the string contains stuff like "*" or "#":
SELECT 'is a number' FROM dual WHERE UPPER('123') = LOWER('123')
After doing some testing, building upon the suggestions in the previous answers, there seem to be two usable solutions.
Method 1 is fastest, but less powerful in terms of matching more complex patterns.
Method 2 is more flexible, but slower.
Method 1 - fastest
I've tested this method on a table with 1 million rows.
It seems to be 3.8 times faster than the regex solutions.
The 0-replacement solves the issue that 0 is mapped to a space, and does not seem to slow down the query.
SELECT *
FROM <table>
WHERE TRANSLATE(replace(<char_column>,'0',''),'0123456789',' ') IS NOT NULL;
Method 2 - slower, but more flexible
I've compared the speed of putting the negation inside or outside the regex statement. Both are equally slower than the translate-solution. As a result, #ciuly's approach seems most sensible when using regex.
SELECT *
FROM <table>
WHERE NOT REGEXP_LIKE(<char_column>, '^[0-9]+$');
You can use this one check:
create or replace function to_n(c varchar2) return number is
begin return to_number(c);
exception when others then return -123456;
end;
select id, n from t where to_n(n) = -123456;
I tray order by with problematic column and i find rows with column.
SELECT
D.UNIT_CODE,
D.CUATM,
D.CAPITOL,
D.RIND,
D.COL1 AS COL1
FROM
VW_DATA_ALL_GC D
WHERE
(D.PERIOADA IN (:pPERIOADA)) AND
(D.FORM = 62)
AND D.COL1 IS NOT NULL
-- AND REGEXP_LIKE (D.COL1, '\[\[:alpha:\]\]')
-- AND REGEXP_LIKE(D.COL1, '\[\[:digit:\]\]')
--AND REGEXP_LIKE(TO_CHAR(D.COL1), '\[^0-9\]+')
GROUP BY
D.UNIT_CODE,
D.CUATM,
D.CAPITOL,
D.RIND ,
D.COL1
ORDER BY
D.COL1