Oracle NVL problem - sql

I'm trying to pass a null value as something else in my db and it seems to work without a Where clause like so
select NVL(column1, '0') column1 from table1
produces this
0 test1
0 test2
1 test3
But when I add the where clause like so
select NVL(column1, '0') column1 from table1 where column1 <=1
it produces this
1 test3
But now if I add the following to the query it works
select NVL(column1, '0') column1
from table1
where NVL(column1, '0') <=1
But it seems like a long way round to get the value to show correctly with a Where clause
Any ideas what i'm doing wrong?
Thanks in advance

You cannot refer to an alias defined in the SELECT list from the WHERE clause. So if you apply the NVL function in the SELECT list, you'd need the same function call in the WHERE clause (as you've demonstrated) or you would need to apply the function in an inline view
SELECT column1
FROM (SELECT nvl(column1, 0) column1
FROM table1)
WHERE column1 <= 1
Note that for general sanity, if COLUMN1 is a NUMBER, the second parameter to NVL should also be a NUMBER. Otherwise, you're going to do implicit conversions and your comparison operations may end up using string comparison semantics rather than numeric comparison semantics. Since the string '12' is less than the string '2', that can lead to unexpected results.

you have shown the correct way.
an alternative would be to say
OR column IS NULL

Related

DB2 SQL Split Record by Delimiter if exists

I would like to return the portion of a string BEFORE a delimiter (in this case, a hyphen). What I find is that DB2 is throwing an error because there's an inconsistency in the values of the column where some records have a hyphen meanwhile others do not. So, I'd like to return the string before hyphen if it exists, otherwise just return the string as is.
Example shown with COLUMN1 below:
ID
COLUMN1
1
ASHJE-JFE
2
QER-SK
3
KSETK
4
SDJ-EJLF
I wrote the following query to return the string prior/before '-' but, I get the following error:
The statement was not executed because a numeric argument of a scalar
function is out of range.
I believe this is because there are records where a hyphen does not exist...
select distinct column1, locate('-',column1), substr(column1,1, (locate('-',column1) - 1)) from db2.table
where column1 is not null
fetch first 25 rows only
with ur
Does anyone know how to accomplish something similar but return the string as is when a hyphen does not exist? Thank you!
You may use the following expression:
SUBSTR (COLUMN1, 1, COALESCE (NULLIF (LOCATE ('-', COLUMN1), 0) - 1, LENGTH (COLUMN1)))
You can use a CASE statement to search for the hyphen along with a LIKE operator and % wildcards.
SELECT ID, column1,
CASE WHEN column1 LIKE '%-%'
THEN substr(column1,1,locate('-',column1)-1)
ELSE column1
END AS column2
FROM fileName
WHERE column1 IS NOT NULL
FETCH FIRST 25 ROWS ONLY
See Fiddle
Result:
ID
COLUMN1
COLUMN2
1
ASHJE-JFE
ASHJE
2
QER-SK
QER
3
KSETK
KSETK
4
SDJ-EJLF
SDJ

Snowflake if String Pattern replace

I have column which should only contain numbers[0-9], But in some cases we started see alphanumeric[eg:p8eSadfghrc] values in that column.
I wanna write a condition where if the value is not completely numeric{0-9}, i wanna replace it with another value from another column.
Something like this?
update t
set col = <other value>
where regexp_like(col, '[^0-9]');
This updates the data. You could also just do this in a query:
select t.*,
(case when regexp_like(col, '[^0-9]') then <other value> else col end)
from t;
In Snowflake, I would recommend try_to_decimal(). It attempts to convert the number to a decimal value (you control the target precision and scale with arguments) and rturns null if that fails:
select t.*, case when try_to_decimal(mycol) is null then myothercol else mycol end newcol
from mytable
If you want an update statement:
update mytable
set mycol = myothercol
where try_to_decimal(mycol) is null
When given no second and third argument, the number of allowed digits is 38, with 0 decimals (which seems to be what you want).

group by with empty string

I need to make 2 selects and put them together with an UNION. So far, so good. The problem is, for fields with no value, I can put just "0" instead of an actual column, but what do I put for string values? I know that the following example doesn't work:
Select field1, field2, 0, 0 from AnyTable
...
UNION
Select '','',sum(field3),sum(field4) from AnyTable2
...
So, what do I use instead of ' '?
An zero length string in Oracle is considered as NULL. You need to use NVL function to convert the NULL values into some value.
SQL> select nvl(null, 'This is null') val from dual;
VAL
------------
This is null
Note : Take care of the individual DATA TYPE of each column in the UNION

Oracle "Select Level from dual" does not work as expected with to_number result

Why does
select *
from (
SELECT LEVEL as VAL
FROM DUAL
CONNECT BY LEVEL <= 1000
ORDER BY LEVEL
) n
left outer join (select to_number(trim(alphanumeric_column)) as nr from my_table
where NOT regexp_like (trim(alphanumeric_column),'[^[:digit:]]')) d
on n.VAL = d.nr
where d.nr is null
and n.VAL >= 100
throw a ORA-01722 invalid number (reason is the last row, n.VAL), whereas the similar version with numeric columns im my_table works fine:
select *
from (
SELECT LEVEL as VAL
FROM DUAL
CONNECT BY LEVEL <= 1000
ORDER BY LEVEL
) n
left outer join (select numeric_column as nr from my_table) d
on n.VAL = d.nr
where d.nr is null
and n.VAL >= 100
given that numeric_column is of type number and alphanumeric_column of type nvarchar_2. Note that the upper example works fine without the numerical comparison (n.VAL >= 100).
Does anybody know?
This problem was driving me crazy. I narrowed the problem to a simpler query
SELECT *
FROM (SELECT TO_NUMBER(TRIM (alphanumeric_column)) AS nr
FROM my_table
WHERE NOT REGEXP_LIKE (TRIM (alphanumeric_column), '[^[:digit:]]')) d
WHERE d.nr > 1
With alphanumeric_colum values of ('100','200','XXXX'); Running the above statement gave the "invalid number" error. I then made a slight change to the query to use the CAST function instead of TO_NUMBER:
SELECT *
FROM (SELECT CAST (TRIM (alphanumeric_column) AS NUMBER) AS nr
FROM my_table
WHERE NOT REGEXP_LIKE (TRIM (alphanumeric_column), '[^[:digit:]]')) d
WHERE d.nr > 1
And this correctly returned - 100, 200. I would think that those functions would be similar in behavior. It almost appears as though oracle is trying to evaluate the d.nr > 1 constraint before the view is constructed, which makes no sense. If anyone can shed light on why this is happening, I would be grateful. See SQLFiddle example
UPDATE: I did some more digging, because I don't like not knowing why something just works. I ran EXPLAIN PLAN on both queries and got some interesting results.
For the query that failed, the predicate information looks like this:
1 - filter(TO_NUMBER(TRIM("ALPHANUMERIC_COLUMN"))>1 AND NOT
REGEXP_LIKE (TRIM("ALPHANUMERIC_COLUMN"),'[^[:digit:]]'))
You will notice that the TO_NUMBER function is called first in the AND condition, then
the regexp to exclude alpha values. I am thinking oracle maybe does a short-circuit evaluation with the AND condition, and since it is executing TO_NUMBER first, it fails.
However, when we use the CAST function, the evaluation order is swapped, and the
regexp exclusion is evaluated first. Since for the alpha values, it is false, then the
second part of the AND clause is not evaluated, and the query works.
1 - filter( NOT REGEXP_LIKE (TRIM("ALPHANUMERIC_COLUMN"),'[^[:digit:]
]') AND CAST(TRIM("ALPHANUMERIC_COLUMN") AS NUMBER)>1)
Oracle can be strange sometimes.
I believe when it comes to the Predicate (where) clause, Oracle can/will reorder the entire plan as it sees fit. So with regard to the predicate, it will short-circuit (as OldProgrammer noted) the evaluation however it wants, and you wont be able to guarantee the exact order it occurs.
In your current SQL, you are depending on the predicate to remove non numbers. One option would be to not use "WHERE NOT regexp_like ..." and instead use regexp_substr with coalesce. For example:
create table t_tab2
(
col varchar2(10)
);
create index t_tab2_idx on t_tab2(col);
insert into t_tab2
select level from dual
connect by level <= 100;
insert into t_tab2 values ('123ABC456');
commit;
-- select values > 95 (96->100 exclude non numbers)
select d.* from
(
select COALESCE(TO_NUMBER(REGEXP_SUBSTR(trim(col), '^\d+$')), 0) as nr
from t_tab2
) d
where d.nr > 95;
This should run without throwing invalid number error. Note that the coalesce will return the number 0 for any non numbers coming from the data, you may want to change that based on your needs and data.

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