Oracle CASE expression documentation issue - sql

In the SQL Language Reference for Oracle 11g R2, the documentation for the simple CASE expressions says that:
You cannot specify the literal NULL for every return_expr and the else_expr.
However the following SQL executes without problem and returns null:
select case 'test'
when 'test' then null
else null
end "Null Test"
from dual;
Is this a problem with the documentation or am I missing something?

You have to have at least one not null return expression when your case expression(whether it's simple or search case expression) is used inside a PL/SQL block. In SQL this restriction is relaxed:
Is this a problem with the documentation or am I missing something
It seems like a minor documentation bug.
SQL:
SQL> select case 1
2 when 1 then null
3 else null
4 end as res
5 from dual
6 ;
Result:
RES
---
null
PL/SQL:
SQL> declare
2 l_res number;
3 begin
4 l_res := case 1
5 when 1 then null
6 else null
7 end;
8 end;
9 /
ORA-06550: line 4, column 11:
PLS-00617: at least one result in the CASE expression must not be NULL
ORA-06550: line 4, column 2:
PL/SQL: Statement ignored

Related

Binding data to a variable in a CASE WHEN statement

I am writing an oracle PL/SQL compound trigger. In the code I'm querying a single value and putting it into a variable.
From there I check whether the variable is null or not. If it is, I need to assign 0 to another variable, if it isn't I assign 1.
I think this is possible with a CASE WHEN statement.
An example is as follows
//line 23 ↓
SELECT contract_end INTO contractEnd FROM contract WHERE contract_id = :new.expense_job;
//line 25 ↓
SELECT CASE WHEN contractEnd IS NOT NULL THEN
contractIDCollection(iterator).ended := 1
ELSE
contractIDCollection(iterator).ended := 0
END
FROM dual;
However, when I do this the compiler throws an error and says that I have not finished the statement.
Is this the correct way to go about doing this?
contractIDCollection is a record with parameters, the definition code is working properly
LINE/COL ERROR
-------- -----------------------------------------------------------------
23/5 PL/SQL: SQL Statement ignored
24/44 PL/SQL: ORA-00905: missing keyword
Do not try to switch from the PL/SQL scope to the SQL scope (with a SELECT statement) to try to assign the variable; just do it all in the PL/SQL scope:
IF contractEnd IS NOT NULL THEN
contractIDCollection(iterator).ended := 1;
ELSE
contractIDCollection(iterator).ended := 0;
END IF;
If you did want to incur the overheads of context-switching (don't, as you do not need to and it is likely to be slower) then you can use:
SELECT CASE WHEN contractEnd IS NOT NULL THEN 1 ELSE 0 END
INTO contractIDCollection(iterator).ended
FROM dual;
or do it all on the line before:
SELECT contract_end,
CASE WHEN contract_end IS NOT NULL THEN 1 ELSE 0 END
INTO contractEnd,
contractIDCollection(iterator).ended
FROM contract
WHERE contract_id = :new.expense_job;

Is there boolean type for column in Oracle SQL?

I tried to:
select 1>2 from dual;
but got:
ORA-00923: FROM keyword not found where expected
Is there boolean type for column expression in Oracle SQL?
I able to do:
select case when 1>2 then 'T' else 'F' end from dual;
Originally I tried to compare date fields and the quickest way I found was getting difference and look to sign...
UPDATE I tried SIGN function, I don't know if it is vendor specific extension:
select SIGN(1-2) from dual;
select SIGN(DATE '2017-01-02' - DATE '2017-02-12') from dual;
but this trick doesn't work for strings...
No there is not, you can use 0 and 1 just as yes/no.
If you need to get the result 1 if something is true and 0 if it is false, you can use a case expression:
select case when (any_logical_condition_here) then 1 else 0 end as my_col
from ....
where ....
For example:
select case when 1 > 2 then 1 else 0 end as bool_result
from dual;
BOOL_RESULT
---------------------------------------
0
NOTE though - "Boolean" refers strictly to the TRUE/FALSE logic, it has no place for UNKNOWN. When you deal with null, as you must in SQL, you need three-valued logic. The case expression as written above returns 1 when the logical condition is true and 0 otherwise. Try it with 1 > null - the truth value is UNKNOWN, the case expression will return 0.

Pl/Sql Oracle Function in a procedure

I need to make a procedure that needs to update the situation of a student, but I need to use a function that returns 'A' for the approved students, and 'R' for the not approved. After this I need to update the field "Situation" with an 'A' or 'R' that is returned from the function. I already have the function, but I don't have any idea how to make the procedure. Here goes the code of the function:
CREATE OR REPLACE FUNCTION check_grade(grade IN NUMBER,
frequency IN NUMBER) RETURN VARCHAR2 AS
RESULT VARCHAR2(1) DEFAULT '';
BEGIN
IF (grade >= 6) AND (frequency >= 0.75) THEN
resultado := 'A';
ELSE
resultado := 'R';
END IF;
RETURN RESULT;
END;
I think you are over complicating the things. You could do it in pure SQL rather than using PL/SQL function and procedure.
You could use CASE expression, for example:
Test 1
SQL> SELECT
2 CASE
3 WHEN &grade >=6
4 AND &frequency>=0.75
5 THEN 'A'
6 ELSE 'R'
7 END
8 FROM DUAL;
Enter value for grade: 10
old 3: WHEN &grade >=6
new 3: WHEN 10 >=6
Enter value for frequency: 1
old 4: AND &frequency>=0.75
new 4: AND 1>=0.75
C
-
A
Test 2
SQL> SELECT
2 CASE
3 WHEN &grade >=6
4 AND &frequency>=0.75
5 THEN 'A'
6 ELSE 'R'
7 END
8 FROM DUAL;
Enter value for grade: 1
old 3: WHEN &grade >=6
new 3: WHEN 1 >=6
Enter value for frequency: 0.5
old 4: AND &frequency>=0.75
new 4: AND 0.5>=0.75
C
-
R
So, using the same logic in your UPDATE statement:
UPDATE table_name
SET situation = (
CASE
WHEN &grade >=6
AND &frequency>=0.75
THEN 'A'
ELSE 'R'
END)
WHERE situation IS NOT NULL;
Update OP only wants to do it in a procedure:
create or replace procedure pr_update
is
begin
update table_name
set Situation = check_grade(grade, frequency);
end;
/
In the above procedure, put actual table_name in the update statement. grade and frequency are considered as the column names of the table.
See if this help.
create or replace procedure pr_update(v_grade in number, v_frequency in number) is
update tab_name set Situation=check_grade(v_grade,v_frequency);
end;

Searched Case works / Simple Case doesn't in Oracle

I have the following Searched Case field selection in a Oracle 10g SELECT query
(case
when LOADER_CELLS.CELL_MODE='RW' then 1
when LOADER_CELLS.CELL_MODE='R' then 2
end) as CELL_EDIT_MODE_ID
but if I write it as a Simple Case expression, as follows:
(case LOADER_CELLS.CELL_MODE
when 'RW' then 1
when 'R' then 2
end) as CELL_EDIT_MODE_ID
I get a ORA-12704: character set mismatch error on the when 'RW' line.
I gave a look to the Oracle documentation, and it seems my syntax is correct. http://docs.oracle.com/cd/B19306_01/server.102/b14200/expressions004.htm
Can someone help me on this?
" I supposed that it could be a encoding problem but I don't know how to "cast" the constant strings to a NVARCHAR"
you do it with "N" syntax.
case LOADER_CELLS.CELL_MODE
when n'RW' then 1
when n'R' then 2
end
eg
SQL> select case a when 'a' then 1 end from foo;
select case a when 'a' then 1 end from foo
*
ERROR at line 1:
ORA-12704: character set mismatch
SQL> select case a when n'a' then 1 end from foo;
CASEAWHENN'A'THEN1END
---------------------

How to test value of Alien_Body_Part (NCLOB) in Oracle projection list?

I have a table with three NCLOB columns. For each NCLOB I want a count of how many are not 'TC' or 'NC'. The case when ... end approach works for NVARCHAR2 columns but not for NCLOB. How can I test the values of an NCLOB in the projection list?
Oracle Database 11g Release 11.1.0.6.0
This minimal example demonstrates the root issue.
create table t (
alien_body_part nclob
);
insert into t(alien_body_part) values(null);
insert into t(alien_body_part) values('TC');
insert into t(alien_body_part) values('NC');
insert into t(alien_body_part) values('Extended Mandible');
select case when alien_body_part in ('TC', 'NC') then 0 else 1 end from t
*
ERROR at line 1:
ORA-00932: inconsistent datatypes: expected - got NCLOB
Only compare the first characters:
SQL> SELECT dbms_lob.substr(alien_body_part, 4000, 1) body_part,
2 CASE
3 WHEN dbms_lob.substr(alien_body_part, 4000, 1)
4 IN ('TC', 'NC') THEN
5 0
6 ELSE
7 1
8 END is_nc_or_tc
9 FROM t;
BODY_PART IS_NC_OR_TC
---------------------- -----------
1
TC 0
NC 0
Extended Mandible 1
In this case since one side of the comparison is only 2 characters long, comparing the first 3 characters would be sufficient (as a NCLOB will be equal to 'TC' only if it is 2 characters long AND those characters equal 'TC' obviously).
Also neither the CASE nor the IN is the cause of the error here (you can't compare directly a CLOB/NCLOB in SQL), consider:
SQL> select * from t where alien_body_part = 'TC';
select * from t where alien_body_part = 'TC'
ORA-00932: inconsistent datatypes: expected - got NCLOB