error signaled in parallel query server P000 ORA-01722: invalid number - sql

I am getting the error:
error signaled in parallel query server P000
ORA-01722: invalid number
while executing the code
There is a cursor that fetches the values from the table
/* VALIDATE MANDATORY CFA FIELDS START */
CURSOR c_cfa_fields IS
select storage_col_name,
view_col_name
from cfa_attrib
where group_id = 150
and value_req = 'Y'
order by display_seq;
---
TYPE cfa_fields_tbl IS TABLE OF c_cfa_fields%ROWTYPE;
cfa_fields_rec cfa_fields_tbl;
/* VALIDATE MANDATORY CFA FIELDS END */
I then fetch the cursor:
OPEN c_cfa_fields;
FETCH c_cfa_fields BULK COLLECT INTO cfa_fields_rec;
CLOSE c_cfa_fields;
---
IF cfa_fields_rec.COUNT > 0 THEN -- Skip if no mandatory CFA Field exists for the GROUP_ID
FOR i IN 1 .. cfa_fields_rec.COUNT
LOOP
L_val_query := 'INSERT /*+ APPEND NOLOGGING PARALLEL */ INTO dmf_val_error_log' || CHR(10) ||
'SELECT /*+ PARALLEL (s,50) */ ' || CHR(10) ||
' ''' || I_TABLE_NAME || ''', -- error_table ' || CHR(10) ||
' ''STORE='' || s.store || ''; GROUP_ID='' || s.group_id, -- pk_value' || CHR(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || ''', -- error_column' || CHR(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || '='' || ' ||
'NVL(s.'|| cfa_fields_rec(i).storage_col_name || ',''(null)''), -- error_value' || CHR(10) ||
' ''' || cfa_fields_rec(i).view_col_name || ' (' ||
cfa_fields_rec(i).storage_col_name || ') cannot be NULL'', -- error_desc' || CHR(10) ||
' SYSDATE -- error_datetime' || CHR(10) ||
' FROM ' || I_TABLE_NAME || ' s' || CHR(10) ||
' WHERE ' || cfa_fields_rec(i).storage_col_name || ' IS NULL';
---
EXECUTE IMMEDIATE L_val_query;
One thing I have noticed:
If the Datatype of storage_col_name is NUMBER,
it gives the error.
It works fine for VARCHAR2 data type for storage_col_name
STORAGE_COL_NAME VIEW_COL_NAME
VARCHAR2_1 LATITUDE
VARCHAR2_2 LONGITUDE
VARCHAR2_3 IS_CROSS_DOCK
VARCHAR2_4 CLEARANCE_STORE
VARCHAR2_5 CLIMATE
VARCHAR2_6 DEMOGRAPHY
NUMBER_11 DEFAULT_WH

When errors occur within dynamically generated code, it's worth checking what is actually being generated and testing it.
Test to reproduce your situation:
declare
cursor c_cfa_fields is
with cfa_attrib (storage_col_name, view_col_name) as
( select 'VARCHAR2_1', 'LATITUDE' from dual union all
select 'VARCHAR2_2', 'LONGITUDE' from dual union all
select 'VARCHAR2_3', 'IS_CROSS_DOCK' from dual union all
select 'VARCHAR2_4', 'CLEARANCE_STORE' from dual union all
select 'VARCHAR2_5', 'CLIMATE' from dual union all
select 'VARCHAR2_6', 'DEMOGRAPHY' from dual union all
select 'NUMBER_11', 'DEFAULT_WH' from dual )
select storage_col_name, view_col_name
from cfa_attrib;
type cfa_fields_tbl is table of c_cfa_fields%rowtype;
cfa_fields_rec cfa_fields_tbl;
l_val_query long;
i_table_name varchar2(30) := 'SAMPLE_TABLE_NAME';
begin
open c_cfa_fields;
fetch c_cfa_fields bulk collect into cfa_fields_rec;
close c_cfa_fields;
if cfa_fields_rec.count > 0 then -- skip if no mandatory cfa field exists for the group_id
for i in 1 .. cfa_fields_rec.count loop
l_val_query := 'INSERT /*+ APPEND PARALLEL */ INTO dmf_val_error_log' || chr(10) ||
'SELECT /*+ PARALLEL (s,50) */ ' || chr(10) ||
' ''' || i_table_name || ''', -- error_table ' || chr(10) ||
' ''STORE='' || s.store || ''; GROUP_ID='' || s.group_id, -- pk_value' || chr(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || ''', -- error_column' || chr(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || '='' || ' ||
'NVL(s.'|| cfa_fields_rec(i).storage_col_name || ',''(null)''), -- error_value' || chr(10) ||
' ''' || cfa_fields_rec(i).view_col_name || ' (' ||
cfa_fields_rec(i).storage_col_name || ') cannot be NULL'', -- error_desc' || chr(10) ||
' SYSDATE -- error_datetime' || chr(10) ||
'FROM ' || i_table_name || ' s' || chr(10) ||
'WHERE ' || cfa_fields_rec(i).storage_col_name || ' IS NULL';
dbms_output.put_line(l_val_query);
dbms_output.new_line();
end loop;
end if;
end;
I've used a with clause to generate your sample data, rather than reading an actual table, and I just print the generated INSERTs rather than executing them (I don't know what your dmf_val_error_log looks like, or the table you are dynamically querying in the generated code).
(I took out the word NOLOGGING from your hint. If you want dmf_val_error_log to be nologging, then use alter table dmf_val_error_log nologging. There's no hint for it.)
The output is a series of INSERTs like this:
INSERT /*+ APPEND PARALLEL */ INTO dmf_val_error_log
SELECT /*+ PARALLEL (s,50) */
'SAMPLE_TABLE_NAME', -- error_table
'STORE=' || s.store || '; GROUP_ID=' || s.group_id, -- pk_value
'VARCHAR2_1', -- error_column
'VARCHAR2_1=' || NVL(s.VARCHAR2_1,'(null)'), -- error_value
'LATITUDE (VARCHAR2_1) cannot be NULL', -- error_desc
SYSDATE -- error_datetime
FROM SAMPLE_TABLE_NAME s
WHERE VARCHAR2_1 IS NULL
Substituting dummy data via a WITH clause, you can test how that will behave with different datatypes.
If the VARCHAR2_1 column is a string, it works:
with sample_table_name(store, group_id, varchar2_1) as
( select 'London', 123, cast(null as varchar2(1)) from dual )
select 'SAMPLE_TABLE_NAME', -- error_table
'STORE=' || s.store || '; GROUP_ID=' || s.group_id, -- pk_value
'VARCHAR2_1', -- error_column
'VARCHAR2_1=' || nvl(s.varchar2_1,'(null)'), -- error_value
'LATITUDE (VARCHAR2_1) cannot be NULL', -- error_desc
sysdate -- error_datetime
from sample_table_name s
where varchar2_1 is null;
If it's a number, it fails:
with sample_table_name(store, group_id, varchar2_1) as
( select 'London', 123, cast(null as number) from dual )
select 'SAMPLE_TABLE_NAME', -- error_table
'STORE=' || s.store || '; GROUP_ID=' || s.group_id, -- pk_value
'VARCHAR2_1', -- error_column
'VARCHAR2_1=' || nvl(s.varchar2_1,'(null)'), -- error_value
'LATITUDE (VARCHAR2_1) cannot be NULL', -- error_desc
sysdate -- error_datetime
from sample_table_name s
where varchar2_1 is null;
ERROR at line 6:
ORA-01722: invalid number
Essentially, it is attempting something like this:
select nvl(1, '(null)')
from dual;
when it would need to be:
select nvl(to_char(1), '(null)')
from dual;
Applying that back to your code, it should be
l_val_query := 'INSERT /*+ APPEND PARALLEL */ INTO dmf_val_error_log' || chr(10) ||
'SELECT /*+ PARALLEL (s,50) */ ' || chr(10) ||
' ''' || i_table_name || ''', -- error_table ' || chr(10) ||
' ''STORE='' || s.store || ''; GROUP_ID='' || s.group_id, -- pk_value' || chr(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || ''', -- error_column' || chr(10) ||
' ''' || cfa_fields_rec(i).storage_col_name || '='' || ' ||
'NVL(to_char(s.'|| cfa_fields_rec(i).storage_col_name || '),''(null)''), -- error_value' || chr(10) ||
' ''' || cfa_fields_rec(i).view_col_name || ' (' ||
cfa_fields_rec(i).storage_col_name || ') cannot be NULL'', -- error_desc' || chr(10) ||
' SYSDATE -- error_datetime' || chr(10) ||
'FROM ' || i_table_name || ' s' || chr(10) ||
'WHERE ' || cfa_fields_rec(i).storage_col_name || ' IS NULL';
which generates INSERT statements like this:
INSERT /*+ APPEND PARALLEL */ INTO dmf_val_error_log
SELECT /*+ PARALLEL (s,50) */
'SAMPLE_TABLE_NAME', -- error_table
'STORE=' || s.store || '; GROUP_ID=' || s.group_id, -- pk_value
'VARCHAR2_1', -- error_column
'VARCHAR2_1=' || NVL(to_char(s.VARCHAR2_1),'(null)'), -- error_value
'LATITUDE (VARCHAR2_1) cannot be NULL', -- error_desc
SYSDATE -- error_datetime
FROM SAMPLE_TABLE_NAME s
WHERE VARCHAR2_1 IS NULL

Related

oracle: display results of dynamic sql based on antoher sql

I would like to display results of dynamic sql based on antoher sql, but get error message. My code:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF all_tab_columns%rowtype;
results_tbl results;
BEGIN
FOR i IN (
SELECT
*
FROM
all_tab_columns
WHERE
owner = 'OWNER_XYZ'
AND upper(column_name) LIKE '%COLUMN_XYZ%'
ORDER BY
table_name,
column_name
) LOOP
sql_qry := ' SELECT DISTINCT '
|| i.column_name
|| ' as column_name '
|| ' FROM '
|| i.owner
|| '.'
|| i.table_name
|| ' WHERE SUBSTR('
|| i.column_name
|| ',1,1) = ''Y''';
EXECUTE IMMEDIATE sql_qry BULK COLLECT
INTO results_tbl;
dbms_output.put_line(results_tbl);
END LOOP;
END;
I get the error message:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
In fact I need the results of all queries with an union between them like that
[1] [1]: https://i.stack.imgur.com/llxzr.png
Your dynamic query is selecting a single column, but you are bulk collecting into results_tbl, which is of type results, based on all_tab_columns%rowtype - which has lots of columns.
If you define your collection type as a single column of the data type you need, i.e. some length of string:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF varchar2(4000);
results_tbl results;
...
You will then bulk collect a single column into that single-column collection. To display the results you need to loop over the collection:
FOR j IN 1..results_tbl.COUNT loop
dbms_output.put_line(results_tbl(j));
END LOOP;
so the whole block becomes:
DECLARE
sql_qry VARCHAR2(1000) := NULL;
TYPE results IS
TABLE OF varchar2(4000);
results_tbl results;
BEGIN
FOR i IN (
SELECT
*
FROM
all_tab_columns
WHERE
owner = 'OWNER_XYZ'
AND upper(column_name) LIKE '%COLUMN_XYZ%'
ORDER BY
table_name,
column_name
) LOOP
sql_qry := ' SELECT DISTINCT '
|| i.column_name
|| ' as column_name '
|| ' FROM '
|| i.owner
|| '.'
|| i.table_name
|| ' WHERE SUBSTR('
|| i.column_name
|| ',1,1) = ''Y''';
EXECUTE IMMEDIATE sql_qry BULK COLLECT
INTO results_tbl;
FOR j IN 1..results_tbl.COUNT loop
dbms_output.put_line(results_tbl(j));
END LOOP;
END LOOP;
END;
/
However, you can also do this without PL/SQL, using a variation on an XML trick. You can get the results of the dynamic query as an XML document using something like:
select dbms_xmlgen.getxmltype(
'select distinct "' || column_name || '" as value'
|| ' from "' || owner || '"."' || table_name || '"'
|| ' where substr("' || column_name || '", 1, 1) = ''Y'''
)
from all_tab_columns
where owner = 'OWNER_XYZ'
and upper(column_name) like '%COLUMN_XYZ%';
and then extract the value you want from that:
with cte (xml) as (
select dbms_xmlgen.getxmltype(
'select distinct "' || column_name || '" as value'
|| ' from "' || owner || '"."' || table_name || '"'
|| ' where substr("' || column_name || '", 1, 1) = ''Y'''
)
from all_tab_columns
where owner = 'OWNER_XYZ'
and upper(column_name) like '%COLUMN_XYZ%'
)
select x.value
from cte
cross apply xmltable(
'/ROWSET/ROW'
passing cte.xml
columns value varchar2(4000) path 'VALUE'
) x;
You can also easily include the table each value came from if you want that information (and the owner, and actual column name, etc.).
db<>fiddle showing both approaches.

How to quickly see what columns in a table have data?

We are currently undertaking a testing phase which requires us to see if there is any data in each column for each table. Now, the route that is long and labour-intensive is:
SELECT COUNT(Col1), COUNT(Col2)...FROM TABLE
Is there any easier way to do this? We can go down this route by concatenating each column name from our data lineage document with the COUNT() function, but we have a lot of tables and a lot of columns in each table, making this a bit unfeasible.
Essentially we just need a count of records in each column for each table, without having to write long COUNT(Col) queries.
Thanks
This query will return accurate results if the table statistics were recently gathered with the default value for ESTIMATE_PERCENT:
SELECT utab.table_name
, tcol.column_name
, utab.num_rows
from user_tables utab,
user_tab_cols tcol
where utab.table_name = tcol.table_name
and utab.num_rows > 0
and utab.num_rows = tcol.num_nulls;
You could use a dynamic query to build the queries. This will generate all the queries.
SELECT 'SELECT COUNT(' || t.column_name || ' ) FROM ' || t.owner || '.' || t.table_name || ';' FROM dba_tab_columns t
You can generate all the select statements like so:
SELECT CASE WHEN column_id = 1 AND column_id_desc != 1 THEN 'SELECT ''' || LOWER(owner) || '.' || LOWER(table_name) || ''' table_name, ' || CHR(10) || 'COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt,'
WHEN column_id = 1 AND column_id_desc = 1 THEN 'SELECT ''' || LOWER(owner) || '.' || LOWER(table_name) || ''' table_name, ' || CHR(10) || 'COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt FROM ' || LOWER(owner) || '.' || LOWER(table_name) || ';'
WHEN column_id_desc = 1 THEN ' COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt' || CHR(10) || 'FROM ' || LOWER(owner) || '.' || LOWER(table_name) || ';'
ELSE ' COUNT(' || LOWER(column_name) || ') ' || SUBSTR(LOWER(column_name), 1, 26) || '_cnt,'
END sql_text
FROM (SELECT owner,
table_name,
column_name,
column_id,
row_number() OVER (PARTITION BY owner, table_name ORDER BY column_id DESC) column_id_desc
FROM all_tab_columns)
WHERE <predicates to filter on the tables you're interested in>
ORDER BY owner,
table_name,
column_id;
This goes through all the tables you're interested in plus their columns and outputs text that will, when taken together, form a select statement for each table.
The text that is output in the sql_text column depends on whether the column in the list is the first or last (or both!); this way you get the full statement which queries each table once, rather than one per table and column.
You can then copy and paste the results and run that as a script.
It's can help you
SELECT
a.table_name,
a.column_name
FROM
ALL_TAB_COLUMNS a
WHERE owner = '<your user>'
AND a.SAMPLE_SIZE = a.NUM_NULLS

SQL - Combining information with similar primary key/ID

I know this question was asked before but the solution given for that question did not work for me. I couldn't write a comment stating my problem as I needed 50 reputation.
Details:
Oracle version 10g
Solution Provided:
select book_id,
'Total Hours of Loan: ' || to_char(sum(hours_of_loan)) || chr(10) ||
lpad('-', 30, '-') || chr(10) ||
xmlagg(xmlelement(x, descr, chr(10) || lpad('-', 30, '-') || chr(10)).extract('//text()')
order by date_of_loan) as book_description
from (
select book_id, date_of_loan, hours_of_loan,
'Written by: ' || checked_by || chr(10) ||
'Date of Loan : ' || to_char(date_of_loan, 'mm/dd/yy') || chr(10) ||
'Hour(s) of Loan: ' || to_char(hours_of_loan) || chr(10) ||
'Comments: ' || comments || chr(10) ||
'Student: ' || student as descr
from student_book
)
group by book_id
order by book_id
;
I tried creating a dummy table with fake data to test out the logic to see if it works for me and it does. But when I used the query for my real data, it prompted out an error that says:
ORA-22813: operand value exceeds system limits
ERROR prompted
After reading about it, I found that it was because my data was too large.
I tried creating table and a query to call from it
DECLARE
sSUM_TEXT CLOB;
sBOOK_ID VARCHAR2(10):= '1';
nCount NUMBER:=1;
BEGIN
FOR rRECORDRec IN(SELECT BOOK_ID, CHECKED_BY, DATE_OF_lOAN, HOURS_OF_LOAN, COMMENTS, STUDENT
FROM STUDENT_BOOK
WHERE BOOK_ID = '1')
LOOP
IF nCount != 1 THEN
sSUM_TEXT := sSUM_TEXT||chr(10)||
'Written by: ' || rRECORDRec.checked_by || chr(10) ||
'Date of Loan : ' || rRECORDRec.date_of_loan || chr(10) ||
'Hour(s) of Loan: ' || rRECORDRec.hours_of_loan || chr(10) ||
'Comments: ' || rRECORDRec.comments || chr(10) ||
'Student: ' || rRECORDRec.student || chr(10) ||
'--------------------------------';
ELSE
sSUM_TEXT := 'Written by: ' || rRECORDRec.checked_by || chr(10) ||
'Date of Loan : ' || rRECORDRec.date_of_loan || chr(10) ||
'Hour(s) of Loan: ' || rRECORDRec.hours_of_loan || chr(10) ||
'Comments: ' || rRECORDRec.comments || chr(10) ||
'Student: ' || rRECORDRec.student || chr(10) ||
'--------------------------------';
END IF;
nCount := nCount + 1;
END LOOP;
INSERT INTO COMMENTS_SUMMARY(BOOK_ID,SUM_TEXT)
VALUES (sBOOK_ID,sSUM_TEXT);
COMMIT;
END;
This did not really solve my problem but it combined all the "COMMENTS" which had that specific BOOK_ID but without format
Tried Solution
select book_id,
'Total Hours of Loan: ' || to_char(sum(hours_of_loan)) || chr(10) ||
lpad('-', 30, '-') || chr(10) ||
xmlagg(xmlelement(x, descr, chr(10) || lpad('-', 30, '-') || chr(10)).extract('//text()')
order by date_of_loan) as book_description
from (
select book_id, date_of_loan, hours_of_loan,
to_clob('Written by: ' || checked_by || chr(10) ||
'Date of Loan : ' || date_of_loan || chr(10) ||
'Hour(s) of Loan: ' || hours_of_loan || chr(10) ||
'Comments: ' || comments || chr(10) ||
'Student: ' || student) as descr
from student_book
)
group by book_id
order by book_id;
The same error prompted out
2nd SELECT Statement
I tried running the query separately. I ran the 2nd SELECT Statement for my real data and no errors were prompted even without the TO_CLOB
select book_id, date_of_loan, hours_of_loan,
'Written by: ' || checked_by || chr(10) ||
'Date of Loan : ' || date_of_loan || chr(10) ||
'Hour(s) of Loan: ' || hours_of_loan || chr(10) ||
'Comments: ' || comments || chr(10) ||
'Student: ' || student as descr
from student_book
order by book_id
)
--group by book_id
order by book_id;
My Question:
How may I overcome this problem and group the data base on their ID?
There are probably at least a few books for which you have too many loans, so the string built in XMLAGG is longer than 4000 characters. So you have to deal with CLOBs.
You need to add .getclobval() here:
... order by date_of_loan).getclobval() as book_description
It is possible that even the text for one "loan" is already too long (more than 4000 characters). This is a problem for concatenation - if all the inputs are less than 4000 characters Oracle treats them as VARCHAR2 and expects the result to also be VARCHAR2 (no more than 4000 characters). To "force" it to treat everything as CLOBs, it suffices to make the first input a CLOB.
Instead of 'Written by: ' (find it in the code) use
... to_clob('Written by: ')

How do i copy old columns to my new existing table?

I have an existing new table that has no columns yet. I want to copy all 10 columns in my old table. How do i do that? I don't want to drop the table so I could perform: create table newTable as select * from oldTable.
I don't want to drop the table so I could perform:
create table newTable as select * from oldTable.
Assuming your new table has at least 1 column BobC told you cannot have a table without any column, you can alter your table.
Alter new_table add (col1 varchar2(10)) , col2 ...);
Note that you need to mention all the column same as old table manually here.
If you don't want to do this manually then probably you would need a PLSQL block to do this.
CREATE OR REPLACE PROCEDURE creat_tbl_frm_tbl (tablname VARCHAR2)
AS
db_user VARCHAR2 (100) := USER;
TYPE t_rec IS RECORD
(
COLUMN_NAME VARCHAR2 (30 BYTE),
DATA_TYPE VARCHAR2 (106 BYTE),
DATA_LENGTH NUMBER
);
TYPE t_record IS TABLE OF t_rec;
-- initialization of record
v_record t_record := t_record ();
v_cntr NUMBER := 0;
v_status NUMBER;
sql_stmt VARCHAR2 (2000);
BEGIN
---dropping Already Existing temporary table
BEGIN
SELECT 1
INTO v_status
FROM all_objects
WHERE UPPER (object_name) = UPPER (tablname) AND owner = db_user;
--dbms_output.put_line( v_status );
IF v_status = 1
THEN
sql_stmt := 'drop table ' || tablname || '_new';
EXECUTE IMMEDIATE sql_stmt;
END IF;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
DBMS_OUTPUT.put_line ('Table not found--' || tablname);
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while dropping table-' || SQLCODE || SQLERRM);
END;
---------------------------------------------------------------------------
---- retrieving the columns of the table
BEGIN
SELECT column_name, data_type, data_length
BULK COLLECT INTO v_record
FROM all_tab_columns
WHERE table_name = UPPER (tablname)
ORDER BY column_id;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error while retrieving the column details-' || SQLCODE || SQLERRM);
END;
FOR i IN 1 .. v_record.COUNT
LOOP
v_cntr := v_cntr + 1;
IF v_cntr = 1
THEN
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'create table '
|| ' '
|| tablname
|| '_new'
|| '('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
ELSE
IF v_record (i).data_type = 'DATE'
OR UPPER (v_record (i).data_type) = 'TIMESTAMP(6)'
THEN
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| ')';
ELSE
sql_stmt :=
'alter table'
|| ' '
|| tablname
|| '_new'
|| ' '
|| 'add ('
|| v_record (i).column_name
|| ' '
|| v_record (i).data_type
|| '('
|| v_record (i).data_length
|| '))';
END IF;
--dbms_output.put_line(sql_stmt);
EXECUTE IMMEDIATE sql_stmt;
END IF;
END LOOP;
DBMS_OUTPUT.put_line ('Process Completed Successfully..!!');
END;
Output:
SQL> exec creat_tbl_frm_tbl('table2')
Process Completed Successfully..!!
PL/SQL procedure successfully completed.
SQL> select * from table2;
no rows selected
-- This is created with same columns as of passed table table2
SQL> select * from table2_new;
no rows selected
SELECT col1,col2,col3 --...
INTO newTable
FROM oldTable

Runtime Query in Oracle store procedures not returning proper result

This is first my stint with procedure and I am trying to execute below oracle procedure but facing some issue. Any inputs on this would be really helpful:
Issue:-----
I have a select query that returns two values:
src_Columns contains:
ID_ELEMENT
ID_ELEMENT_SA
Now, When I am trying to travesre a select-query(single column) result using "For Loop", I am not getting the values of the column rather I am getting its name only.
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
LOOP
dbms_output.put_line('src_Columns 3: ' || columnItem.src_Columns);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || columnItem.dummyValue || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
I mean the test query generated is following when i use variable name(src_columns):
insert into ATT_WTPART(NAME,STRINGVALUE) VALUES( 'ID_ELEMENT_SA','ID_ELEMENT_SA')
whereas if I use ID_ELEMENT_SA instead of src_Columns in FOR LOOP
FOR columnItem IN (SELECT ID_ELEMENT FROM ELEM90_LNK_ELEM_BOSE)
then I get proper values that are desired like
insert into ATT_WTPART(NAME,STRINGVALUE) VALUES( 'ID_ELEMENT_SA','ID05')
How can I make sure that I get the values even when I am using the variable name instead of any hard-coding
Below is the Complete Procedure:-------------
create or replace
PROCEDURE ELEM90_Lnk_Elem_ATT_WTPART_MK
AS
CURSOR targ_dest_relation IS
SELECT sourcecolumn FROM mapping where destinationtable='ATT_WTPART';
BEGIN
DECLARE
dest_Columns varchar2(1000);
src_Columns varchar2(1000);
src_Type varchar2(1000);
destination_Table varchar2(1000) := 'ATT_WTPART';
source_Table varchar2(1000) := 'ELEM90_LNK_ELEM_BOSE';
query_test varchar2(1000);
query_test2 varchar2(1000);
src_name varchar2(255);
src_Type_Value varchar2(255);
dest_Columns_Value varchar2(255);
src_name_Value varchar2(255);
for_query varchar2(1000);
for_query_data varchar2(1000);
dummyValue varchar2(1000);
BEGIN
FOR rec IN targ_dest_relation loop
dbms_output.put_line('destination_Table: ' || destination_Table);
dbms_output.put_line('source_Table: ' || source_Table);
src_Columns := rec.sourcecolumn;
dbms_output.put_line('src_Columns: ' || src_Columns);
src_Type := 'select data_type from user_tab_columns where table_name ='''||source_Table||'''and column_name='''|| src_Columns ||'''';
dbms_output.put_line('src_Type: ' || src_Type);
execute immediate src_Type INTO src_Type_Value;
dbms_output.put_line('src_Type_Value: ' || src_Type_Value);
dest_Columns := 'select DEST_COLUMN from ATT_PART_MAPPING where SOURCETYPE='''|| src_Type_Value || '''';
dbms_output.put_line('dest_Columns: ' || dest_Columns);
execute immediate dest_Columns INTO dest_Columns_Value;
dbms_output.put_line('dest_Columns_Value: ' || dest_Columns_Value);
src_name := 'select column_name from user_tab_columns where table_name ='''|| source_Table ||''' and column_name= ''' || src_Columns || '''';
dbms_output.put_line('src_name: ' || src_name);
execute immediate src_name INTO src_name_Value;
dbms_output.put_line('src_name_Value: ' || src_name_Value);
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
LOOP
dbms_output.put_line('src_Columns 3: ' || columnItem.src_Columns);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || columnItem.dummyValue || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
END loop;
END;
END;
The problem with the line
FOR columnItem IN (SELECT src_Columns FROM ELEM90_LNK_ELEM_BOSE)
is that src_Columns is a local variable. As a result, you end up selecting the same value for each row in ELEM90_LNK_ELEM_BOSE.
If you want the value of the local variable to be used as a column name in the query, you must use dynamic SQL instead. Try replacing FOR columnItem ... LOOP ... END LOOP with the following:
OPEN curs FOR 'SELECT ' || src_Columns || ' FROM ELEM90_LNK_ELEM_BOSE';
LOOP
FETCH curs INTO column_value;
EXIT WHEN curs%NOTFOUND;
dbms_output.put_line('src_Columns 3: ' || column_value);
query_test:= 'insert into ' || destination_Table || '(NAME,' || dest_Columns_Value || ') VALUES( ''' || src_name_Value || ''',''' || column_value || ''')';
dbms_output.put_line('query_test:' || query_test);
execute immediate query_test;
END LOOP;
CLOSE curs;
You will need to declare the following extra variables:
curs SYS_REFCURSOR;
column_value VARCHAR2(4000);
However, in truth it would probably be better to remove the loop entirely. You can replace it with an INSERT INTO ... SELECT ... FROM ... statement instead, such as the following:
EXECUTE IMMEDIATE 'INSERT INTO ' || destination_Table || ' (NAME,' ||
dest_Columns_Value || ') SELECT :name,' || src_Columns ||
' FROM ELEM90_LNK_ELEM_BOSE' USING src_name_Value;
This also gets rid of the need for the two local variables curs and column_value, and is also likely to be considerably faster, as there's no need to parse dynamic SQL once for each row in the destination table.
Is it working better if you try this one:
query_test:= 'insert into ' || destination_Table ||
'(NAME,'||dest_Columns_Value||') VALUES (:p1, :p2)';
execute immediate query_test USING src_name_Value, columnItem.dummyValue;
At least it should have a positive impact on performance.