SQL Update with CASE - sql

could you please help me with this SQL?
UPDATE VERSION
SET VERSION_STATUS_ID = IF((SELECT COUNT(*) from VERSION where KEY = 'ABC') > 1, 5, 6)
WHERE VERSION_ID = 1;
Error report -
SQL Error: ORA-00907: missing right parenthesis
00907. 00000 - "missing right parenthesis"
Thanks

Use a CASE expression:
UPDATE VERSION
SET VERSION_STATUS_ID = CASE WHEN (SELECT COUNT(*) from VERSION where KEY = 'ABC') > 1
THEN 5 ELSE 6 END
WHERE VERSION_ID = 1;

EXISTS is usually more efficient than COUNT(*) in a subquery. If you have a unique id column in the table, you can use:
UPDATE VERSION v
SET VERSION_STATUS_ID = (CASE WHEN EXISTS (SELECT 1
FROM VERSION v2
WHERE v2.KEY = 'ABC' AND
v2.VERSION_ID <> v.VERSION_ID
) > 1
THEN 5 ELSE 6
END)
WHERE VERSION_ID = 1;
This assumes that VERSION_ID is unique. If you don't have such an explicitly declared column, you can use ROWID instead.

try this is working very good
SET SERVEROUTPUT ON;
DECLARE
EXPE NUMBER;
SAL NUMBER;
BEGIN
SELECT TRUNC((SYSDATE - hire_date) / 365) INTO EXPE
FROM EMPLOYEES
WHERE EMPLOYEE_ID = 155;
UPDATE EMPLOYEES
SET SALARY = CASE
WHEN EXPE > 15 THEN SALARY+(SALARY*20/100)
WHEN EXPE < 15 THEN SALARY+(SALARY*30/100)
ELSE 0
END;
SELECT SALARY INTO SAL
FROM EMPLOYEES
WHERE EMPLOYEE_ID =155;
DBMS_OUTPUT.PUT_LINE('THIS IS THE SALARY ' || ' ' ||SAL || ' ' || ' AND THIS IS THE EXPERINCE '||EXPE);
END;

Related

trying to check record exist in table -Oracle sql [duplicate]

I need to check a condition. i.e:
if (condition)> 0 then
update table
else do not update
end if
Do I need to store the result into a variable using select into?
e.g:
declare valucount integer
begin
select count(column) into valuecount from table
end
if valuecount > o then
update table
else do
not update
You cannot directly use a SQL statement in a PL/SQL expression:
SQL> begin
2 if (select count(*) from dual) >= 1 then
3 null;
4 end if;
5 end;
6 /
if (select count(*) from dual) >= 1 then
*
ERROR at line 2:
ORA-06550: line 2, column 6:
PLS-00103: Encountered the symbol "SELECT" when expecting one of the following:
...
...
You must use a variable instead:
SQL> set serveroutput on
SQL>
SQL> declare
2 v_count number;
3 begin
4 select count(*) into v_count from dual;
5
6 if v_count >= 1 then
7 dbms_output.put_line('Pass');
8 end if;
9 end;
10 /
Pass
PL/SQL procedure successfully completed.
Of course, you may be able to do the whole thing in SQL:
update my_table
set x = y
where (select count(*) from other_table) >= 1;
It's difficult to prove that something is not possible. Other than the simple test case above, you can look at the syntax diagram for the IF statement; you won't see a SELECT statement in any of the branches.
Edit:
The oracle tag was not on the question when this answer was offered, and apparently it doesn't work with oracle, but it does work with at least postgres and mysql
No, just use the value directly:
begin
if (select count(*) from table) > 0 then
update table
end if;
end;
Note there is no need for an "else".
Edited
You can simply do it all within the update statement (ie no if construct):
update table
set ...
where ...
and exists (select 'x' from table where ...)
not so elegant but you dont need to declare any variable:
for k in (select max(1) from table where 1 = 1) loop
update x where column = value;
end loop;

Oracle optimize select after update status performance

I have a store procedure that will
Update maximum 500 rows status from 0 to 1
Return those rows to program via cursor
Here is my store procedure code
PROCEDURE process_data_out (
o_rt_cursor OUT SYS_REFCURSOR
) IS
v_limit NUMBER;
l_data_ids VARCHAR2(32000);
BEGIN
v_limit := 500; -- limit 500
l_data_ids := '';
-- Create loop to get data
FOR i IN (
SELECT *
FROM
(
SELECT id FROM
TBL_DATA a
WHERE
a.created_at BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND a.status = 0
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
ORDER BY
priority
)
WHERE
ROWNUM <= v_limit
) LOOP
BEGIN
-- Build string of ids like id1,id2,id3,
l_data_ids := l_data_ids
|| i.id
|| ',';
-- update row status to prevent future repeat
UPDATE TBL_DATA
SET
status = 1
WHERE
id = i.id;
END;
END LOOP;
COMMIT;
-- If string of ids length >0 open cursor to take data
IF ( length(l_data_ids) > 0 )
THEN
-- Cut last comma id1,id2,id3, --> id1,id2,id3
l_data_ids := substr(l_data_ids,1,length(l_data_ids) - 1);
-- open cursor
OPEN o_rt_cursor FOR
SELECT
id,
phone
FROM
TBL_DATA a
WHERE
a.id IN (
SELECT
to_number(column_value)
FROM
XMLTABLE ( l_data_ids )
);
END IF;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END process_data_out;
I want to optimize this performance and here is my question
Should I replace in by exists
Replace
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_1)
AND a.phone NOT IN (SELECT phone FROM TBL_BIG_TABLE_2 WHERE IS_DENY = 1)
by
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_1 where TBL_BIG_TABLE_1.phone = a.phone)
AND NOT Exists (SELECT phone FROM TBL_BIG_TABLE_2 WHERE TBL_BIG_TABLE_2.phone = a.phone and IS_DENY = 1)
Is there a better way than
Save a string of ids like id1,id2,id3 after update row status
Open cursor by select from string of ids
I appreciate for any suggestion.
Thank for your concern
Row-by-row processing is always slower and you are also creating the string of ids, which again takes time so overall performance is going down.
You can use the collection DBMS_SQL.NUMBER_TABLE to store the updated ids from the UPDATE statement using the RETURNING clause and use it in the cursor query.
Also, I have changed your update statement so that it does not use NOT IN and uses the LEFT JOINS and ROW_NUMBER analytical function for increasing the performance as follows:
CREATE OR REPLACE PROCEDURE PROCESS_DATA_OUT (
O_RT_CURSOR OUT SYS_REFCURSOR
) IS
V_LIMIT NUMBER;
L_DATA_IDS DBMS_SQL.NUMBER_TABLE;
BEGIN
V_LIMIT := 500; -- limit 500
UPDATE TBL_DATA A
SET A.STATUS = 1
WHERE A.ID IN (
SELECT ID
FROM ( SELECT ID,
ROW_NUMBER() OVER(ORDER BY PRIORITY) AS RN
FROM TBL_DATA B
LEFT JOIN TBL_BIG_TABLE_1 T1 ON T1.PHONE = B.PHONE
LEFT JOIN TBL_BIG_TABLE_2 T2 ON T2.IS_DENY = 1 AND T2.PHONE = B.PHONE
WHERE B.CREATED_AT BETWEEN SYSDATE - 0.5 AND SYSDATE + 0.1
AND B.STATUS = 0
AND T1.PHONE IS NULL
AND T2.PHONE IS NULL)
WHERE RN <= V_LIMIT ) RETURNING ID BULK COLLECT INTO L_DATA_IDS;
OPEN O_RT_CURSOR FOR SELECT ID, PHONE
FROM TBL_DATA A
WHERE A.ID IN (SELECT COLUMN_VALUE FROM TABLE ( L_DATA_IDS ));
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END PROCESS_DATA_OUT;
/

How to properly use an oracle in the request, the "If" and "LIKE" condition

i need help with the code, i have two pieces of code that write two variables: v_responsible_job and v_responsible.I would like to combine these queries into one and add a condition if.
If the answer to this query is "ABC", then the first query must be completed, and if not,the second query:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=4ce28fad7e33f00c5538e49943ae7dechere is the demo data
SELECT NAME FROM data_separators WHERE id = way.DS_ID
example answer:
NAME
ABC Info
Scool
offise
ABC SHOP
first call :
BEGIN
SELECT full_name, job INTO v_responsible, job_id
FROM physical_persons
WHERE id IN (SELECT physical_person
FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name
INTO v_responsible_job
FROM jobs
WHERE id = job_id AND
rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '';
END;
second call :
BEGIN
SELECT full_name, job
INTO v_responsible, job_id
FROM physical_persons
WHERE id IN (SELECT RESPONSIBLE
FROM ADRESSES
WHERE name = p1.name) AND
rownum = 1;
SELECT name
INTO v_responsible_job
FROM jobs
WHERE id = job_id AND
rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '';
END;
my varianr answer , but his don't work(
BEGIN
select count(*) into v_count_word FROM data_separators WHERE id = way.DS_ID and NAME LIKE '%ABC%';
select count(RESPONSIBLE) into v_count_respon FROM ADRESSES WHERE name = p1.name;
if v_count_word > 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT physical_person FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
if v_count_respon > 0 and v_count_word = 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT RESPONSIBLE
FROM ADRESSES
WHERE name = p1.name) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
if v_count_respon = 0 and v_count_word = 0 then
SELECT full_name, job INTO v_responsible, job_id FROM physical_persons WHERE id IN (SELECT physical_person FROM data_separators WHERE id = way.DS_ID) AND rownum = 1;
SELECT name INTO v_responsible_job FROM jobs WHERE id = job_id AND rownum = 1;
if v_responsible_job is not null then
if length(v_responsible_job) > 0 then
v_responsible_job := ', '|| v_responsible_job;
end if;
end if;
end if;
EXCEPTION WHEN OTHERS THEN
v_responsible_job := '-';
END;
Combine the first two queries using EXISTS and then use LISTAGG to aggregate the values:
DECLARE
v_responsible_jobs VARCHAR2(4000);
BEGIN
SELECT LISTAGG( name, ',' ) WITHIN GROUP ( ORDER BY name )
INTO v_responsible_jobs
FROM jobs j
WHERE EXISTS(
SELECT 1
FROM physical_persons pp
WHERE pp.job_id = j.job_id
AND id IN (
SELECT physical_person
FROM data_separators
WHERE id = way.DS_ID
)
);
END;
/
An other way would be to use BULK COLLECT INTO a collection data type and then to iterate over the collection concatenating the string; but that is more complicated than using LISTAGG to do all the work for you.

PL/SQL procedure to output line the given date if not existing, latest date should be given

I have this table informationvalues with the contents:
Now I create a procedure where I need to input a date parameter which should output line the correct attr with given price. If the date doesn't exist the latest date should be selected.
The solution table for to_date('01-jan-19') would look like this:
This would be then output line in the procedure.
Should I select to correct tuple and output line it or would it be best to just bulk collect everything and then check in a for loop with an if statement what tuple I need to display.
What I have so far:
A select statement with the tuples I am looking for:
create or replace procedure print_inf_value(closingDate Date) is
cursor d1 (closingDate Date) is
select t.attr, t.dateOfValue, t.price
from (
select i.*,
row_number() over (
partition by attr
order by case when dateOfValue = closingdate then 1 else 2 end, dateOfValue desc
) rn
from InformationValues i
) t
where t.rn = 1;
BEGIN
dbms_output.put_line('Information Value ');
dbms_output.put_line('--------------------------------');
FOR d1_rec IN d1 LOOP
dbms_output.put_line(d1_rec.attr || ' ' || d1_rec.price );
END LOOP;
END;
Or a procedure where I bulk collect everything and then I need to sort out what tuple I need:
create or replace procedure print_inf_value(closingDate Date) is
TYPE d1 IS TABLE OF informationvalues%rowtype;
emps d1;
begin select * bulk collect into emps
from informationvalues;
FOR i IN 1 .. emps.COUNT LOOP
if emps(i).dateofvalue = closingDate then
dbms_output.put_line(emps(i).attr || ' ' || emps(i).price );
/*else*/
end if;
END LOOP;
END;
Both are not working right, so what am I missing to display tuple with the correct date.
Please try:
CREATE OR REPLACE PROCEDURE print_inf_value (closingDate DATE)
IS
BEGIN
DBMS_OUTPUT.put_line (RPAD ('ATTR', 20) || RPAD ('PRICE', 20));
FOR o
IN (select attr, trim(case when price < 1 then to_char(price,90.9) else to_char(price) end) price from (
select attr, price, dateofvalue,
row_number() over (partition by attr order by dateofvalue desc) rn from informationvalues
) i where dateofvalue = closingdate
or (rn = 1 and not exists (select 1 from informationvalues iv where iv.attr = i.attr and dateofvalue = closingdate) )
)
LOOP
DBMS_OUTPUT.put_line (RPAD (o.attr, 20) || RPAD ( o.price, 20));
END LOOP;
END;
Sample execution:
set serveroutput on;
begin
print_inf_value(date'2019-01-01');
end;
Output:
ATTR PRICE
age 2
electronics 0.5
gender 3
hobbies 0.5
homeAddress 7
maritalStatus 1
mobilePhone 5
musicTaste 0.1
socialContacts 1

IF EXISTS condition not working with PLSQL

I am trying to print the TEXT when condition is TRUE. The select code is perfectly working fine. It's showing 403 value when i only run select code. But I have to print some text when condition exists. What's the problem with following code.
BEGIN
IF EXISTS(
SELECT CE.S_REGNO FROM
COURSEOFFERING CO
JOIN CO_ENROLMENT CE
ON CE.CO_ID = CO.CO_ID
WHERE CE.S_REGNO=403 AND CE.COE_COMPLETIONSTATUS = 'C' AND CO.C_ID = 803
)
THEN
DBMS_OUTPUT.put_line('YES YOU CAN');
END;
Here is the error report:
Error report:
ORA-06550: line 5, column 1:
PLS-00103: Encountered the symbol "JOIN" when expecting one of the following:
) , with group having intersect minus start union where
connect
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
IF EXISTS() is semantically incorrect. EXISTS condition can be used only inside a SQL statement. So you might rewrite your pl/sql block as follows:
declare
l_exst number(1);
begin
select case
when exists(select ce.s_regno
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1
)
then 1
else 0
end into l_exst
from dual;
if l_exst = 1
then
DBMS_OUTPUT.put_line('YES YOU CAN');
else
DBMS_OUTPUT.put_line('YOU CANNOT');
end if;
end;
Or you can simply use count function do determine the number of rows returned by the query, and rownum=1 predicate - you only need to know if a record exists:
declare
l_exst number;
begin
select count(*)
into l_exst
from courseoffering co
join co_enrolment ce
on ce.co_id = co.co_id
where ce.s_regno=403
and ce.coe_completionstatus = 'C'
and ce.c_id = 803
and rownum = 1;
if l_exst = 0
then
DBMS_OUTPUT.put_line('YOU CANNOT');
else
DBMS_OUTPUT.put_line('YES YOU CAN');
end if;
end;
Unfortunately PL/SQL doesn't have IF EXISTS operator like SQL Server. But you can do something like this:
begin
for x in ( select count(*) cnt
from dual
where exists (
select 1 from courseoffering co
join co_enrolment ce on ce.co_id = co.co_id
where ce.s_regno = 403
and ce.coe_completionstatus = 'C'
and co.c_id = 803 ) )
loop
if ( x.cnt = 1 )
then
dbms_output.put_line('exists');
else
dbms_output.put_line('does not exist');
end if;
end loop;
end;
/