SQL Oracle object%rowtype dynamic - sql

I have an object which is a ROWTYPE from a table and need to recover some of the columns to an array list.
For example:
The ROWTYPE contents Name1, Last_Name1, Id1, Adress1...Name25,Last_Name25,Id25,Adress25 in columns.
I need to know if I can recover them by a dynamic way with a loop to an array type like this:
Name(1-25),Last_Name(1-25),Id(1-25),Adress(1-25).

Given a simplified example of the problem with the table:
CREATE TABLE table_name (id1, name1, id2, name2, id3, name3, id4, name4) AS
SELECT 1, 'N1', 2, 'N2', 3, 'N3', 4, 'N4' FROM DUAL;
I am assuming, that to get the %ROWTYPE variable, you are using something like:
DECLARE
data TABLE_NAME%ROWTYPE;
BEGIN
SELECT * INTO data FROM table_name FETCH FIRST ROW ONLY;
DBMS_OUTPUT.PUT_LINE('1: ' || data.id1 || ', ' || data.name1);
DBMS_OUTPUT.PUT_LINE('2: ' || data.id2 || ', ' || data.name2);
DBMS_OUTPUT.PUT_LINE('3: ' || data.id3 || ', ' || data.name3);
DBMS_OUTPUT.PUT_LINE('4: ' || data.id4 || ', ' || data.name4);
END;
/
Rather than trying to dynamically access the %ROWTYPE record, why do you not bypass the problem and separate the values in the SELECT statement using UNPIVOT in a cursor and then you can use %ROWTYPE on the cursor (rather than the table):
DECLARE
CURSOR cur IS
SELECT id, name
FROM (SELECT * FROM table_name FETCH FIRST ROW ONLY)
UNPIVOT (
(id, name)
FOR idx IN (
(id1, name1) AS 1,
(id2, name2) AS 2,
(id3, name3) AS 3,
(id4, name4) AS 4
)
);
TYPE cur_row_arr IS TABLE OF cur%ROWTYPE;
rw cur%ROWTYPE;
arr cur_row_arr := cur_row_arr();
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rw;
EXIT WHEN cur%NOTFOUND;
arr.EXTEND;
arr(arr.COUNT) := rw;
END LOOP;
CLOSE cur;
FOR i IN 1 .. arr.COUNT LOOP
DBMS_OUTPUT.PUT_LINE( i || ': ' || arr(i).id || ', ' || arr(i).name );
END LOOP;
END;
/
db<>fiddle here

Don't try to do it dynamically, just hard-code the values.
Given the simplified example:
CREATE TABLE table_name (id1, name1, id2, name2, id3, name3, id4, name4) AS
SELECT 1, 'N1', 2, 'N2', 3, 'N3', 4, 'N4' FROM DUAL;
Then:
CREATE PROCEDURE test(
v_row IN TABLE_NAME%ROWTYPE
)
IS
TYPE details_type IS RECORD(
id TABLE_NAME.ID1%TYPE,
name TABLE_NAME.NAME1%TYPE
);
TYPE details_tab_type IS TABLE OF details_type;
details details_tab_type := details_tab_type();
BEGIN
details.EXTEND(4);
details(1) := details_type(v_row.id1, v_row.name1);
details(2) := details_type(v_row.id2, v_row.name2);
details(3) := details_type(v_row.id3, v_row.name3);
details(4) := details_type(v_row.id4, v_row.name4);
FOR i IN 1 .. details.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(i || ': ' || details(i).id || ', ' || details(i).name);
END LOOP;
END;
/
All you need to do is write the first assignment statement details(1) := details_type(v_row.id1, v_row.name1); and then copy-paste multiple copies and increment the numbers by 1 each time.
Then you can call it using:
DECLARE
v_row TABLE_NAME%ROWTYPE;
BEGIN
SELECT * INTO v_row FROM table_name FETCH FIRST ROW ONLY;
test(v_row);
END;
/
Which outputs:
1: 1, N1
2: 2, N2
3: 3, N3
4: 4, N4
db<>fiddle here

Related

cursor for loop & dynamic SQL - Snowflake

I'm attempting to write a procedure that takes in a list of tables and date_column to create some row_counts by calendar dates for reconciliation purposes.
SELECT t.*
FROM (
VALUES ('tbl1', 'created_date')
, ('tbl2', 'modify_date')
, ('tbl3', 'last_seen_date')
) t(tbl, dt)
+----+--------------+
|TBL |DT |
+----+--------------+
|tbl1|created_date |
|tbl2|modify_date |
|tbl3|last_seen_date|
+----+--------------+
I'm connected to Snowflake via a JDBC connection using Datagrip - so I assume I need to follow the classic SnowSQL part of the documentation:
https://docs.snowflake.com/en/developer-guide/snowflake-scripting/loops.html#cursor-based-for-loops
EXECUTE IMMEDIATE $$
DECLARE
dt text
, tbl text;
c1 CURSOR FOR SELECT dt, tbl from t;
BEGIN
FOR record in c1 DO
dt := record.dt
tbl := record.tbl
stmt =: 'SELECT COUNT(*)' ||
CONCAT(', DAYOFMONTH(', $dt, ')') ||
CONCAT('\n FROM ', $tbl) ||
CONCAT('\n WHERE YEAR(', $dt, ')', ' = YEAR(CURRENT_DATE)') ||
CONCAT('\n AND MONTH(', $dt, ')', ' = MONTH(CURRENT_DATE)') ||
'\n GROUP BY' ||
CONCAT('\n DAYOFMONTH(', $dt, ')')
EXECUTE IMMEDIATE stmt -- will adapt this to be an update statement eventually.
END FOR
end;
$$
This returns a SQL Compilation error, I've tried a few different variations of this but I'm none the wiser on how to proceed.
Instead of concatenating the query string which makes it almost unreadable it could be rewritten using bind variables:
DECLARE
dt text;
tbl text;
stmt text;
c1 CURSOR FOR SELECT dt, tbl from t;
BEGIN
FOR record in c1 DO
dt := record.dt;
tbl := record.tbl;
stmt := 'INSERT INTO result(cnt, day_of_month)
SELECT COUNT(*), DAYOFMONTH(IDENTIFIER(?)) AS day_of_month
FROM TABLE(?)
WHERE YEAR(IDENTIFIER(?)) = YEAR(CURRENT_DATE)
AND MONTH(IDENTIFIER(?)) = MONTH(CURRENT_DATE)
GROUP BY day_of_month';
EXECUTE IMMEDIATE :stmt USING (dt, tbl, dt, dt);
RETURN stmt;
END FOR;
END;
If column or table is parameter it should be wrapped with IDENTIFIER/TABLE funtion.
For sample data:
CREATE OR REPLACE TABLE t AS
SELECT 'col1' AS dt, 'tab1' AS tbl UNION ALL
SELECT 'col2' AS dt, 'tab1' ;
CREATE TABLE tab1(col1 DATE, col2 DATE) AS
SELECT CURRENT_DATE(), CURRENT_DATE()-40;
CREATE TABLE result(cnt INT, day_of_month INT);
SELECT * FROM result;
There are lots of minor issues like missing semicolons etc. Here is the fixed script:
DECLARE
dt text;
tbl text;
stmt text;
c1 CURSOR FOR SELECT dt, tbl from t;
BEGIN
FOR record in c1 DO
dt := record.dt;
tbl := record.tbl;
stmt := 'SELECT COUNT(*)' ||
CONCAT(', DAYOFMONTH(', dt, ')') ||
CONCAT('\n FROM ', tbl) ||
CONCAT('\n WHERE YEAR(', dt, ')', ' = YEAR(CURRENT_DATE)') ||
CONCAT('\n AND MONTH(', dt, ')', ' = MONTH(CURRENT_DATE)') ||
'\n GROUP BY' ||
CONCAT('\n DAYOFMONTH(', dt, ')');
-- EXECUTE IMMEDIATE :stmt;
RETURN stmt;
END FOR;
END;

How to take where clause conditions from table column in oracle SQL or plsql

How to take where clause conditions from table column in oracle plsql.
E.g. data in table
Condition
1.sourceSystemId = 'SN'
2.AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
e.g query:
select * from xyz where rule='abc' and "sourceSystemId = 'SN'"
select * from xyz where rule='abc' AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
Not entirely sure what you're asking here, but I would imagine that
select * from xyz where rule='abc' AND(coverageType='AD',amountType1='PREMIUM',premiumFrequency='REGULAR',yearOfPremium='1')
would become
select * from xyz
where rule='abc'
AND coverageType='AD'
and amountType1='PREMIUM'
and premiumFrequency='REGULAR'
and yearOfPremium='1'
I suppose you want something like :
DECLARE
l_query VARCHAR2(2000) := 'select * from xyz where rule=''abc''';
l_result xyz%ROWTYPE;
l_cursor SYS_REFCURSOR;
BEGIN
dbms_output.put_line(l_query);
FOR clause IN (SELECT condition
FROM conditions)
LOOP
l_query := l_query||' AND '||clause.condition;
END LOOP;
OPEN l_cursor FOR l_query;
LOOP
FETCH l_cursor INTO l_result;
EXIT WHEN l_cursor%NOTFOUND;
..
-- your processing
END LOOP;
CLOSE l_cursor;
END;
Here is example of SQL solution. I used justt first and last condition but you can get them all...
WITH
xyz As
(
Select 1 "ID", 'abc' "RULE", 'AD' "COVERAGETYPE", 'PREMIUM' "AMOUNTTYPE1", 'REGULAR' "PREMIUMFREQUENCY", '1' "YEAROFPREMIUM" From Dual
UNION
Select 2 "ID", 'abc' "RULE", 'BF' "COVERAGETYPE", 'ORDINARY' "AMOUNTTYPE1", 'EXTRA' "PREMIUMFREQUENCY", '2' "YEAROFPREMIUM" From Dual
UNION
Select 3 "ID", 'abc' "RULE", 'AD' "COVERAGETYPE", 'PREMIUM' "AMOUNTTYPE1", 'REGULAR' "PREMIUMFREQUENCY", '1' "YEAROFPREMIUM" From Dual
),
conditions As
(
SELECT UPPER('coverageType=AD,amountType1=PREMIUM,premiumFrequency=REGULAR,yearOfPremium=1') "CND" From Dual
)
SELECT
x.ID, x.RULE, x.COVERAGETYPE, x.AMOUNTTYPE1, x.PREMIUMFREQUENCY, x.YEAROFPREMIUM
FROM
xyz x
INNER JOIN
conditions c ON(1=1)
WHERE
x.RULE = 'abc' And
x.COVERAGETYPE = CASE WHEN InStr(c.CND || ',', 'COVERAGETYPE=') = 0 THEN x.COVERAGETYPE
ELSE SubStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'COVERAGETYPE=') + Length('COVERAGETYPE=')), 1, InStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'COVERAGETYPE=') + Length('COVERAGETYPE=') + 1), ',')) END And
x.YEAROFPREMIUM = CASE WHEN InStr(c.CND || ',', 'YEAROFPREMIUM=') = 0 THEN x.YEAROFPREMIUM
ELSE SubStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'YEAROFPREMIUM=') + Length('YEAROFPREMIUM=')), 1, InStr(SubStr(c.CND || ',', InStr(c.CND || ',', 'YEAROFPREMIUM=') + Length('YEAROFPREMIUM=') + 1), ',')) END
Result:
ID RULE COVERAGETYPE AMOUNTTYPE1 PREMIUMFREQUENCY YEAROFPREMIUM
1 abc AD PREMIUM REGULAR 1
3 abc AD PREMIUM REGULAR 1

Oracle dynamic query (return or select) result as table

How can I show the result of running a dynamic query as a table in the output?
I want to show the result of the following query as a table in the output.
my table :
create table myTable(ROW_NAME varchar(10),COLUMN_NAME varchar(10),COLUMN_NAME_VALUE varchar(10));
table data :
insert into myTable (ROW_NAME,COLUMN_NAME,COLUMN_NAME_VALUE)
select 'ROW1','COL1','R1C1' from dual
union all select 'ROW1','COL2','R1C2' from dual
union all select 'ROW1','COL3','R1C3' from dual
union all select 'ROW2','COL1','R2C1' from dual
union all select 'ROW2','COL2','R2C2' from dual
union all select 'ROW2','COL3','R2C3' from dual
union all select 'ROW3','COL1','R3C1' from dual
union all select 'ROW3','COL2','R3C3' from dual
union all select 'ROW3','COL3','R3C3' from dual
my dynamic query :
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
type pivotted is record (row_name myTable.row_name%type, col1 myTable.column_name_value%type, col2 myTable.column_name_value%type, col3 myTable.column_name_value%type);
piv_rec pivotted;
BEGIN
select (select LISTAGG('''' || COLUMN_NAME || '''', ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME')) into sqlCommand from dual;
DBMS_OUTPUT.PUT_LINE(sqlCommand);
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COL1 || ' COL2: ' || piv_rec.COL2 || ' COL3: ' || piv_rec.COL3);
END LOOP;
CLOSE pivot_cv;
END;
/
demo in db<>fiddle
Thanks for any help
Maybe I misunderstood, I guess what you want is this?
select 'ROW_NAME ' || t1.row_name || ' ' || listagg(t1.column_name || ': ' || t1.column_name_value, ' ')
within group(order by t1.column_name)
from myTable t1
group by t1.row_name
order by t1.row_name

oracle dynamic pivot error: SQL command not properly ended

I have a table whose data is obtained according to the desired output with pivot. But I want to create the number of columns dynamically.
my table :
create table myTable(ROW_NAME varchar(10),COLUMN_NAME varchar(10),COLUMN_NAME_VALUE varchar(10));
table data :
insert into myTable (ROW_NAME,COLUMN_NAME,COLUMN_NAME_VALUE)
select 'ROW1','COL1','R1C1' from dual
union all select 'ROW1','COL2','R1C2' from dual
union all select 'ROW1','COL3','R1C3' from dual
union all select 'ROW2','COL1','R2C1' from dual
union all select 'ROW2','COL2','R2C2' from dual
union all select 'ROW2','COL3','R2C3' from dual
union all select 'ROW3','COL1','R3C1' from dual
union all select 'ROW3','COL2','R3C3' from dual
union all select 'ROW3','COL3','R3C3' from dual
my query :
select * from myTable
pivot (
max (COLUMN_NAME_VALUE)
for COLUMN_NAME
in (
'COL1' as COL1,'COL2' as COL2,'COL3' as COL3
)
)
ORDER BY ROW_NAME;
The above query works but I want to get the columns dynamically.
my dynamic query :
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
piv_rec mytable%ROWTYPE;
BEGIN
select (select LISTAGG(COLUMN_NAME, ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME;')) into sqlCommand from dual;
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COLUMN_NAME_VALUE || 'COL2: ' || piv_rec.COLUMN_NAME_VALUE || 'COL3: ' || piv_rec.COLUMN_NAME_VALUE);
END LOOP;
CLOSE pivot_cv;
END;
/
Note : The equivalent of the above query can be generated on SQL Server and I have created it.
demo in db<>fiddle
Thanks for any help
There are 3 problems in your script:
semicolon-terminated dynamic query (that's the cause of "SQL command not properly ended")
identifiers in in clause instead of string literals (you can use 'foo' or 'foo' as foo but not foo alone)
improper piv_rec type - use table format after pivot, not before pivot
Summary:
DECLARE
mycols VARCHAR2(1000);
sqlCommand varchar2(1000);
TYPE PivotCurTyp IS REF CURSOR;
pivot_cv PivotCurTyp;
type pivotted is record (row_name myTable.row_name%type, col1 myTable.column_name_value%type, col2 myTable.column_name_value%type, col3 myTable.column_name_value%type);
piv_rec pivotted;
BEGIN
select (select LISTAGG('''' || COLUMN_NAME || '''', ',') from myTable group by ROW_NAME FETCH FIRST 1 ROWS ONLY) into mycols from dual;
select Concat('select * from myTable pivot ( max (COLUMN_NAME_VALUE) for COLUMN_NAME in (',Concat(mycols,')) ORDER BY ROW_NAME')) into sqlCommand from dual;
DBMS_OUTPUT.PUT_LINE(sqlCommand);
OPEN pivot_cv FOR sqlCommand;
LOOP
FETCH pivot_cv INTO piv_rec;
EXIT WHEN pivot_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('ROW_NAME: ' || piv_rec.ROW_NAME || ' COL1: ' ||
piv_rec.COL1 || ' COL2: ' || piv_rec.COL2 || ' COL3: ' || piv_rec.COL3);
END LOOP;
CLOSE pivot_cv;
END;
/
updated db fiddle (BTW composing fiddle was very motivating to help)

Function to get results for all values in cursor column in PL/SQL

I've got problem with query. As for now I've got:
create or replace function f_test(par_cat in varchar2) return sys_refcursor is
l_str varchar2(1000);
l_rc sys_refcursor;
begin
for cur_r in (select cat, kw_crt
from (select owner_category cat,
substr(PROPERTY_ID, 1, 4) kw_crt,
ROW_NUMBER() OVER(PARTITION BY owner_category
ORDER BY owner_category, substr(PROPERTY_ID, 1, 4)) rank
from owners)
where rank < 3
and cat = par_cat) loop
l_str := 'select * ' || 'from owners ' ||
'where substr(PROPERTY_ID,1,4) in (' || chr(39) ||
cur_r.kw_crt || chr(39) || ')' || ' and Owner_category = ' ||
chr(39) || cur_r.cat || chr(39);
end loop;
open l_rc for l_str;
return l_rc;
end;
/
where
select owner_category cat,
substr(PROPERTY_ID, 1, 4) kw_crt,
ROW_NUMBER() OVER(PARTITION BY owner_category ORDER BY owner_category,
substr(PROPERTY_ID, 1, 4)) rank
from owners)
where rank < 3
and cat = par_cat
is table
+---------+----------+
| cat | kw_crt |
+---------+----------+
| retired | AAD1 |
| retired | AAH2 |
+---------+----------+
The problem is when I run this function I receive results with only 'AAH2' code and I would like to get for all values in col kw_crt meaning AAH2 or AAD1. Could you help me change the function?
You don't need to repeat SQL query redundantly, and looping is not needed also. Just need to use bind variable for par_cat parameter (call :i_par_cat) within the query string to bring all records conforming to your query in the result set. So, recreate your function as :
CREATE OR REPLACE FUNCTION f_test(par_cat in varchar2) RETURN sys_refcursor is
l_str varchar2(1000);
l_rc sys_refcursor;
BEGIN
l_str := 'SELECT cat, kw_crt
FROM (SELECT owner_category cat,
SUBSTR(property_id, 1, 4) kw_crt,
ROW_NUMBER() OVER
(PARTITION BY owner_category
ORDER BY owner_category, SUBSTR(property_id, 1, 4)) rank
FROM owners)
WHERE rank < 3
AND cat = :i_par_cat';
OPEN l_rc FOR l_str USING par_cat;
RETURN l_rc;
END;
/
You are doing way to much work looping through a cursor to build a dynamic query. Actually you do not need dynamic SQL (Execute Immediate) at all. Dynamic SQL is required only when you do not know the name of a db object (Table, Column, etc). In this case all that is known; the only unknown is the value of the parameter. So
create or replace
function f_test(p_par_cat in varchar2)
return sys_refcursor
is
l_rc sys_refcursor;
begin
open l_rc for
select cat, kw_crt
from ( select owner_category cat
, substr(property_id, 1, 4) kw_crt
, row_number()
over (partition by owner_category
order by owner_category, substr(property_id, 1, 4)
) rank
from owners
)
where rank < 3
and cat = p_par_cat;
return l_rc;
end f_test;
-- test
declare
rcursor sys_refcursor;
cat owners.owner_category%type;
kw_crt owners.property_id%type;
begin
rcursor := f_test('retired');
dbms_output.put_line('cat' || chr(09)|| chr(09) || 'kw_crt');
loop
fetch rcursor
into cat, kw_crt;
exit when rcursor%notfound;
-- dbms_output.put_line('cat: ' || cat || ' kw_crt: ' || kw_crt);
dbms_output.put_line(cat || chr(09) || kw_crt);
end loop;
end;
You also claim you only get 1 of the expected rows in the output. Helping with that requires you to post post the calling routine, which but you failed to do. Post that if you still need further help.