Evaluating dynamic PLSQL statment - oracle

The purpose of this proc is to combine columns of a record type into a line of text.
Example: SALARY= 80000, POSITION=ENG, ID=8
How can I evaluate l_stmt dynamically.
Please note I do not want to do it the traditional way by concatenation.
Open to other suggestions.
Thank you
CREATE TABLE Employee
(
ID INTEGER,
POSITION VARCHAR2 (32),
SALARY NUMBER
);
INSERT INTO EMPLOYEE (ID, POSITION, SALARY)
VALUES (1, 'ENG', '100000');
INSERT INTO EMPLOYEE (ID, POSITION, SALARY)
VALUES (2, 'PROGRAMMER', '80000');
COMMIT;
CREATE OR REPLACE PROCEDURE column_to_text (rec employee%ROWTYPE)
AS
TYPE tb IS TABLE OF VARCHAR2 (30)
INDEX BY BINARY_INTEGER;
l_colnames tb;
l_stmt VARCHAR2 (2500);
BEGIN
SELECT column_name
BULK COLLECT INTO l_colnames
FROM sys.all_tab_cols
WHERE table_name = 'EMPLOYEE';
FOR i IN 1 .. l_colnames.COUNT
LOOP
l_stmt :=
l_stmt || l_colnames (i) || '=' || 'REC.' || l_colnames (i) || ', ';
END LOOP;
DBMS_OUTPUT.PUT_LINE ( l_stmt );
--SALARY=REC.SALARY, POSITION=REC.POSITION, ID=REC.ID,
END;

Related

Passing dynamic Columns to Pivot in Oracle

I am new to oracle, I am trying to pass the dynamically generated column names to pivot in oracle using the below query
DECLARE
v_cols VARCHAR2(100);
v_query VARCHAR2(4000);
BEGIN
SELECT LISTAGG('''' ||product_code||'''',',') WITHIN GROUP (ORDER BY product_code)
INTO v_cols
FROM (
SELECT DISTINCT product_code
FROM pivot_test
);
v_query:='
SELECT *
FROM (
SELECT product_code,
quantity
FROM pivot_test)
PIVOT (sum(quantity) AS qunts
FOR product_code IN ('|| v_cols ||'));';
EXECUTE IMMEDIATE v_query;
--dbms_output.Put_line(v_cols);
--dbms_output.Put_line(v_query);
END;
The column generated is 'A','B','C','D' and the query generated with dynamic column is
SELECT *
FROM (
SELECT
product_code,
quantity
FROM pivot_test) PIVOT (sum(quantity) AS qunts FOR product_code IN ('A','B','C','D'));
Result:
When I take the above query and run it separately it is running correctly but when I use EXECUTE IMMEDIATE v_query; I get the error
ORA-00911: invalid character
ORA-06512: at line 20
I am not aware what is causing the problem here can any please point me what is wrong with this dynamic query execution
Value used for testing
CREATE TABLE pivot_test
(
id NUMBER,
customer_id NUMBER,
product_code VARCHAR2(5),
quantity NUMBER
);
INSERT INTO pivot_test VALUES (1,1,'A',10);
INSERT INTO pivot_test VALUES (2,1,'B',20);
INSERT INTO pivot_test VALUES (3,1,'C',30);
INSERT INTO pivot_test VALUES (4,2,'A',40);
INSERT INTO pivot_test VALUES (5,2,'C',50);
INSERT INTO pivot_test VALUES (6,3,'A',60);
INSERT INTO pivot_test VALUES (7,3,'B',70);
INSERT INTO pivot_test VALUES (8,3,'C',80);
INSERT INTO pivot_test VALUES (9,3,'D',90);
INSERT INTO pivot_test VALUES (10,4,'A',100);
COMMIT;
For the ORA-00911 error you need to remove the ; appended to the end of dynamically generated v_query
To display the output you can fetch the results into a cursor. The entire block can go into your dynamic SQL. A sample is below:
set serveroutput on;
declare
v_cols varchar2(100);
v_query varchar2(4000);
begin
select listagg('''' ||product_code||'''',',') within group (order by product_code) into v_cols
from(
select distinct product_code
from pivot_test
);
v_query:='
declare
cur_pivot_test sys_refcursor;
a number;
b number;
c number;
d number;
begin
open cur_pivot_test for
select * from (select product_code, quantity from pivot_test)
pivot (sum (quantity)
as qunts
for product_code in ('|| v_cols ||'));
fetch cur_pivot_Test into a, b, c, d;
close cur_pivot_test;
dbms_output.put_line (rpad(''A_QTY'',10) || '' | '' || rpad(''B_QTY'',10) || '' | '' || rpad(''C_QTY'',10) || '' | '' || rpad(''D_QTY'',10) );
dbms_output.put_line (rpad(a,10) || '' | '' || rpad(b,10) || '' | '' || rpad(c,10) || '' | '' || rpad(d,10) );
end;
';
execute immediate v_query;
end;
Output:
A_QTY | B_QTY | C_QTY | D_QTY
210 | 90 | 160 | 90

Can someone help me find the error in this procedure?

I would like to know where is the mistake in this procedure
set serveroutput on
create or replace procedure insert_table(column_name in varchar2, dat in varchar2)
as
table_name varchar2(100):= column_name || '_' || dat;
sql_create varchar2(100) := 'create table '||table_name||'(FIRSTNAME varchar2(100))';
sql_str varchar2(100);
CURSOR c_emp is
select FIRSTNAME
from employees
where column_name = dat;
begin
execute immediate sql_create;
for r_reg in c_emp
loop
sql_str:='INSERT INTO '||table_name||'('''||r_reg.firstname||''')';
execute immediate sql_str;
end loop;
end;
/
execute insert_table('CITY','London');
Edit:
Ok i add the correction mentioned below in the syntax error, but how can I do so that the parameter of the column name can be taken at the cursor, because for now it is of type varchar and 'CITY' should be a row name.
You will need to use the dynamic query in the cursor as follows. Also, you missed VALUES keyword in the INSERT statement which I have added (Please see inline comment in the codes)
Oracle sample data creation:
SQL> CREATE TABLE "EMPLOYEES" (
2 "ID" NUMBER,
3 "FIRSTNAME" VARCHAR2(100 BYTE),
4 "CITY" VARCHAR2(100 BYTE),
5 PRIMARY KEY ( "ID" ) USING INDEX ENABLE
6 );
Table created.
SQL>
Now, Let's create your procedure
SQL> CREATE OR REPLACE PROCEDURE INSERT_TABLE (
2 COLUMN_NAME IN VARCHAR2,
3 DAT IN VARCHAR2
4 ) AS
5
6 TABLE_NAME VARCHAR2(100) := COLUMN_NAME || '_' || DAT;
7 SQL_CREATE VARCHAR2(100) := 'create table ' || TABLE_NAME || '(FIRSTNAME varchar2(100))';
8 C_EMP SYS_REFCURSOR; -- declaration of cursor
9 LV_FNAME EMPLOYEES.FIRSTNAME%TYPE; -- to store the each value from cursor
10 BEGIN
11 EXECUTE IMMEDIATE SQL_CREATE;
12 OPEN C_EMP FOR 'SELECT FIRSTNAME
13 FROM EMPLOYEES
14 WHERE ' || COLUMN_NAME || ' = ''' || DAT || '''';
15 -- above statement used dynamic query in cursor
16 LOOP
17 FETCH C_EMP INTO LV_FNAME;
18 EXIT WHEN C_EMP%NOTFOUND;
19 EXECUTE IMMEDIATE 'INSERT INTO ' || TABLE_NAME || ' VALUES (''' || LV_FNAME || ''')'; -- added VALUES keyword in INSERT statement.
20 END LOOP;
21
22 COMMIT;
23 END INSERT_TABLE;
24 /
Let's execute it and see the result now.
SQL>
SQL> EXECUTE INSERT_TABLE('CITY', 'London');
PL/SQL procedure successfully completed.
SQL>
SQL>
SQL> SELECT * FROM CITY_LONDON;
FIRSTNAME
--------------------------------------------------------------------------------
TEJASH
SQL>
Ohh Yes, It created the desired table and also data is populated correctly.
Cheers!!
You are missing quotes. This
sql_str:='INSERT INTO '||table_name||'('||r_reg.firstname||');';
should be
sql_str:='INSERT INTO '||table_name||'('''||r_reg.firstname||''');';
This will turn INSERT INTO CITY_London (Peter); into INSERT INTO CITY_London ('Peter');.
And according to the docs (e.g. https://docs.oracle.com/cd/B19306_01/appdev.102/b14261/dynamic.htm):
When constructing a single SQL statement in a dynamic string, do not include a semicolon (;) at the end inside the quotation mark.
So the two SQL strings for EXECUTE IMMEDIATE should be:
sql_create varchar2(100) := 'create table ' || table_name || '(FIRSTNAME varchar2(100))';
and
sql_str := 'INSERT INTO ' || table_name || '(''' || r_reg.firstname || ''')';
It is the answer for your last question. You can pass parameters to the cursor like there:
declare
CURSOR c_emp(p_param varchar2) is
select p_param val
from dual;
begin
for r_reg in c_emp('b')
loop
dbms_output.put_line(r_reg.val);
end loop;
end;

How to insert multiple row result of dynamic sql to another Table?

I write one dynamic SQL which the result of it is a table with 2 columns and multiple rows, I want to insert it to another table with 4 columns that 2 of them will be filled by the result of dynamic SQL, I try to use collection but don't know how to insert result to another table
CREATE OR REPLACE PROCEDURE P_C_SM_Failure_error_Code_P2P AS
v_month VARCHAR2(16); -- to get Month for each table
v_day VARCHAR2(16); -- to get day for each table
v_ERRCODE t_c_rpt_resultmsg.code%TYPE;
v_ERRMSG t_c_rpt_resultmsg.MESSAGE%TYPE;
v_param VARCHAR2(16);
v_sql VARCHAR2(3000);
v_result number;
type t_c_result is record (Err_code varchar2(2000), Err_count number);
type v_t_result is table of t_c_result index by PLS_INTEGER;
v_t1_result v_t_result;
BEGIN
v_sql :='0';
v_param := 'Gateway_G';
v_result := '0';
select to_char(sysdate - 1,'MM') into v_month from dual;
select to_char(sysdate - 1,'DD') into v_day from dual;
-- Get count of P2P
v_sql := '(select count(*), error_code from (
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC01 where
orgaccount = '''||v_param||''' and destaccount = '''||v_param||''' and
sm_status <> 1 union all
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC02 where
orgaccount = '''||v_param||''' and destaccount = '''||v_param||''' and
sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql bulk collect into v_t1_result;
--insert into t_c_rpt_result2 values (trunc(sysdate, 'DD'), v_errcount,
v_err_code,'Failure_error_Code_P2P');
--for indx in 1 .. v_t1_result.COUNT
--loop
--dbms_output.put_line (v_t1_result (indx).Err_code);
--end loop;
You may append the constant values of date and the error message to the subquery and run a dynamic insert. It should also work if you remove the outer parentheses of your dynamic sql since constants can be included in group by. Always remember to pass values as bind variables rather than concatenating them (v_param). Also, specify the column names explicitly in an INSERT statement.
v_sql := '(select count(*) as cnt, error_code
from (
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC01
where orgaccount = :x and destaccount = :x and sm_status <> 1
union all
select error_code from sm_histable'||v_month||''||v_day||'#ORASMSC02
where orgaccount = :x and destaccount = :x and sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql bulk collect into v_t1_result using v_param;
EXECUTE IMMEDIATE 'insert into t_c_rpt_result2(err_dt,err_msg,errcount,error_code)
select :dt,:msg,cnt,error_code from '|| v_sql
USING trunc(sysdate, 'DD'),'Failure_error_Code_P2P',v_param;
I think you are looking at an excellent use case for FORALL. The collection you are populating needs to be done with execute immediate since you are dynamically constructing the table name. But the insert into t_c_rpt_result2 looks static to me.
BEGIN
v_sql :=
'(select count(*) as cnt, error_code
from (
select error_code from sm_histable'
|| v_month
|| ''
|| v_day
|| '#ORASMSC01
where orgaccount = :x and destaccount = :x and sm_status <> 1
union all
select error_code from sm_histable'
|| v_month
|| ''
|| v_day
|| '#ORASMSC02
where orgaccount = :x and destaccount = :x and sm_status <> 1 )
group by error_code)';
EXECUTE IMMEDIATE v_sql BULK COLLECT INTO v_t1_result USING v_param;
FORALL indx IN 1 .. v_t1_result.COUNT
INSERT INTO t_c_rpt_result2 (err_dt,
err_msg,
errcount,
ERROR_CODE)
VALUES (TRUNC (SYSDATE, 'DD'),
'Failure_error_Code_P2P',
v_t1_result (indx).cnt,
v_t1_result (indx).ERROR_CODE);
END;
Find more examples of FORALL on LiveSQL here. Of course, even if your insert was dynamic, you can use FORALL - put the execute immediate directly "inside" the FORALL statement. But I don't think that complexity is justified here.
Hope that helps!

store multiple records into variable without using cursor

I have a table structure like as :
desc temp_table ;
Name Null? Type
student_name VARCHAR2(500)
Is_delete NUMBER(2)
using pl sql procedure I'm trying to store all values of above table into a variable.
If table has more then one row then I'm getting error like
ORA-01422: exact fetch returns more than requested number of rows
So without using cursor how can we store multiple row's in a variable??
Define two collection types:
CREATE OR REPLACE TYPE StringList IS TABLE OF VARCHAR2(4000);
/
CREATE OR REPLACE TYPE BooleanList IS TABLE OF NUMBER(1,0);
/
Use BULK COLLECT INTO:
CREATE OR REPLACE PROCEDURE UPDATE_TABLE
AS
Names StringList;
Del BooleanList;
BEGIN
DBMS_OUTPUT.ENABLE(1000000);
SELECT student_name,
Is_delete
BULK COLLECT INTO
Names,
Del
FROM temp_table
WHERE MODIFIED_DATE >= TRUNC( SYSDATE )
AND MODIFIED_DATE < TRUNC( SYSDATE ) + INTERVAL '1' DAY;
DBMS_OUTPUT.PUT_LINE ('Number Of Names: ' || Names.COUNT );
DBMS_OUTPUT.PUT_LINE ('Number Of Del: ' || Del.COUNT ); -- Will always be the same amount
FOR i IN 1 .. Names.COUNT LOOP
DBMS_OUTPUT.PUT_LINE ( Names(i) || ', ' || Del(i) );
END LOOP;
END;
/

Oracle PL/SQL function

I have a function for confluence 52 columns
create or replace FUNCTION get_one_row(i_code IN integer) RETURN CLOB IS
l_columns VARCHAR2(2000);
l_res CLOB;
BEGIN
SELECT listagg(column_name,' || ') WITHIN GROUP(ORDER BY column_name ASC) AS GRAFIK
INTO l_columns
FROM user_tab_columns
WHERE TABLE_NAME = 'GRAFIK';
EXECUTE IMMEDIATE 'SELECT '||l_columns||' FROM grafik WHERE kod_sotr=:A' INTO l_res USING i_code;
RETURN l_res;
END;
The table Grafik has kod of worker, year and weeks, where
designated their holidays letter y or o
On a exit function displays
2017109909уууууооооо
First, meaning in conclutions is very fused, and not comfortable browse their. How devided the meaning?
You could edit your dynamic SQL to add a separator between the columns; for example:
SELECT listagg(column_name,' || '', '' || ') WITHIN GROUP(ORDER BY column_name ASC) AS GRAFIK
will add a comma between the values of the columns

Resources