Keep written " SQL Statement ignored" and "not enough values" - sql

This is my code:
declare
cursor c1 is
select * from PUTRAJAYA.STRATA_PJ_BORANG4
WHERE HM_DETAIL_NOHMDETAIL is NOT NULL;
err_num number;
err_msg varchar2(250);
begin
for c1rec in c1 loop
BEGIN
insert into ET_MAIN_WPPJ_JOZRIN.IND_HKMLK
(HKMLK_ID, CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, VERSION,
ID_HAKMILIK,NO_HAKMILIK, NO_BANGUNAN,NO_TINGKAT,NO_PETAK,UPI,FLAG_GANTUNG,
FLAG_TUKARGANTI,TARIKH_KUTIPAN_ETANAH
)
values (
SEQ_HKMLK.NEXTVAL,'DMS', SYSDATE, 'DMS', SYSDATE,'0',
(select substr(HM_DETAIL_NOHMDETAIL,1,17),
substr(HM_DETAIL_NOHMDETAIL,-8) from strata_PJ_BORANG4),
-- substr(STRATA_PJ_BORANG4.HM_DETAIL_NOHMDETAIL,1,17),
-- substr(STRATA_PJ_BORANG4.HM_DETAIL_NOHMDETAIL,-8),
(SELECT
HM_DETAIL_NOBGN,
HM_DETAIL_NOTKT,
HM_DETAIL_NOPETAK,
HM_DETAIL_NOHMDETAIL,
'N','N',
HM_DETAIL_PUNGUT_DATETIME
FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B
WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL));
END;
END LOOP;
END;
This the errors:
Error report - ORA-06550: line 18, column 5: PL/SQL: ORA-00947: not
enough values ORA-06550: line 13, column 5: PL/SQL: SQL Statement
ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.

First of all lets try to understand the meaning of the error "not enough values". It suggest that for some column in the insert into claues value has not been provided in values clause. In your case insert into has 15 columns but values clause have only 8 values.I have re written your subqueries in value clause. Please try to execute this and see if this works fine for you.
declare
cursor c1 is
select * from PUTRAJAYA.STRATA_PJ_BORANG4
WHERE HM_DETAIL_NOHMDETAIL is NOT NULL;
err_num number;
err_msg varchar2(250);
begin
for c1rec in c1 loop
BEGIN
insert into ET_MAIN_WPPJ_JOZRIN.IND_HKMLK (
HKMLK_ID,
CREATED_BY,
CREATED_DATE,
LAST_MODIFIED_BY,
LAST_MODIFIED_DATE,
VERSION,
ID_HAKMILIK,
NO_HAKMILIK,
NO_BANGUNAN,
NO_TINGKAT,
NO_PETAK,
UPI,
FLAG_GANTUNG,
FLAG_TUKARGANTI,
TARIKH_KUTIPAN_ETANAH
)
values (
SEQ_HKMLK.NEXTVAL,
'DMS',
SYSDATE,
'DMS',
SYSDATE,
'0',
(select substr(HM_DETAIL_NOHMDETAIL,1,17) from strata_PJ_BORANG4),
(select substr(HM_DETAIL_NOHMDETAIL,-8) from strata_PJ_BORANG4),
-- substr(STRATA_PJ_BORANG4.HM_DETAIL_NOHMDETAIL,1,17),
-- substr(STRATA_PJ_BORANG4.HM_DETAIL_NOHMDETAIL,-8),
(SELECT HM_DETAIL_NOBGN FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL),
(SELECT HM_DETAIL_NOTKT FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL),
(SELECT HM_DETAIL_NOPETAK FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL),
(SELECT HM_DETAIL_NOHMDETAIL FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL),
'N',
'N',
SELECT HM_DETAIL_PUNGUT_DATETIME FROM STRATA_PJ_BORANG1 A, STRATA_PJ_BORANG4 B WHERE A.DAF_HM_NOFAIL = B.HM_DETAIL_NOFAIL),
);
END;
END LOOP;
END;

In your code you are not providing enough values compared to the number of columns you want to insert in. This happens, because the sub-selects are used for one column, not several. I've rewritten your statement as a single insert without loop. I'm using a CTE (common table expression) in order to put the logic in one place:
insert into ET_MAIN_WPPJ_JOZRIN.IND_HKMLK
(HKMLK_ID, CREATED_BY, CREATED_DATE, LAST_MODIFIED_BY, LAST_MODIFIED_DATE, VERSION,
ID_HAKMILIK,NO_HAKMILIK, NO_BANGUNAN,NO_TINGKAT,NO_PETAK,UPI,FLAG_GANTUNG,
FLAG_TUKARGANTI,TARIKH_KUTIPAN_ETANAH
)
with borang4_data as
(
select
SEQ_HKMLK.NEXTVAL HKMLK_ID,
'DMS' CREATED_BY,
SYSDATE CREATED_DATE,
'DMS' LAST_MODIFIED_BY,
SYSDATE LAST_MODIFIED_DATE,
'0' VERSION,
substr(borang4.HM_DETAIL_NOHMDETAIL, 1, 17) ID_HAKMILIK,
substr(borang4.HM_DETAIL_NOHMDETAIL, -8) NO_HAKMILIK,
borang1.NO_BANGUNAN NO_BANGUNAN,
borang1.NO_TINGKAT NO_TINGKAT,
borang1.NO_PETAK NO_PETAK,
borang1.UPI UPI,
borang1.FLAG_GANTUNG FLAG_GANTUNG,
borang1.FLAG_TUKARGANTI FLAG_TUKARGANTI,
borang1.TARIKH_KUTIPAN_ETANAH TARIKH_KUTIPAN_ETANAH
from PUTRAJAYA.STRATA_PJ_BORANG4 borang4
left join
(SELECT HM_DETAIL_NOBGN NO_BANGUNAN,
HM_DETAIL_NOTKT NO_TINGKAT,
HM_DETAIL_NOPETAK NO_PETAK,
HM_DETAIL_NOHMDETAIL UPI,
'N' FLAG_GANTUNG,
'N' FLAG_TUKARGANTI,
HM_DETAIL_PUNGUT_DATETIME TARIKH_KUTIPAN_ETANAH
FROM STRATA_PJ_BORANG1) borang1
on borang1.DAF_HM_NOFAIL = borang4.HM_DETAIL_NOFAIL
WHERE borang4.HM_DETAIL_NOHMDETAIL is NOT NULL
)
select * from borang4_data;
I've made some assumptions regarding the joins looking at your code. Please verify if this works for you.

Related

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 write multiple SQL query statement and PL/SQL statement inside one PL/SQL statement

In 1 PL/SQL block have to use multiple SELECT query and one block statement. In this block statement we have to take counts before insert query and once insert statement run then after have to take its after_counts of the id value that mentioned below.
set heading off
set colsep '|'
set feedback off
set sqlformat csv
set trimspool on
spool output.txt
declare
ln_rec tab1%rowtype;
lv varchar(20);
sid tab.id%type;
b_cnts number;
a_cnts number;
type sh_id is varray(10) of tab.col1%type;
id sh_id := sh_id(1, 3, 5, 7, 9, 11, 13, 15, 17, 19);
begin
select a.id, count(b.sub_id) into sid, b_cnts as "before_counts" from tab a, tab1 b;
for i in (select distinct b.sub_id from tab a, tab1 b where a.id in (1, 3, 5, 7, 9, 11, 13, 15, 17, 19))
loop
select * into ln_rec from tab1 where sub_id = i.sub_id;
insert into new_tab values(id, i.sub_id, lv);
commit;
end loop;
select a.id, count(b.sub_id) into sid, a_cnts as "after_counts" from tab a, tab b;
end;
spool off
But when i execute it then got error because of above SET system variable summary and in the insert statement due to id. I want output in the csv format or output format like where 3 columns should be generated as id, before_counts, after_counts & its proper value. Like this:-
<id> <before_counts> <after_counts> -- This heading should not appear because above used heading off
1 135 138
3 246 250
5 298 302
7 389 399
.........
Something like this:
DECLARE
type sh_id is varray(10) of tab.col1%type;
a_cnts number;
b_cnts number;
lv varchar2(20) := NULL; -- This is never modified in your code.
ids sh_id := sh_id(1, 3, 5, 7, 9, 11, 13, 15, 17, 19);
BEGIN
FOR i IN 1 .. ids.COUNT LOOP
SELECT count(b.sub_id)
INTO b_cnts
FROM tab a
INNER JOIN tab1 b
ON ( <some join conditions> ) -- you need to specify the join
WHERE a.id = ids(i);
INSERT INTO new_tab
SELECT DISTINCT
ids(i),
b.sub_id,
lv
FROM tab a
INNER JOIN tab1 b
ON ( <some join conditions> ) -- you need to specify the join
WHERE a.id = ids(i);
-- Assuming you have a trigger to populate tab or tab1 from new_tab then
a_cnts := b_cnts + SQL%ROWCOUNT;
-- Otherwise:
SELECT count(b.sub_id)
INTO a_cnts
FROM tab a
INNER JOIN tab1 b
ON ( <some join conditions> ) -- you need to specify the join
WHERE a.id = ids(i);
DBMS_OUTPUT.PUT_LINE( ids(i) || CHR(9) || b_cnts || CHR(9) || a_cnts );
END LOOP;
-- Commit outside the loop
COMMIT;
END;
/

Trigger code not working in oracle to avoid duplicate data

Below trigger code(converted from MSSQL) in oracle is not working.
The two columns should not have duplicate row in the table. I'm creating a trigger for accomplishing this.
Can anyone help in updating/correcting the above code to be used in my trigger?
/*
**Unique Constraint for TestOracle - TestTinyInt.
*/
if (Update(UpdOperation) or Update(TestTinyInt)) THEN
IF Exists(
SELECT * FROM inserted i INNER LOOP JOIN TestOracle x ON
(i.TestTinyInt=x.TestTinyInt)
WHERE i.updoperation IN (0, 1) AND x.updoperation IN (0, 1) GROUP BY x.TestTinyInt
HAVING COUNT(*) > 1)
BEGIN
RAISERROR( 'Invalid attempt to enter duplicate TestTinyInt in TestOracle', 16, -1 )
ROLLBACK TRAN
RETURN
END
END
The best way is to create 2 unique index on each of columns. By doing this you are eliminating duplication in particual column(like #a_horse_with_no_name mentioned).
For other case you don't need to use triger, you need only simple where condition
where Column_A not in (select Column_B from table) and Column_B not in (Select Column_A in table).
EDIT:
It if have to be done in trigger THEN :
create or replace trigger ... instead of insert or update on ...
Declare
dummy number;
Begin
select 1 into dummy from dual where :new.Column_A in (select Column_B from table) or new:.Column_B in (Select Column_A in table);
if dummy <> 1 THEN
INSERT
END IF;
END;
EDIT2: IF you don't want unique index and tirgger here is solution :
create or replace trigger ... instead of insert or update on ...
Declare
dummy number;
Begin
select count(*) into dummy from(
SELECT COL1 FROM (
(select :new.Column_A col1 from dual
UNION
select :new.Column_B from dual))
INTERSECT
SELECT COL2 FROM (
( SELECT COLUMN_A COL2 from table
UNION
SELECT COLUMN_B from table));
if dummy = 0 THEN
INSERT
END IF;
END;

How can I pass comma separated value in cursor's select statement where clause

I have following Block which has a cursor and a select query. I want to pass the output of select, which is comma separated into
the cursor's select statement, into the where clause.
I know below code will throw an error because of SQL query in Declare section but how I achieve this using array or collection.
here, id column is number
code snippet:
declare
test varchar2(30);
SELECT LISTAGG(value, ', ') WITHIN GROUP (ORDER BY value2) into test from table3 where value2=12;
cursor c1 (select * from table where id in (test))
begin
for i in c1 loop
null;
end loop;
end;
Why would you ever do this?
You can simple write you select as:
Select * from table where id in (select value from table3 where value2=12)
Edit:
Also you need to open your cursor c1, for it to work AFAIK.
You can use a collection and the MEMBER OF operator:
Oracle Setup:
CREATE TYPE IntegerList AS TABLE OF NUMBER(8,0);
/
CREATE TABLE table1 ( value, value2 ) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 3, 4 FROM DUAL UNION ALL
SELECT 5, 3 FROM DUAL UNION ALL
SELECT 7, 2 FROM DUAL;
CREATE TABLE table2 ( id, value ) AS
SELECT 1, 11 FROM DUAL UNION ALL
SELECT 2, 22 FROM DUAL UNION ALL
SELECT 3, 33 FROM DUAL UNION ALL
SELECT 7, 77 FROM DUAL;
PL/SQL:
DECLARE
test IntegerList;
c1 SYS_REFCURSOR;
BEGIN
SELECT value
BULK COLLECT INTO test
FROM table1;
FOR r IN ( SELECT * FROM table2 WHERE id MEMBER OF test ) LOOP
DBMS_OUTPUT.PUT_LINE( r.id || ', ' || r.value );
END LOOP;
END;
/
Output:
1, 11
3, 33
7, 77
db<>fiddle here

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;
/