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

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;

Related

How to find any variation of the number zero; 0, 0.0, 00.00, 0.000, 000.0, etc

I'm using oracle pl/sql and I have the following sql query.
SELECT column_1 FROM table_1
WHERE column_1 != null
AND column_1 != ' '
AND column_1 != '0'
AND column_1 != '0.0'
AND column_1 != '00.00'
AND column_1 != '000.000'
AND column_1 != '0.000'
AND column_1 != '000.0'
etc..
etc..
As you can see, column_1 is a text field.
What I need to do is remove anything that is any variation of zero, null, or, empty.
Is there anyway to do this without having to list out each individual variation with a WHERE clause?
Something like this is what I was hoping for, but open to any suggestions.
SELECT column_1 FROM table_1
WHERE column_1 != null
AND column_1 != ' '
AND ConvertToNumberFormat(column_1) != ConvertToNumberFormat(0)
One method would be to simply convert to a number and compare to zero:
where cast(column_1 as number) = 0 or column_1 is null or column_1 = ' '
That might not work if the column could have non-digit characters. In that case, you could use a regular expression. Or even:
where regexp_like(column_1, '(0*[.])?[0]+') or column_1 is null or column_1 = ' '
Assuming the assignment is to exclude all strings that consist entirely of zero's, at most one decimal point and possibly leading and/or trailing spaces, here is one way to do it, which requires only standard string functions (and therefore should be faster than any regular-expression solution). Note that this also excludes strings made up entirely of spaces (and NULL).
select * from table_1
where translate(trim(column_1), '.0', '.') <> '.' or trim(column_1) = '.';
Explanation: TRIM() will remove all leading and trailing spaces. TRANSLATE() will change all decimal points to themselves, it will delete (translate to empty string) all 0's, and it will leave all other characters unchanged. If what is left is just one single decimal point, the row should be excluded. Also if what is left is NULL it should be excluded (then the string was all zeros, no decimal point, with or without leading and/or trailing spaces; this includes strings that are all-spaces as well as NULL, the empty string.)
Now, this will also exclude a string that's all spaces, a single decimal point, and no zeros on either side of it. I assume that should not be excluded; that's why there's the OR part of the WHERE clause.
You may be wondering why TRANSLATE() needs to translate the decimal point to itself. It doesn't; but you must translate something to itself, because Oracle has some very strange ideas about NULL and empty string; TRANSLATE(str, '0', '') returns NULL, it doesn't just zap all the zeros.
You can use the regular expression which matches:
^ the start-of-the-string; then
\s* zero-or-more white-space characters; then
0* zero-or-more zero characters; then
\.? zero-or-one dot character; then
0* zero-or-more zero characters; then
\s* zero-or-more white-space characters; then
$ the end-of-the-string.
(You need to match the start and end of the string otherwise it could match sub-strings like 400.03.)
Giving the regular expression ^\s*0*\.?0*\s*$ which you can use like this:
Oracle Setup:
CREATE TABLE table_1 ( column_1 ) AS
SELECT ' ' FROM DUAL UNION ALL
SELECT '0' FROM DUAL UNION ALL
SELECT '0.' FROM DUAL UNION ALL
SELECT '.0' FROM DUAL UNION ALL
SELECT '0.0' FROM DUAL UNION ALL
SELECT '00.0' FROM DUAL UNION ALL
SELECT '4.0' FROM DUAL UNION ALL
SELECT '200.0' FROM DUAL UNION ALL
SELECT '.03' FROM DUAL UNION ALL
SELECT ' 00.000' FROM DUAL UNION ALL
SELECT ' 0000 ' FROM DUAL UNION ALL
SELECT NULL FROM DUAL;
Query:
SELECT column_1
FROM table_1
WHERE NOT REGEXP_LIKE( column_1, '^\s*0*\.?0*\s*$' );
Output:
COLUMN_1
--------
4.0
200.0
.03
You could also create a user-defined function to convert the value to a number and handle any exceptions in the conversion:
Oracle Setup:
CREATE FUNCTION ValidateNumber(
value IN VARCHAR2
) RETURN NUMBER
IS
BEGIN
RETURN TO_NUMBER( value );
EXCEPTION
WHEN OTHERS THEN
RETURN NULL;
END;
/
Query:
SELECT column_1
FROM table_1
WHERE ValidateNumber( column_1 ) != 0;
Output:
COLUMN_1
--------
4.0
200.0
.03

PL SQL replace conditionally suggestion

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')

Query to remove all non-digit but only keep last period/dot

Struggle to design a regular expression to filter field value from varchar2 to number, so that it can remove all non-digit and only left the last period in the string, so that
"about 1,000.00" return 1000.00 or 1000
"3,000,000.000" return 300000.000 or 3000000
"3.000.000.000" return return 3000000.000 or 3000000
"a^*3^%*(C4.5d*9" return 34.59
Any method just change the string into accurate convertible string that can be converted by to_number()
I use
SELECT REGEXP_REPLACE(field_value, '[^0-9\.]+', '') from dual;
but can't resolve the 3rd case....
Because the regex in oracle are somewhat limited I don't think it's possible only using regexp_replace. You could do a workaround like this:
SELECT
CASE
WHEN last_dot < 2 THEN digits_and_dots
ELSE REPLACE(SUBSTR(digits_and_dots, 1, last_dot - 1), '.') ||
SUBSTR(digits_and_dots, last_dot)
END
FROM (
SELECT
INSTR(digits_and_dots, '.', -1) last_dot,
digits_and_dots
FROM (
SELECT
REGEXP_REPLACE(field_value, '[^0-9\.]+', '') digits_and_dots
FROM DUAL
) t
) o
Here's a way to do it, assuming there is one decimal character. The value you are working with is a string so I think of the decimal that we want to keep as a separator of the string and split it into 2 parts based on that. The first part is all characters leading up to but not including the last decimal, the second part is the last decimal and all characters after it. Then apply the replace, getting rid of everything that is not a number from the first part, and everything that is not a number or a decimal from the second part, then concatenate them together. Needs more testing with varied inputs but you get the idea. All these regular expressions are kind of expensive though so I doubt this will be the fastest solution.
with tbl(str) as (
select 'about 1,000.00' from dual union
select '3,000,000.000' from dual union
select '3.000.000.000' from dual union
select 'a^*3^%*(C4.5d*9' from dual
)
select str original,
regexp_replace(regexp_substr(str, '^(.*)\.', 1, 1, NULL, 1), '[^0-9]+', '') ||
regexp_replace(regexp_substr(str, '.*(\..*)$', 1, 1, NULL, 1), '[^0-9\.]+', '') Converted
from tbl;
SQL> /
ORIGINAL CONVERTED
--------------- ---------------
3,000,000.000 3000000.000
3.000.000.000 3000000.000
a^*3^%*(C4.5d*9 34.59
about 1,000.00 1000.00
SQL>
Shortest way is as follows:
select regexp_substr('a^*3^%*(C4.5d*9s','\d+\.\d+') from dual;
or
select regexp_replace('a^*3^%*(C4.5d*9s', '[^0.0-9]', '') from dual;

Insert character between string Oracle SQL

I need to insert character string after each character in Oracle SQL.
Example:
ABC will A,B,C
DEFG will be D,E,F,G
This question gives only one character in string
Oracle insert character into a string
Edit: As some fellows have mentioned, Oracle does not admit this regex. So my approach would be to do a regex to match all characters, add them a comma after the character and then removing the last comma.
WITH regex AS (SELECT REGEXP_REPLACE('ABC', '(.)', '\1,') as reg FROM dual) SELECT SUBSTR(reg, 1, length(reg)-1) FROM regex;
Note that with the solution of rtrim there could be errors if the string you want to parse has a final ending comma and you don't want to remove it.
Previous solution: (Not working on Oracle)
Check if this does the trick:
SELECT REGEXP_REPLACE('ABC', '(.)(?!$)', '\1,') FROM dual;
It does a regexp_replace of every character, but the last one for the same character followed by a ,
To see how regexp_replace works I recommend you: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions130.htm
SELECT rtrim(REGEXP_REPLACE('ABC', '(.)', '\1,'),',') "REGEXP_REPLACE" FROM dual;
You could do it using:
REGEXP_REPLACE
RTRIM
For example,
SQL> WITH sample_data AS(
2 SELECT 'ABC' str FROM dual UNION ALL
3 SELECT 'DEFG' str FROM dual UNION ALL
4 SELECT 'XYZ' str FROM dual
5 )
6 -- end of sample_data mimicking a real table
7 SELECT str,
8 rtrim(regexp_replace(str, '(\w?)', '\1,'),',') new_str
9 FROM sample_data;
STR NEW_STR
---- ----------
ABC A,B,C
DEFG D,E,F,G
XYZ X,Y,Z
Since there is no way to negate the end of string in an Oracle regex (that does not support lookarounds), you may use
SELECT REGEXP_REPLACE(
REGEXP_REPLACE('ABC', '([^,])([^,])','\1,\2'),
'([^,])([^,])',
'\1,\2')
AS Result from dual
See the DB Fiddle. The point here is to use REGEXP_REPLACE with ([^,])([^,]) pattern twice to cater for consecutive matches.
The ([^,])([^,]) pattern matches any non-comma char into Group 1 (\1) and then any non-comma char into Group 2 (\2), and inserts a comma in between them.

oracle searching word within the input string

i have to find out INPUT string word found within the other string that is pipe delimited,i am trying below way but it is surprisingly return 'Y' instead of 'N'.please let me know what i am doing in wrong in below cast statement.
CASE
WHEN REGEXP_INSTR('TCS|XY|XZ','CS',1,1,1,'i') > 0
THEN 'Y'
ELSE 'N'
END
Regards,
Raj
There is really no need to use regexp_instr() regular expression function. If you just need to know if a particular character literal is part of another character literal, instr() function will completely cover your needs:
with t1(col) as(
select 'TCS|XY|XZ' from dual union all
select 'TAB|XY|XZ' from dual
)
select col
, case
when instr(col, 'CS') > 0
then 'Y'
else 'N'
end as Is_Part
from t1
Result:
COL IS_PART
--------- -------
TCS|XY|XZ Y
TAB|XY|XZ N
Edit
If you need to take vertical bars into consideration - returning yes only if there is a standalone CS sub-string surrounded by vertical bars |CS| then yes, you could use regexp_instr() regular expression function as follows:
with t1(col) as(
select 'TCS|XY|XZ|' from dual
)
select col
, case
when regexp_instr(col, '(\||^)CS(\||$)', 1, 1, 0, 'i') > 0
then 'YES'
else 'NO'
end as res
from t1
Result:
COL RES
---------- ---
TCS|XY|XZ| NO
Note: If a character literal is dynamic you could use a concatenation operator || to form a search pattern '(\||^)' || <<'character literal', column or variable>> || '(\||$)'
The first field (TCS) contains CS which counts as a match.
If you want to match an entire field you can do like this:
CASE
WHEN REGEXP_INSTR('|' || 'TCS|XY|XZ' || '|' , '\|' || 'CS' || '\|',1,1,1,'i') > 0
THEN 'Y'
ELSE 'N'
END
Add the delimiter to your query string to "anchor" the search to whole fields. To be able to match the first and last field I also added the delimiter to the searched string.