How to write multiple SQL query statement and PL/SQL statement inside one PL/SQL statement - sql

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

Related

Too many values inside a Where

I want to get all products inside an order. I give to my procedure the ID of the order and then I want to list all of them.
Here is the database scheme of the tables I use in this procedure:
And here is the procedure:
CREATE OR REPLACE PROCEDURE exercitiu6(v_ID_Comanda Comanda.ID_Comanda%TYPE) AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat:= imbricat();
prod Produs%ROWTYPE;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs BULK COLLECT INTO imbricat_produse FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
SELECT ID_Produs, nume, pret INTO prod FROM Produs
WHERE ID_Produs = imbricat_produse(i);
p(j):= prod;
j:= j + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
I get the Error Sql Statement ignored; too many values on this line:
WHERE ID_Comanda = v_ID_Comanda;
How do I solve this error?
Use a JOIN and a single cursor:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
BEGIN
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR i IN (
SELECT p.nume
FROM ProduseComanda c
INNER JOIN Produs p
ON p.ID_Produs = c.ID_Produs
WHERE c.ID_Comanda = v_ID_Comanda
)
LOOP
DBMS_OUTPUT.PUT_LINE(i.nume);
END LOOP;
END;
/
Then, for the sample data:
CREATE TABLE produsecomanda (ID_Produs, ID_Comanda) AS
SELECT 1, 1 FROM DUAL UNION ALL
SELECT 2, 1 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL;
CREATE TABLE produs (ID_Produs, nume) AS
SELECT 1, 'Alice' FROM DUAL UNION ALL
SELECT 2, 'Beryl' FROM DUAL UNION ALL
SELECT 3, 'Carol' FROM DUAL;
Then:
BEGIN
DBMS_OUTPUT.ENABLE();
exercitiu6(1);
END;
/
Outputs:
Comanda cu ID-ul 1 contine urmatoarele produse:
Alice
Beryl
Carol
If you want to fix your code then the error is not with the WHERE clause but with the mismatch between the number of columns in the SELECT clause and the INTO clause. To fix it you need to use SELECT * INTO ... rather than naming all the columns when you are working with %ROWTYPE variables:
CREATE OR REPLACE PROCEDURE exercitiu6(
v_ID_Comanda ProduseComanda.ID_Comanda%TYPE
)
AS
TYPE produse IS TABLE OF Produs%ROWTYPE INDEX BY PLS_INTEGER;
p produse;
TYPE imbricat IS TABLE OF ProduseComanda.ID_Produs%TYPE;
imbricat_produse imbricat;
i number:=0;
j number:=0;
BEGIN
SELECT ID_Produs
BULK COLLECT INTO imbricat_produse
FROM ProduseComanda
WHERE ID_Comanda = v_ID_Comanda;
FOR i IN imbricat_produse.FIRST..imbricat_produse.LAST LOOP
j:= j + 1;
SELECT *
INTO p(j)
FROM Produs
WHERE ID_Produs = imbricat_produse(i);
END LOOP;
DBMS_OUTPUT.PUT_LINE('Comanda cu ID-ul ' || v_ID_Comanda || ' contine urmatoarele produse: ');
FOR j IN p.FIRST..p.LAST LOOP
DBMS_OUTPUT.PUT_LINE(p(j).nume);
END LOOP;
END;
/
fiddle

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

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.

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

Need help on writing sql query with dynamic columns

I have to write a query which does below. I tried but couldn't write. Please help me.
I have table which returns below result set.
select *
from table1; --(rowid and ColumnName are columns of the table)
Output:
rowid ColumnName
------------------------------
1 Segment1
2 Segment2
I have another table which has below structure : (Segment1 and Segment2 are columns here)
select *
from table2;
Output:
appId Segment1 Segment2 Segment3
---------------------------------------------
a1 fld1 fld2 per
a2 cmp1 hcd4 klp
I need to write a query, which reads the "ColumnName" values from first table and retrieves column values in the second table.
That means, from the table1, I will know what are the available columns I the table2 and from table2, I will know what is the data stored against those columns.
Please let me know if I am not clear.
This query is in Oracle SQL
As mentioned in the comment you need a PLSQL block with dynamic sql. See below an example:
Tables:
create table table1 (row_id number,
ColumnName varchar2(100))
create table table2 (appId number,
Segment1 varchar2(100),
Segment2 varchar2(100),
Segment3 varchar2(100));
Insert all
into TABLE1 (ROW_ID, COLUMNNAME) Values (1, 'Segment1')
into TABLE1 (ROW_ID, COLUMNNAME) Values (2, 'Segment2')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (1, 'RRR', 'KKK', 'MMM')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (2, 'ZZZ', 'PPP', 'QQQ')
into TABLE2 (APPID, SEGMENT1, SEGMENT2, SEGMENT3) Values (3, 'LLL', 'NNN', 'DDD')
select * from dual;
Code:
DECLARE
var VARCHAR2 (1000);
v_sql VARCHAR2 (2000);
TYPE x_var IS TABLE OF VARCHAR2(1000);
z_var x_var;
num number:=0;
BEGIN
FOR rec IN ( SELECT DISTINCT columnname
FROM table1
ORDER BY 1)
LOOP
num := num +1;
if num = 1 then
var:= rec.columnname;
else
var := var || ' || '' , ''||' || rec.columnname;
end if;
END LOOP;
var := RTRIM (LTRIM (var, ','), ',');
v_sql := 'select '|| var ||' from table2';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO z_var;
FOR i IN 1 .. z_var.COUNT
LOOP
DBMS_OUTPUT.put_line (z_var(i));
END LOOP;
END;
Output:
SQL> /
RRR , KKK
ZZZ , PPP
LLL , NNN
Dynamic columns in a SQL statement are almost always a bad idea. There's usually a way to avoid these kind of problems and build a simpler solution.
But if this is one of those rare times when you really need to run dynamic SQL in SQL then you'll need to install and run something like my open source project Method4.
For example:
create table table1 as
select 1 id, 'Segment1' columnName from dual union all
select 2 id, 'Segment2' columnName from dual;
create table table2 as
select 'a1' appId, 'fld1' Segment1, 'fld2' Segment2, 'per' Segment3 from dual union all
select 'a2' appId, 'cmp1' Segment1, 'hcd4' Segment2, 'klp' Segment3 from dual;
select * from table(method4.dynamic_query(
q'[
select
'select appID, '
||listagg(columnName, ',') within group (order by id)
||' from table2'
sql_statement
from table1
]'
));
APPID SEGMENT1 SEGMENT2
----- -------- --------
a1 fld1 fld2
a2 cmp1 hcd4
There are a lot of downsides to running this way. The code is complicated, slow, and has some odd behavior. For an explanation of how this works, see this article
by Adrian Billington.
Will the below PL SQL block help your requirement.
BEGIN
FOR iter IN (
SELECT column_name
FROM all_tab_columns
WHERE upper(table_name) = 'table1'
AND UPPER(column_name) LIKE 'SEGMENT%'
)
LOOP
SELECT iter.column_name INTO temp_table FROM table1
dbms_output.put_line(temp_table.column_name);
END LOOP;
END;
/
Say you have tables like the following:
SQL> select * from someTable;
COLUMN1 COLUMN2 COLUMN3
---------- ---------- ----------
1 2 3
2 4 6
3 6 9
SQL> select * from tableOfColumns;
COLUMNN
-------
column1
column3
You may need something like the following:
SQL> declare
2 type tListOfResults is table of varchar2(1000);
3 vSQL varchar2(1000);
4 vResult tListOfResults ;
5 begin
6 select 'select ' || listagg (columnName, ' || '', '' || ') within group (order by columnName) || ' from someTable'
7 into vSQL
8 from tableOfColumns;
9 --
10 execute immediate vSQL bulk collect into vResult;
11 if vResult.count() > 0 then
12 for i in vResult.first .. vResult.last loop
13 dbms_output.put_line(vResult(i));
14 end loop;
15 end if;
16 end;
17 /
1, 3
2, 6
3, 9
PL/SQL procedure successfully completed.

How can I debug open for cursor statement?

I'm wondering if it possible to debug similar statements in an easy way.
When I save the 'select string' in a variable , it become 'long' and I would need to split it in more variables. I' presenting the very simplified sample:
OPEN o_recordset FOR
'SELECT distinct
a, b, c
FROM t1,t2
WHERE'
|| CASE
WHEN i_use_ctr_id = 1 then ' a = b'
END
|| ' ORDER BY 1 ASC , DECODE('''||i_sort_order||''',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
;
I wish to see the select like this (i_use_ctr_id = 1, i_sort_order = a)
SELECT distinct
a, b, c
FROM t1,t2
WHERE a = b
END
ORDER BY 1 ASC , DECODE('a',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
;
Use a debug procedure that either writes to a file or inserts in a table (with an autonomous transaction).
For instance:
CREATE TABLE debug_t (ts timestamp default systimestamp, data VARCHAR2(4000));
CREATE OR REPLACE PROCEDURE debug_p (p VARCHAR2) IS
PRAGMA autonomous_transaction;
BEGIN
-- you should split p if length is > 4000
INSERT INTO debug_t (data) VALUES (p);
COMMIT;
END;
/
Then you can debug values by inserting a single line of code:
SQL> DECLARE
2 l_sql VARCHAR2(4000);
3 i_use_ctr_id NUMBER;
4 i_sort_order NUMBER;
5 BEGIN
6 l_sql := 'SELECT distinct
7 a, b, c
8 FROM t1,t2
9
10 WHERE'
11 || CASE
12 WHEN i_use_ctr_id = 1 then ' a = b'
13 END
14 || ' ORDER BY 1 ASC , DECODE('''||i_sort_order
15 ||''',null, '''', ''a'', '' NULLS LAST '', ''b'' ,'',2 ASC NULLS LAST'')'
16 ;
17 debug_p(l_sql); -- debug before opening cursor
18 END;
19 /
PL/SQL procedure successfully completed
SQL> select * from debug_t;
TS DATA
----------------- --------------------------------------------------------------------------------
11/09/13 11:52:30 SELECT distinct
a, b, c
FROM t1,t2
WHERE ORDER BY 1 ASC , DECODE('',null, '', 'a', ' NULLS LAST ', 'b' ,',2 ASC