Can't compare 2 strings - strange errors - sql

I have this code in my procedure:
IF old_value_l = new_value_l THEN
dummy_val_l := 0;
ELSIF old_value_l != new_value_l THEN -- This is line 4019 mentioned in the error
-- some code here
END IF;
But it is throwing some strange errors:
ORA-01722: invalid number
ORA-06512: at "DBS.MONITORING", line 4019
ORA-06512: at line 12
It clearly says that the number is invalid, but wait a minute, what number?
Both old_value_l and new_value_l are declared as VARCHAR2(255 CHAR)
and here is how:
new_value_l history.new_value%TYPE;
old_value_l history.old_value%TYPE;
Why even when both of the variables are declared as VARCHAR2 it says that the number is invalid?
I know that I'm missing an extremely small part here, but I can't spot it or understand my mistake at this momment.
EDIT
Values I'm comparing:
new_value_l = 15 and old_value_l = 10

Code like this does not tell us they are numbers, it tells that first variable has a type like column new_value in history table, and the second variable has a type like old_value in history table.
new_value_l history.new_value%TYPE;
old_value_l history.old_value%TYPE;
So, you need to:
Ensure that both of these columns have the same datatype, VARCHAR2
Sometimes errors appear in a line, while actual error is in next or previous line, so you have to check them too
Try to check there is no spaces stored in values by using trim
...
ELSIF trim(old_value_l) != trim(new_value_l)
...
I think you have to handle null values too.
...
ELSIF trim(old_value_l) != trim(new_value_l)
...

It will be converted to a number automatically. So, you can test it as per the below:
select 1 from dual where '114' >120;
select 1 from dual where '125' >120;
You'd better check whether each value has some characters which cannot be converted to a number, such as CRLF.

Related

postgresql 12 function strange value / length behaviour

I have a char(13) column named a in table test.
I created a classic before insert function with the following line :
raise notice 'a: -->%<-- len = %', new.a, length(new.a);
When I run insert into test (a) values('1');, I get the following output :
a: -->1 <-- len = 1
Strange, no ?
No, not strange at all. char is a blank padded data type. So if you assign '1' to your column, it is actually stored as '1 '.
The solution is simple: don't use char(n)

Why is Oracle not treating '' as NULL in a BETWEEN with NVL comparison

I have this query, not filling in any data as the variables. Expecting a row; I get nothing.
select 1 from dual
where SYSDATE BETWEEN NVL(:START_DATE, SYSDATE) AND NVL(:END_DATE, SYSDATE)
I try this, expecting a row, and I get a row.
select 1 from dual
where SYSDATE BETWEEN SYSDATE AND SYSDATE
Something is not working as expected. An NVL should treat '' as NULL.
I try this, expecting a row, and I get a row.
select 1 from dual
where '' is null
If '' IS NULL, which we have now proven, this should return a row. But it's not.
select 1 from dual
where SYSDATE BETWEEN NVL('', SYSDATE) AND NVL('', SYSDATE)
There are some interesting answers here, but I'm not sure anyone has yet explained quite why your query returns no rows. (Apologies if they have and I missed it - just ignore me then!)
It is because NVL('', SYSDATE) is returning a character string, since the first argument '' is a character strng. So NVL('', SYSDATE) is equivalent to NVL('', TO_CHAR(SYSDATE)). And since we haven't specified the format for the TO_CHAR, Oracle will use the default which usually doesn't include a time component.
So this:
where SYSDATE BETWEEN NVL(:START_DATE, SYSDATE) AND NVL(:END_DATE, SYSDATE)
Is being treated somethinlg like this:
where SYSDATE BETWEEN '25-APR-2020' AND '25-APR-2020'
Oracle will then convert those strings back into dates to perform the BETWEEN, so will assume 00:00:00 for the time of day. Therefore unless you run this at exactly midnight, it will not return a row.
However, if you set your default date format to include the time like this:
alter session set nls_date_format = 'DD-MON-YYYY HH24:MI:SS';
You will find it now works, because the time component of the date no longer gets lost in the implicit conversions.
Probably the best way to deal with this though is to ensure you are dealing with dates and not strings at all:
select 1 from dual
where SYSDATE BETWEEN NVL(TO_DATE(:START_DATE), SYSDATE)
AND NVL(TO_DATE(:END_DATE), SYSDATE)
It ain't what you don't know that'll kill you. It's "what you know" that just ain't so...
The reason that Oracle isn't treating '' as NULL is because '' is not a NULL - it's a zero-length string constant. Now, right here people are going to jump in and tell me I'm wrong, and that I don't know what I'm talking about, and question my parentage, and probably start a petition to deny my Social Security and kick me out of the Old Farts Home, but I'm right. Here's a demonstration:
DECLARE
vStr VARCHAR2(10) := '';
cStr CHAR(10) := '';
vNULL VARCHAR2(10) := NULL;
cNULL CHAR(10) := NULL;
nStr NUMBER := '';
nNULL NUMBER := NULL;
BEGIN
DBMS_OUTPUT.PUT_LINE('LENGTH(vStr) = ' || LENGTH(vStr));
DBMS_OUTPUT.PUT_LINE('LENGTH(cStr) = ' || LENGTH(cStr));
DBMS_OUTPUT.PUT_LINE('LENGTH(vNULL) = ' || LENGTH(vNULL));
DBMS_OUTPUT.PUT_LINE('LENGTH(cNULL) = ' || LENGTH(cNULL));
DBMS_OUTPUT.PUT_LINE('LENGTH(nStr) = ' || LENGTH(nStr));
DBMS_OUTPUT.PUT_LINE('LENGTH(nNULL) = ' || LENGTH(nNULL));
END;
Now without looking down below (yes, I know - it's hard. Try... :-) what output do you expect from the code above? If you're like most people (including me, until I tripped over this a few years ago) you'd expect it to be:
LENGTH(vStr) =
LENGTH(cStr) =
LENGTH(vNULL) =
LENGTH(cNULL) =
LENGTH(nStr) =
LENGTH(nNULL) =
That is, you'd expect the LENGTH function to return NULL when applied to all those variables - because they should all be NULL, right?
But that's not what you get (see this db<>fiddle). What you actually get is:
LENGTH(vStr) =
LENGTH(cStr) = 10
LENGTH(vNULL) =
LENGTH(cNULL) =
LENGTH(nStr) =
LENGTH(nNULL) =
Whoa! Wait!! What's that 10 doing in there?!?
Well, it's pretty simple. Think about the semantics of the CHAR data type in PL/SQL. If you assign a string to a CHAR variable or field, and the length of the string assigned is shorter than the defined length of the variable or field, the value assigned to the variable or field is padded on the right to the full defined width of the variable/field - in this case, 10 characters. So when a '' - that is, a zero-length string constant - is assigned to the variable cStr, the value assigned to the variable is padded on the right to the defined width of the variable, so cStr ends up filled with 10 blanks. But when a NULL is assigned to that same character variable it ends up being set to NULL, as expected, and the LENGTH function returns NULL, as expected.
The semantics of VARCHAR2 (and, for the moment, VARCHAR - at least until Oracle gets around to supporting ANSI semantics for VARCHAR - which they're going to do Real Soon Now (tm)) are different in Oracle. When a variable/field of type VARCHAR2 is assigned a value it doesn't do any padding; instead, it assigns only the significant characters of the source string to the variable or field, and if the resultant length of the string assigned to the variable is zero then the variable or field is set to NULL, in accordance with the Oracle rule that "zero-length string values are the same as NULL". But this happens at the time that the value is assigned to the variable. '' on its own is still a zero-length string constant.
Just remember - it ain't what you don't know that'll kill you. It's "what you know" that just ain't so... :-)
The issue is that some user interfaces (including, annoyingly, SQL*Plus and SQL Developer) don't support the DATE data type for bind variables.
This is a limitation of those user interfaces. Oracle SQL is perfectly capable of working with DATE bind variables, and if you are able to pass to it NULL of data type DATE, it will process that as expected. See an illustration below.
Some answers, posted already, deal with the secondary issue - if you pass in NULL of data type CHAR or VARCHAR2, what happens? I don't think understanding the precise reason is important; what is exceptionally important is to understand this limitation of front-end programs, and - if you are forced to use them to interact with the database - make sure your bind variables are string data type, and the query reflects that (by using TO_DATE with proper date format model).
If you are using PL/SQL, and/or perhaps other calling environments (not sure about ApEx for example, since I don't use it), you can pass in NULL of DATE data type directly, and get the expected behavior. Here is an illustration: I build your query as a dynamic query, with bind variables, and I invoke it and pass to it NULL of data type DATE. Look what happens:
declare
l_sql clob;
l_n number;
begin
l_sql := 'select 1
from dual
where sysdate between nvl(:start_date, sysdate)
and nvl(:end_date , sysdate)';
execute immediate l_sql into l_n using cast(null as date), cast(null as date);
dbms_output.put_line(l_n);
end;
/
1
PL/SQL procedure successfully completed.
You have to use like the below to match null with null to get a row
select 1 from dual
where nvl('',SYSDATE) BETWEEN NVL('', SYSDATE) AND NVL('', SYSDATE)

Trying to use a where statement in cursor creation (PL/SQL)

I'm trying to create a block that accepts input from a prompt and uses that input to filter the result set for the cursor. Keep in mind I'm a novice here so I maybe making a very routine mistake, and thank you for your help. My current code is below.
Set serveroutput on
DECLARE
ACCEPT a PROMPT “Please Enter a Date, eg. Format - 01 or 30"
datev char
datev := &a;
CURSOR cur_day_cursor IS
SELECT Arrival_Date Adate
FROM FLIGHT
WHERE TO_CHAR(Arrival_Date, ‘DD’) = datev;
cur_day_cursor_var cur_day_cursor%ROWTYPE;
BEGIN
OPEN Cur_day_cursor;
LOOP
Fetch Cur_day_cursor
INTO cur_day_cursor_var;
EXIT WHEN cur_day_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (cur_day_cursor_var.Adate);
END LOOP;
IF cur_day_cursor%ISOPEN THEN
CLOSE cur_day_cursor;
END IF;
END;
The where statement is causing my errors, so I was thinking that I may have to let the cursor collect all the data and then filter it when displaying, but I'm not sure if I can even do that.
The error I keep receiving ERROR at line 9:-
ORA-06550: line 9, column 1:
PLS-00103: Encountered the symbol "WHERE" when expecting one of the following:
begin function pragma procedure subtype type
current cursor delete
exists prior
I don't know exactly why Oracle is reporting the error at the WHERE. Sometimes the parser gets pretty confused by bad syntax and doesn't point to the real problem. You have several syntax errors before the cursor definition.
ACCEPT is a SQLPlus command, not a PL/SQL statement. Move your ACCEPT line above the DECLARE.
Also, your variable declaration and initialization are incorrect. The assignment should be part of the declaration line; you need to provide a length for the CHAR datatype; and the substitution value should be in quotes to be treated as a string. A valid version of your lines would be:
datev char(2) := '&a';
I ran the same query as above, and got the results perfectly fine.
You have few syntax as well as logical error which I corrected in your query. The syntax error(s) are -
datev char
datev := &a;
You can't do such an initialization in PL/SQL. You probably have to complete it in a single line like below -
datev char := &a;
The logical mistake(s) are -
Why use a CHAR variable to store data when you know that the value being returned is NUMBER.
You expect numbers from 1-31; then why do you choose the default size of char which as 1. It will fail if you provide a 2-digit number
Even if you increase the size of CHAR to CHAR(2), you will not get results when the users enters a number like 1 or 01, because for character wise comparison, '1' != '1 '(Mark the extra space at the end, because of char(2)); and also '1' != '01'.
The only solution for above is to use a NUMBER datatype.
Now here I am posting my query which is similar to your query, with a change of column name and table name. Please replace with your required names and try -
(Take care not to execute the ACCEPT....) with the PL/SQL block. It should be done in the SQL prompt first and then the other DECLARE section should be run.
--ACCEPT a NUMBER PROMPT 'Please Enter a Date, eg. Format - 01 or 30 :'
--Run the above line first in SQL Prompt and then execute the rest as whole
DECLARE
datev NUMBER(2) := &a;
CURSOR cur_day_cursor IS
SELECT Ename, HireDate Adate
FROM Emp
WHERE TO_CHAR(HireDate, 'D') = datev;
cur_day_cursor_var cur_day_cursor%ROWTYPE;
BEGIN
OPEN Cur_day_cursor;
LOOP
Fetch Cur_day_cursor
INTO cur_day_cursor_var;
EXIT WHEN cur_day_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (cur_day_cursor_var.Adate);
END LOOP;
IF cur_day_cursor%ISOPEN THEN
CLOSE cur_day_cursor;
END IF;
END;
/
It appears that the problem is that the single-quotes around ‘DD’ aren't single-quotes. It looks like the code was created in an editor which changes apostrophes into those special "look kind of like single quotes but aren't really" characters. Replace the original version of the WHERE clause with the following:
WHERE TO_CHAR(Arrival_Date, 'DD') = datev;
and I suspect you'll be fine.
And get yourself a good code editor. :-)

TO_CHAR(number) Function returns ORA-01722: invalid number

Query:
Select To_Number(qty) From my_table Where Id=12345;
Output:
ORA-01722: invalid number
01722. 00000 - "invalid number"
Query: Select qty From my_table Where Id=12345;
Output: 0.00080
Query:
Select To_Number(0.00080) From Dual;
Output:
0.00080 (no error)
This is a odd situation I am facing in Oracle. Can anybody suggest why it happens? The column qty is NUMBER type. Hence it is very hard to imagine that it contains invalid number, but it happened.
I want to clarify that it happened for the specific value in the column although we have thousands of records in the same column.
Added more: The same error appears if I use TO_CHAR(qty) function. The qty column is NUMBER type not VARCHAR2. In fact we are using SUM(qty) function which showed error. Hence I went for a dissection and found this row being the culprit.
I'm assuming that qty is defined as a varchar2 in my_table-- otherwise, there would be no purpose served by calling to_number. If that assumption is correct, I'll wager that there is some other row in the table where qty has non-numeric data in it.
SQL is a set-based language so Oracle (or any other database) is perfectly free to evaluate things in whatever order it sees fit. That means that Oracle is perfectly free to evaluate the to_number(qty) expression before applying the id=12345 predicate. If Oracle happens to encounter a row where the qty value cannot be converted to a number, it will throw an error.
It is also possible that there is some non-numeric data in the particular row where id = 12345 that happens not to be displaying (control characters for example). You can check that by running the query
SELECT dump(qty, 1016)
FROM my_table
WHERE id = 12345
(if you want decimal rather than hexadecimal, use 1010 as the second parameter to dump) and checking to see whether there is anything unexpected in the data.
The only way I can see you could get the results you've shown, given that qty really is a number field, is if it holds corrupt data (which is why there has been scepticism about that assumption). I'm also assuming your client is formatting the value with a leading zero, but is not forcing the trailing zero, which wouldn't normally appear; you can of course force it with to_char(.0008, '0.00000'), but you don't appear to be doing that; still, the leading zero makes me wonder.
Anyway, to demonstrate corruption you can force an invalid value into the field via PL/SQL - don't try this with real data or a table you care about:
create table t42(qty number);
table T42 created.
declare
n number;
begin
dbms_stats.convert_raw_value('bf0901', n);
insert into t42 (qty) values (n);
end;
/
anonymous block completed
select qty from t42;
QTY
----------
.00080
select to_number(qty) from t42;
Error starting at line : 12 in command -
select to_number(qty) from t42
Error report -
SQL Error: ORA-01722: invalid number
01722. 00000 - "invalid number"
Note the plain query shows the number as expected - though with a trailing zero, and no leading zero - and running it through to_number() throws ORA-01722. Apart from the leading zero, that is what you've shown.
It also fails with to_char(), as in your question title:
select to_char(qty) from t42;
Error starting at line : 13 in command -
select to_char(qty) from t42
Error report -
SQL Error: ORA-01722: invalid number
... which makes sense; your to_number() is doing an implicit conversion, so it's really to_number(to_char(qty)), and it's the implicit to_char() that actually generates the error, I think.
Your comments suggest you have a process that is loading and removing data. It would be interesting to see exactly what that is doing, and if it could be introducing corruption. This sort of effect can be achieved through OCI as the database will trust that the data it's passed is valid, as it does in the PL/SQL example above. There are bug reports suggesting imp can also cause corruption. So the details of your load process might be important, as might the exact database version and platform.
I encountered the nearly same problem. And I found the mysterious number behaved differently from the normal number after dump(). For example, assuming my qty=500 (datatype: number(30,2)) , then:
select dump(qty) from my_table where Id=12345;
Typ=2 Len=3: 194,6,1
select dump(500.00) from dual;
Typ=2 Len=2: 194,6
If we know how number datatype be stored (if not, plz visit http://translate.google.com/translate?langpair=zh-CN%7Cen&hl=zh-CN&ie=UTF8&u=http%3A//www.eygle.com/archives/2005/12/how_oracle_stor.html ) , we can find that there is a tailing zero (the last extra "1" in Typ=2 Len=3: 194,6,1) in the mysterious number.
So I made a trick to eliminate the tailing zero, and it works for the problem.
select dump(trunc(qty+0.001,2)) from my_table where Id=12345;
Typ=2 Len=2: 194,6
Hope someone to explain the deep mechanism.
try this:
Select To_Number(trim(qty)) From my_table Where Id=12345;

oracle stored procedure - local variable behaving mysteriously

This is how I am delcaring the local variables:
team_counter number (38) := 0;
username varchar2(50) := '';
This is how I am trying to use/see their value after using some select into statement:
dbms_output.put_line(team_counter||'.'||username);
if team_counter< 30 AND username <>'' then
begin
dbms_output.put_line('yuhj');
end;
end if;
The second output is not being printed!
The first output is being printed as '1.tuser' which I was expecting.
This is because you're trying to compare a string with a 0 length string using an inequality operator.
Oracle assumes that 0 length strings are equivalent to NULL and will not evaluate comparisons that don't use the NULL specific conditional. To quote:
Oracle Database currently treats a character value with a length of
zero as null. However, this may not continue to be true in future
releases, and Oracle recommends that you do not treat empty strings
the same as nulls.
Simply put this means that your IF statement should be:
if team_counter < 30 and username is not null then
...
As an additional note there's no need for the begin ... end around the dbms_output.put_line. As you're not catching any exceptions explicitly related to this call or declaring additional variables etc there's no real need.