Oracle - How to tell between null and not null string? - oracle

In an anonymous block I have an input string that is empty/null and want to check that against a non-null string. Example:
DECLARE
v_notnull varchar2(50):='this string is never null';
v_input varchar2(50):='';
BEGIN
IF trim(v_input) != trim(v_notnull) THEN
dbms_output.put_line('the strings do NOT match');
ELSE
dbms_output.put_line('the strings DO match');
END IF;
END;
The issue here is that when I run this block, the output is always 'the strings DO match' even though I am inputting the empty string '' (aka null) into v_input which is not the same as the string 'this string is never null'. How can I make sure oracle covers the empty string case? When v_input is empty I want the output to be 'the strings do NOT match'.

The documentation has a section on null handling. An empty string is treated the same as null, and you cannot compare nulls (of any type) with equality - as the table in the documentation shows, the result of comparing anything with null is unknown - neither true nor false. You have to use is [not] null to compare anything with null.
In this case you could spell it out explicitly, by seeing is one variable is null and the other isn't, and vice versa, and only compare the actual values if that tells you neither are null:
DECLARE
v_notnull varchar2(30):='this string is never null';
v_input varchar2(30):='';
BEGIN
IF (trim(v_input) is null and trim(v_notnull) is not null)
or (trim(v_input) is not null and trim(v_notnull) is null)
or trim(v_input) != trim(v_notnull) THEN
dbms_output.put_line('the strings do NOT match');
ELSE
dbms_output.put_line('the strings DO match');
END IF;
END;
/
the strings do NOT match
PL/SQL procedure successfully completed.
I've added the missing varchar2 sizes; presumably you based this on a procedure that took arguments without running what you were posting stand-alone...

'' is NULL in oracle. So, any comparison with null will always result in false.
Demo:
SQL> DECLARE
2 v_notnull varchar2(1000):='this string is never null';
3 v_input varchar2(1000):='';
4 BEGIN
5 IF v_input is null THEN
6 dbms_output.put_line('v_input is null'); -- should print because v_input is actually null
7 END IF;
8
9 IF trim(v_input) = trim(v_notnull) THEN -- always false because of null
10 dbms_output.put_line('the strings do NOT match');
11 ELSE
12 dbms_output.put_line('the strings DO match'); -- so should print this
13 END IF;
14 END;
15 /
v_input is null -- verified
the strings DO match -- verified
PL/SQL procedure successfully completed.
SQL>

Often you only care whether the values match, in which case null values make no difference:
declare
v_notnull varchar2(50) := 'this string is never null';
v_input varchar2(50) := '';
begin
if trim(v_input) = trim(v_notnull) then
dbms_output.put_line('the strings DO match');
else
dbms_output.put_line('the strings do NOT match');
end if;
end;
In some cases you can simplify a comparison of nullable values using a coalesce() or nvl() expression:
if nvl(v_yesno,'N') <> 'Y' then ...
You might be able to use a dummy comparison value (although of course there is a risk that it will match an actual value, depending on the situation):
if nvl(somevalue, chr(0)) <> nvl(othervalue, chr(0)) then ...
By the way, you can distinguish between null and '' by copying the value to a char variable, as a '' will trigger its normally unhelpful blank-padding behaviour. I can't really think of a situation where this would be useful, but just for fun:
declare
v1 varchar2(1) := null;
v2 varchar2(1) := '';
c char(1);
begin
c := v1;
if v1 is null and c is not null then
dbms_output.put_line('v1 = ''''');
elsif c is null then
dbms_output.put_line('v1 is null');
end if;
c := v2;
if v2 is null and c is not null then
dbms_output.put_line('v2 = ''''');
elsif c is null then
dbms_output.put_line('v2 is null');
end if;
end;
Output:
v1 is null
v2 = ''

Related

Oracle initialization of collection elements with not null constraint

What is the initialization value for NOT NULL element in the collection (Table/Varray)? It seems like the NULL but it is NOT NULL. Tested in Oracle LIVE SQL (Oracle Database 19c Enterprise Edition - 19.2.0.0.0)
declare
type TArrNotNull IS table of number NOT NULL;
type TArrAllowNull IS table of number;
arrNotNull TArrNotNull := TArrNotNull();
arrAllowNull TArrAllowNull := TArrAllowNull();
begin
-- NOT NULL ARRAY ELEMENTS
DBMS_OUTPUT.PUT_LINE('======== table/Array of number NOT NULL example ==========');
arrNotNull.Extend;
IF arrNotNull(1) is null then
DBMS_OUTPUT.PUT_LINE('NULL !!!');
else
DBMS_OUTPUT.PUT_LINE('NOT NULL BUT WHAT ???->['||COALESCE(arrNotNull(1),100)||']');
DBMS_OUTPUT.PUT_LINE('NOT NULL BUT WHAT + 1 BECOMES NULL (LIKE REAL NULL)???->['||COALESCE(arrNotNull(1)+1,100)||']');
end if ;
DBMS_OUTPUT.PUT_LINE('======== table/Array of number example ==========');
-- NOT NULL ARRAY ELEMENTS
arrAllowNull.Extend;
IF arrAllowNull(1) is null then
DBMS_OUTPUT.PUT_LINE('OK IS NULL !!!');
else
DBMS_OUTPUT.PUT_LINE('NOT NULL !!!');
end if ;
end;
RESULTS:
Statement processed.
======== table/Array of number NOT NUMBER example ==========
NOT NULL BUT WHAT ???->[]
NOT NULL BUT WHAT + 1 BECOMES NULL (LIKE REAL NULL)???->[100]
======== table/Array of number example ==========
OK IS NULL !!!
UPD: Also the same if you assign the value to the NUMBER variable.
tst:=arrNotNull(1);
if tst is null then
DBMS_OUTPUT.PUT_LINE('N NULL !!!');
else
DBMS_OUTPUT.PUT_LINE('N NOT NULL !!!+++');
end if;
if (tst+1) is null then
DBMS_OUTPUT.PUT_LINE('N+1 NULL !!!+++');
else
DBMS_OUTPUT.PUT_LINE('N+1 NOT NULL !!!+++');
end if;
RESULT:
N NOT NULL !!!+++
N+1 NULL !!!+++
Very interesting. Didn't find a "value" for 18+, but in 12c you get a NULL.
I did shrink your code a little:
declare
type TArrNotNull IS table of varchar2(100) NOT NULL;
arrNotNull TArrNotNull := TArrNotNull(1, 2, 3);
begin
begin
arrNotNull(2) := to_number(NULL); -- will throw, because null is not allowed
dbms_output.put_line('arrNotNull(2) is null now');
exception
WHEN others THEN
dbms_output.put_line('arrNotNull(2) couldn''t be set to null');
end;
arrNotNull.Extend;
dbms_output.put_line('arrNotNull(4): >>>' || nvl(arrNotNull(4), 'NULL') || '<<<');
end;
Result in 12c:
arrNotNull(2) couldn't be set to null
arrNotNull(4): >>>NULL<<<
Result in 18c (same like yours):
arrNotNull(2) couldn't be set to null
arrNotNull(4): >>><<<
Also interesting is, that an Extend on a nullable Table of has NULL as default value:
declare
type TArrNotNull IS table of varchar2(100);
arrNotNull TArrNotNull := TArrNotNull(1, 2, 3);
begin
arrNotNull.Extend;
dbms_output.put_line('arrNotNull(4): >>>' || nvl(arrNotNull(4), 'NULL') || '<<<');
end;
Result on all versions:
arrNotNull(4): >>>NULL<<<

Need to know if I am using IF statements in stored procedures correctly in PL/SQL

I am pretty amateur to PL/SQL, and I do not know if I am using the IF statements correctly. I am using Oracle Live SQL. This is all trying to insert a new row into a table called 'employees'. And the only NOT NULL values are employeeid, employeename, and jobid
CREATE OR REPLACE PROCEDURE employees.insert_employee
(
p_employeeid employees.employeeid%TYPE,
p_employeename employees.employeename%TYPE,
p_phone employees.phone%TYPE,
p_jobid employees.jobid%TYPE,
p_salary employees.salary%TYPE,
p_managerid employees.managerid%TYPE,
p_departmentid employees.departmentid%TYPE
)
AS
BEGIN
IF p_employeeid IS NULL THEN /* If one of the mandatory values are null */
RAISE VALUE_ERROR;
END IF;
IF p_employeename IS NULL THEN
RAISE VALUE_ERROR;
END IF;
IF p_jobid IS NULL THEN
RAISE VALUE_ERROR;
END IF;
IF p_jobid != employees.jobid THEN /* if jobid entered is not in the table */
RAISE VALUE_ERROR;
END IF;
IF p_salary < 0 THEN /* if the entered salary is negative */
RAISE VALUE_ERROR;
END IF;
IF p_departmentid != employees.departmentid THEN /* if the departmentid entered is not in the table */
RAISE VALUE_ERROR;
END IF;
IF p.employeeid = employees.employeeid THEN /* if the employeeid already exists */
RAISE RAISE_APPLICATION_ERROR(-2000);
END IF;
INSERT INTO employees (employeeid, employeename, phone, jobid, salary, managerid, departmentid)
VALUES(p_employeeid, p_employeename, p_phone, p_jobid, p_salary, p_managerid, p_departmentid);
END;
I don't think that it would even compile. Besides, you shouldn't do it that way (as you were already told). A few more objections, if I may (regarding the original question: whether you use IF correctly).
Bunch of first IFs can be shortened with OR:
IF p_employeeid IS NULL OR
p_employeename IS NULL OR
p_jobid IS NULL OR
p_salary < 0
THEN
RAISE VALUE_ERROR;
END IF;
You can't reference table values that way, e.g.
IF p_jobid != employees.jobid THEN /* if jobid entered is not in the table */
RAISE VALUE_ERROR;
END IF;
There's no employees.jobid - you have to select it first. For example:
declare
l_cnt;
begin
select count(*)
into l_cnt
from employees e
where e.jobid = p_jobid;
if l_cnt = 0 then -- there's no such job in the table
raise value_error;
end if;
end;
Finally, the final condition you checked and tried to raise something
RAISE RAISE_APPLICATION_ERROR(-2000);
is wrong for 3 reasons:
you don't RAISE RAISE_...
User defined exception's range is from -20001 to -20999 (five digits, not 4)
RAISE_APPLICATION_ERROR requires yet another argument
so - correctly - you'd
raise_application_error(-20001, 'That does not exist');
Even if the syntax is right, I don't think you are using them correctly.
1) If things should not be allowed to be null then mark them as NOT NULL on the tables.
2) If the departmentID must exist then that's a foreign key constraint.
3) If the employeeID exists that should be a unique constraint (even if your syntax works, which it does not)
Properly declared, the DB engine will ensure all this for you.

PL/SQL Why my function return NULL value instead of a value in 'HH24:MI:SS' format?

I'm trying to code a PL/SQL function to receive by parameter a number in VARCHAR2 type and return a hour in format 'HH24:MI:SS'. eg: If I send by parameter '6', the function will return 06:00:00. The problem is the function returned NULL value but If I write the same process in a simple PL/SQL block it works fine. Do you have any idea?. Thanks :)
CREATE OR REPLACE FUNCTION format_hour_test (
p_hour VARCHAR2)
RETURN VARCHAR2
IS
v_hour_time TIMESTAMP;
v_hour_char VARCHAR2(50);
BEGIN
IF
REGEXP_LIKE(p_hour, '(0-9)') THEN
v_hour_time := TO_TIMESTAMP(p_hour, 'HH24');
END IF;
v_hour_char := TO_CHAR(v_hour_time, 'HH24:MI:SS');
RETURN v_hour_char;
END format_hour_test;
/
SHOW ERRORS;
When I write a anonymus block with the same process this works fine.
SET SERVEROUTPUT ON
DECLARE
v_hour VARCHAR2(20) := '6';
v_hour_time TIMESTAMP := TO_TIMESTAMP(v_hour, 'HH24');
v_result VARCHAR2(20) := TO_CHAR(v_hour_time, 'HH24:MI:SS');
BEGIN
DBMS_OUTPUT.PUT_LINE(v_result);
END;
You have the wrong type of brackets in your regular expression; it should be:
IF REGEXP_LIKE(p_hour, '[0-9]') THEN
You used grouping parentheses, insteaf of matching/class brackets. You would get a match if you passed in the string '0-9', which wouldn't then be converted to a date/time.
Read more.
Your anonymous block is not the same process. In the anonymous block you do not have if regexp_like... In regular expressions, parens group, so you are looking for the string 0-9. So v_hour_time is not being set in the procedure.
I think you are looking for [0-9], which is match one of the characters in the range. Alternatively, [[:digit:]] https://docs.oracle.com/cd/B12037_01/server.101/b10759/ap_posix001.htm#i690819
Your v_hour_time is never initialized, because of your REGEXP_LIKE conditional:
select (case when REGEXP_LIKE('6', '(0-9)') then 'true' else 'false' end) as res from dual;
returns:
RES
-----
false

Why this code is not going into IF block in PL/SQL?

I have below PL/SQL code and this is not going into IF block. Why is this not going into the IF Block?
Appreciate your responses.
DECLARE
p_datacenterid VARCHAR2(50) := '';
p_dcid VARCHAR2(50);
BEGIN
dbms_output.put_line('test');
-- Check if DataCenterId is null
IF nvl(p_datacenterid, '') = '' THEN
dbms_output.put_line('DataCenterID is empty');
SELECT datacenterid
INTO p_dcid
FROM pod_tab
WHERE url = 'dit3.ezlm.adp.com'
AND rownum = 1;
END IF;
END;
/
It looks like you are getting tripped up on comparing null values. An empty string is converted by Oracle into Null in the background. See here for details.
The IF statement ends up resolving to IF null = null which is never true.
Something like this would work
IF p_DataCenterID IS NOT NULL THEN
--assert the values are within a range
ELSE
--look up the value
END IF;
In PL/SQL and the Oracle database the empty string ('') is the same as NULL, and thus you must test for it as you would for NULL by using the IS NULL test instead of = NULL or = ''. Try running the following:
declare
p_DataCenterID varchar2(50) := ''; -- NOTE: '' is the same as NULL
p_dcID varchar2(50);
BEGIN
dbms_output.put_line( 'test');
-- Check if DataCenterId is null
IF p_DataCenterID IS NULL THEN
dbms_output.put_line('DataCenterID is empty');
SELECT DataCenterId
INTO p_dcID
FROM Pod_Tab
WHERE URL = 'dit3.ezlm.adp.com'
AND ROWNUM = 1;
END IF;
END;
/
This will print
test
DataCenterID is empty
Best of luck.

oracle sql dynamic query to select column in cursor

I've declared the below cursor and variables:
l_level VARCHAR2(100);
l_level_value VARCHAR2(100);
l_select_clause CLOB;
CURSOR l_data IS
SELECT LEVEL1, LEVEL2, LEVEL3
FROM LEVELS;
Then I loop through the cursor:
FOR c1line IN l_data
LOOP
CASE WHEN c1line.LEVEL1 IS NULL THEN l_level := 'c1line.LEVEL2'
WHEN c1line.LEVEL2 IS NULL THEN l_level := 'c1line.LEVEL3'
WHEN c1line.LEVEL3 IS NULL THEN l_level := 'c1line.LEVEL4'
ELSE l_level := NULL
END CASE;
END LOOP;
l_select_clause := 'SELECT ' || l_level || ' INTO l_level_value FROM dual;';
EXECUTE IMMEDIATE l_select_clause;
And then I have some other statements that execute depending on what is selected into the variable l_level_value
My problem is that when execute my procedure I get the following error:
ORA-00904: "C1LINE"."LEVEL2": invalid identifier
ORA-06512: at "MY_PROCEDURE", line 110
ORA-06512: at line 2
Does anyone know what I have done wrong?
Thanks
About the Actual Error c1line.LEVEL1 , you open the cursor dynamically? it seems valid in thee code you have shown
Then..
EXECUTE IMMEDIATE Accepts bind variable, whereas INTO should be after Execution only. To mention, Semicolon (;) is not needed in the Query String
l_select_clause := 'SELECT ' || l_level || ' FROM dual';
EXECUTE IMMEDIATE l_select_clause INTO l_level_value;
My Question is, do you really need dynamic script? I mean same operation can be performed as
FOR c1line IN l_data
LOOP
CASE WHEN c1line.LEVEL1 IS NULL THEN l_level_value := c1line.LEVEL2
WHEN c1line.LEVEL2 IS NULL THEN l_level_value := c1line.LEVEL3
WHEN c1line.LEVEL3 IS NULL THEN l_level_value := c1line.LEVEL4
ELSE l_level_value := NULL
END CASE;
END LOOP;
Why taking two steps? First assign the value to l_level and then assign same value to l_level_value? That too using dynamic scripts?
Always remember, dynamic script should be the last and last options. You should avoid it.

Resources