How can i use column_name dynamically in pl/sql - sql

---- Hey guys, kinldy help me in this code , i want to give column_name dynamically in dbms_output, but its giving me error ------
--set serveroutput on;
declare
type t_list_rec is record
(table_name all_tab_columns.table_name%type,
column_name all_tab_columns.COLUMN_NAME%type);
type t_list is table of t_list_rec index by PLS_INTEGER;
v_array t_list;
type e_list is table of CUSTOMER_ADDRESS%rowtype;
t_array e_list:=e_list();
begin
select /*+ parallel(14) */ table_name,column_name bulk collect into v_array from all_tab_columns where table_name='CUSTOMER_ADDRESS' and OWNER='MIG';
for k in 1..v_array.count() loop
t_array.extend;
EXECUTE IMMEDIATE
'select /*+ parallel (16) */ * from '||v_array(k).table_name||' where not regexp_like ('||v_array(k).column_name||',''[A-za-z0-9.]'')'
into t_array(k);
dbms_output.put_line(t_array(k).v_array(k).column_name);
end loop;
end;
---Error----
Error report -
ORA-06550: line 17, column 37:
PLS-00302: component 'V_ARRAY' must be declared
ORA-06550: line 17, column 5:
PL/SQL: Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:

Not quite sure what you want, but you can get something like it using SYS_REFCURSOR.
Example procedure:
create or replace Procedure Desc_Tab_Content(p_tab IN VarChar2, p_owner IN VarChar2) IS
BEGIN
DECLARE
type t_list_rec is record (table_name all_tab_columns.table_name%type,
column_name all_tab_columns.COLUMN_NAME%type);
type t_list is table of t_list_rec index by PLS_INTEGER;
v_array t_list;
--
mReport SYS_REFCURSOR;
mSql VarChar2(512);
mCount Number(6);
mCountDist Number(6);
mHead VarChar2(64);
mHeadLen Number(3);
mDataLen Number(3);
BEGIN
Select table_name, column_name
bulk collect into v_array
From all_tab_columns
Where table_name = p_tab and owner = p_owner;
--
mHead := 'Table ' || v_array(1).table_name || ' - Column''s (DistVals/TotalVals)';
mHeadLen := Length(mHead);
dbms_output.put_line(mHead);
dbms_output.put_line(RPAD('-', mHeadLen, '-'));
--
For k in 1..v_array.count() Loop
mSql := 'Select Count(DISTINCT ' || v_array(k).column_name || '), Count(*) From ' || v_array(k).table_name;
OPEN mReport FOR mSql;
FETCH mReport Into mCount, mCountDist;
mDataLen := Length(k || '. ' || v_array(k).column_name);
dbms_output.put_line(k || '. ' || v_array(k).column_name || LPAD(' (' || mCount || '/' || mCountDist || ')', mHeadLen - mDataLen, ' '));
End Loop;
END;
END Desc_Tab_Content;
In the procedure you define what you want to do with your data. Here is the list of columns with number of distinct values (per column) out of total records in the given table.
... call & result:
set serveroutput on
BEGIN
Desc_Tab_Content(your_tbl_name, your_owner_name);
END;
/* R e s u l t:
anonymous block completed
Table ATBL - Column's (DistVals/TotalVals)
-------------------------------------------
1. ADATE (1/3)
2. ANAME (3/3)
3. ID (3/3)
*/

Related

PL/SQL Dynamic SQL : Table name not valid

I'm currently learning PL/SQL. I need to create a PL/SQL block to create a backup of all my tables like this : myTable -> myTable_old.
Here's what I got right now :
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_old';
sql_slc VARCHAR2(200);
sql_drp VARCHAR2(200);
sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
sql_drp := 'DROP TABLE :1 CASCADE';
sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_old';
dbms_output.put_line(t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0 THEN
dbms_output.put_line(t_backup_name || ' dropped');
EXECUTE IMMEDIATE sql_drp USING t_backup_name;
END IF;
dbms_output.put_line(t_backup_name || ' created');
EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
/
Here's the error :
OUVRAGE OUVRAGE_old
OUVRAGE_old created
DECLARE
*
ERROR on line 1 :
ORA-00903: table name not valid
ORA-06512: on line 29
I don't understand why this error is coming up, can someone help me ?
The issue is that you can not use bind variables for table names; Oracle documentation:
The database uses the values of bind variables exclusively and does
not interpret their contents in any way.
You should edit your code to use concatenation instead:
DECLARE
Cursor c IS SELECT table_name
FROM user_tables
WHERE table_name NOT LIKE '%_OLD'; /* OLD, upper case */
sql_slc VARCHAR2(200);
--sql_drp VARCHAR2(200);
--sql_crt VARCHAR2(200);
row_count NUMBER;
t_name user_tables.table_name%type;
t_backup_name user_tables.table_name%type;
BEGIN
-- sql_drp := 'DROP TABLE :1 CASCADE';
-- sql_crt := 'CREATE TABLE :1 AS SELECT * FROM :2';
sql_slc := 'SELECT COUNT(*) FROM user_tables WHERE table_name = :1';
OPEN c;
LOOP
FETCH c INTO t_name;
EXIT WHEN (c%NOTFOUND);
t_backup_name := t_name || '_OLD'; /* OLD, upper case */
DBMS_OUTPUT.put_line (t_name || ' ' || t_backup_name);
EXECUTE IMMEDIATE sql_slc INTO row_count USING t_backup_name;
IF row_count > 0
THEN
DBMS_OUTPUT.put_line (t_backup_name || ' dropped');
-- EXECUTE IMMEDIATE sql_drp USING t_backup_name;
EXECUTE IMMEDIATE ' drop table ' || t_backup_name; /* concatenation and not bind variables */
END IF;
DBMS_OUTPUT.put_line (t_backup_name || ' created'); /* concatenation and not bind variables */
-- EXECUTE IMMEDIATE sql_crt USING t_backup_name, t_name;
EXECUTE IMMEDIATE 'create table ' || t_backup_name || ' as select * from ' || t_name;
COMMIT;
END LOOP;
CLOSE c;
END;
Also, notice that, if not double quoted, object names always are uppercase, so you have to look for t_name || '_OLD' and not t_name || '_old'

Oracle PL/SQL How to store and fetch a dynamic multi column query

I am trying hard dynamic PL/SQL thing here.
I don't manage to fetch a column dynamic Query.
I am iterating on the name of the column to concatenate a full query in order to be executed on another table.
sql_req := 'select ';
for c in (SELECT name_col from TAB_LISTCOL)
loop
sql_req := sql_req || 'sum(' || c.name_col || '),';
end loop;
sql_req := sql_req || ' from ANOTHER_TAB ';
And when i try to execute it with EXECUTE IMMEDIATE or cursors or INTO/BULK COLLECT thing or just to fetch, i don't manage to iterate on the result.
I tried a lot.
Can you help me plz ? Or maybe it is not possible ?
ps : i know the coma is wrong but my code is more complexe than this : i didn't want to put more things
If you only want to get string columns, you can use listagg
select listagg(name_col, ',') WITHIN GROUP (ORDER BY null) from TAB_LISTCOL
Please see if this helps
In the absence of actual table structure and requirement, I'm creating dummy tables and query to illustrate an example:
SQL> create table another_tab
as
select 10 dummy_value1, 100 dummy_value2, 1000 dummy_value3 from dual union all
select 11 dummy_value1, 101 dummy_value2, 1001 dummy_value3 from dual union all
select 12 dummy_value1, 102 dummy_value2, 1003 dummy_value3 from dual
;
Table created.
SQL> create table tab_listcol
as select column_name from dba_tab_cols where table_name = 'ANOTHER_TAB'
;
Table created.
To reduce complexity in the final block, I'm defining a function to generate the dynamic sql query. This is based on your example and will need changes according to your actual requirement.
SQL> create or replace function gen_col_based_query
return varchar2
as
l_query varchar2(4000);
begin
l_query := 'select ';
for cols in ( select column_name cname from tab_listcol )
loop
l_query := l_query || 'sum(' || cols.cname || '), ' ;
end loop;
l_query := rtrim(l_query,', ') || ' from another_tab';
return l_query;
end;
/
Function created.
Sample output from the function will be as follows
SQL> select gen_col_based_query as query from dual;
QUERY
--------------------------------------------------------------------------------
select sum(DUMMY_VALUE1), sum(DUMMY_VALUE2), sum(DUMMY_VALUE3) from another_tab
Below is a sample block for executing a dynamic cursor using DBMS_SQL. For your ease of understanding, I've added comments wherever possible. More info here.
SQL> set serveroutput on size unlimited
SQL> declare
sql_stmt clob;
src_cur sys_refcursor;
curid number;
desctab dbms_sql.desc_tab; -- collection type
colcnt number;
namevar varchar2 (50);
numvar number;
datevar date;
l_header varchar2 (4000);
l_out_rows varchar2 (4000);
begin
/* Generate dynamic sql from the function defined earlier */
select gen_col_based_query into sql_stmt from dual;
/* Open cursor variable for this dynamic sql */
open src_cur for sql_stmt;
/* To fetch the data, however, you cannot use the cursor variable, since the number of elements fetched is unknown at complile time.
Therefore you use DBMS_SQL.TO_CURSOR_NUMBER to convert a REF CURSOR variable to a SQL cursor number which you can then pass to DBMS_SQL subprograms
*/
curid := dbms_sql.to_cursor_number (src_cur);
/* Use DBMS_SQL.DESCRIBE_COLUMNS to describe columns of your dynamic cursor, returning information about each column in an associative array of records viz., desctab. The no. of columns is returned in colcnt variable.
*/
dbms_sql.describe_columns (curid, colcnt, desctab);
/* Define columns at runtime based on the data type (number, date or varchar2 - you may add to the list)
*/
for indx in 1 .. colcnt
loop
if desctab (indx).col_type = 2 -- number data type
then
dbms_sql.define_column (curid, indx, numvar);
elsif desctab (indx).col_type = 12 -- date data type
then
dbms_sql.define_column (curid, indx, datevar);
else -- assuming string
dbms_sql.define_column (curid, indx, namevar, 100);
end if;
end loop;
/* Print header row */
for i in 1 .. desctab.count loop
l_header := l_header || ' | ' || rpad(desctab(i).col_name,20);
end loop;
l_header := l_header || ' | ' ;
dbms_output.put_line(l_header);
/* Loop to retrieve each row of data identified by the dynamic cursor and print output rows
*/
while dbms_sql.fetch_rows (curid) > 0
loop
for indx in 1 .. colcnt
loop
if (desctab (indx).col_type = 2) -- number data type
then
dbms_sql.column_value (curid, indx, numvar);
l_out_rows := l_out_rows || ' | ' || rpad(numvar,20);
elsif (desctab (indx).col_type = 12) -- date data type
then
dbms_sql.column_value (curid, indx, datevar);
l_out_rows := l_out_rows || ' | ' || rpad(datevar,20);
elsif (desctab (indx).col_type = 1) -- varchar2 data type
then
dbms_sql.column_value (curid, indx, namevar);
l_out_rows := l_out_rows || ' | ' || rpad(namevar,20);
end if;
end loop;
l_out_rows := l_out_rows || ' | ' ;
dbms_output.put_line(l_out_rows);
end loop;
dbms_sql.close_cursor (curid);
end;
/
PL/SQL procedure successfully completed.
Output
| SUM(DUMMY_VALUE1) | SUM(DUMMY_VALUE2) | SUM(DUMMY_VALUE3) |
| 33 | 303 | 3004 |
You have to use EXECUTE IMMEDIATE with BULK COLLECT
Below is an example of the same. For more information refer this link
DECLARE
TYPE name_salary_rt IS RECORD (
name VARCHAR2 (1000),
salary NUMBER
);
TYPE name_salary_aat IS TABLE OF name_salary_rt
INDEX BY PLS_INTEGER;
l_employees name_salary_aat;
BEGIN
EXECUTE IMMEDIATE
q'[select first_name || ' ' || last_name, salary
from hr.employees
order by salary desc]'
BULK COLLECT INTO l_employees;
FOR indx IN 1 .. l_employees.COUNT
LOOP
DBMS_OUTPUT.put_line (l_employees (indx).name);
END LOOP;
END;
If I understand correctly, you want to create a query and execute it and return the result to another function or some calling app. As the resulting query's columns are note before-known, I'd return a ref cursor in this case:
create function get_sums return sys_refcur as
declare
my_cursor sys_refcursor;
v_query varchar2(32757);
begin
select
'select ' ||
listagg('sum(' || name_col || ')', ', ') within group (order by name_col) ||
' from another_tab'
into v_query
from tab_listcol;
open my_cursor for v_query;
return v_query;
end get_sums;

Query for particular integer value from multiple columns with number datatype

I try to search a number from multiple columns (datatype number), but get ORA-01722: invalid number error.
My Query:
SELECT *
FROM CAMPAIGN
WHERE 1481125 IN (select column_name
from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type = 'NUMBER');
What is wrong with it?
You are comparing your number 1481125 with the names of the each column, not the values of each column in your table.
To go from a column's name (from dba_tab_columns) to the values in that column, you need to use some form of dynamic SQL. Here's a relatively simple example:
DECLARE
-- Since I don't have your CAMPAIGN table or data, I'm using DBA_OBJECTS in it's place.
l_table_name VARCHAR2 (30) := 'DBA_OBJECTS';
l_search_number NUMBER := 20; -- 1481125 in your example
l_record dba_objects%ROWTYPE;
l_sql VARCHAR2 (32000);
l_column_number NUMBER := 0;
l_cur SYS_REFCURSOR;
BEGIN
-- First: build dynamic SQL statement of the form:
-- SELECT * FROM table_name WHERE
-- ( ( col_name_a = 20 ) OR ( col_name_b = 20 ) OR ... )
l_sql := 'SELECT * FROM dba_objects WHERE ( ';
FOR r_number_column IN (SELECT column_name
FROM dba_tab_columns
WHERE table_name = l_table_name
AND data_type = 'NUMBER'
ORDER BY column_id) LOOP
IF l_column_number > 0 THEN
l_sql := l_sql || ' OR ';
END IF;
l_column_number := l_column_number + 1;
l_sql := l_sql || '(' || r_number_column.column_name || ' = ' || l_search_number || ')';
END LOOP;
IF l_column_number = 0 THEN
-- No number columns in table, so there should be no matches
l_sql := l_sql || ' 1=0';
END IF;
l_sql := l_sql || ')';
DBMS_OUTPUT.put_line (l_sql);
OPEN l_cur FOR l_sql;
LOOP
FETCH l_cur INTO l_record;
EXIT WHEN l_cur%NOTFOUND;
DBMS_OUTPUT.put_line ('Object Name ' || l_record.object_name || ' has search number ' || l_search_number);
END LOOP;
END;
Your query is:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
(select column_name from all_tab_columns where table_name = 'CAMPAIGN' AND data_type='NUMBER')
Breaking that down we have:
SELECT * FROM CAMPAIGN WHERE 1481125 IN (<a set of numbers>)
and the subquery:
select column_name from all_tab_columns
where table_name = 'CAMPAIGN'
AND data_type='NUMBER'
That subquery is going to return a list of column names e.g.
CAMPAIGN_COUNT
CAMPAIGN_ID
CAMPAIGN_NUMBER_OF_SOMETHINGS
Your query is thus equivalent to:
SELECT * FROM CAMPAIGN WHERE 1481125 IN
('CAMPAIGN_COUNT', 'CAMPAIGN_ID', 'CAMPAIGN_NUMBER_OF_SOMETHINGS')
You can see why you would get the ORA-01722 error there?
You would need to write dynamic SQL to achieve your aim.

Oracle procedure cursor loop errors

Can you tell me what is wrong with this procedure that I'm trying to create?
CREATE OR REPLACE PROCEDURE create_audit_tables (table_owner VARCHAR2)
IS
CURSOR c_tables (
table_owner VARCHAR2)
IS
SELECT ot.owner AS owner, ot.table_name AS table_name
FROM all_tables ot
WHERE ot.owner = table_owner
AND ot.table_name NOT LIKE 'AUDIT_%'
AND ot.table_name <> 'EXAUDIT'
AND NOT EXISTS
(SELECT 1
FROM EXAUDIT efa
WHERE ot.table_name = efa.tname)
AND NOT EXISTS
(SELECT 1
FROM all_tables at
WHERE at.table_name = 'AUDIT_'||ot.table_name);
v_sql VARCHAR2 (8000);
v_count NUMBER := 0;
v_aud VARCHAR2 (30);
BEGIN
FOR r_table IN c_tables (table_owner)
LOOP
BEGIN
v_aud := 'AUDIT_'||r_table.table_name;
v_sql :=
'create table '
|| v_aud
|| ' as select * from '
|| r_table.owner
|| '.'
|| r_table.table_name
|| ' where 1 = 1';
DBMS_OUTPUT.put_line ('Info: ' || v_sql);
EXECUTE IMMEDIATE v_sql;
v_sql :=
'alter table '
|| v_aud
|| ' add ( AUDIT_ACTION char(1), AUDIT_BY varchar2(50), AUDIT_AT TIMESTAMP)';
EXECUTE IMMEDIATE v_sql;
v_count := c_tables%ROWCOUNT;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Failed to create table '
|| v_aud
|| ' due to '
|| SQLERRM);
END;
END LOOP;
IF v_count = 0
THEN
DBMS_OUTPUT.put_line ('No audit tables created');
ELSE
DBMS_OUTPUT.put_line (v_count || ' audit tables created.');
END IF;
END;
/
I checked it with show errors function and it gave me this:
LINE/COL ERROR
-------- --------------------------------------------
6/1 PL/SQL: SQL Statement ignored
16/3 PL/SQL: ORA-00906: missing left parenthesis
Perhaps the problem is quite simple, but I can't see it, so please help me if you can
I can compile procedure without problems after creating table
create table EXAUDIT (tname varchar2(100));
Check status of procedure:
select status from user_objects where lower(object_name) = 'create_audit_tables';

procedure to delete duplicate records using dynamic sql

i have successfully implemented a proc which can delete delete duplicate records from a table .
Carrying on i am further trying to generalize this procedure using Dynamic sql But i get the following error when compiling itself .
ERROR line 13, col 7, ending_line 14, ending_col 14, Found ''SELECT ROWID, ERR_MOD
FROM '', Expecting: ( SELECT -or- WITH
Here is my code :-
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
)
IS
v_cnt NUMBER;
CURSOR TMP
IS
'SELECT ROWID, ERR_MOD
FROM ' || p_tblname||
'WHERE '|| p_cname '='||''p_cvalue '';
BEGIN
BEGIN
'SELECT COUNT ( * )
INTO v_cnt
FROM ' || p_tblname||
'WHERE '|| p_cname' = '||''p_cvalue'';
EXCEPTION
WHEN OTHERS
THEN
p_out:=1;
end;
FOR REC IN tmp
LOOP
IF v_cnt = 1
THEN
EXIT;
ELSE
'DELETE FROM '|| p_tblname||
'WHERE ROWID = REC.ROWID';
END IF;
v_cnt := v_cnt - 1;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
p_out := 2;
END;
/
Hi you cannot create a dynamic query for normal cursors in such cases you should use ref cursors and follow the the steps below
--create a query string
--open the ref cursor
and please use execute immediate for execution of delete statements...
CREATE OR REPLACE PROCEDURE BIOCON.proc_dedup (
p_tblname IN VARCHAR2,
p_cname IN varchar2,
p_cvalue IN varchar2,
p_out OUT VARCHAR2:=0
IS
v_cnt NUMBER;
v_string varchar2(200);
TYPE ref_cur IS REF CURSOR
ref_cursor_name ref_cur;
BEGIN
v_string:='SELECT ROWID, ERR_MOD FROM ' || p_tblname|| 'WHERE '|| p_cname
'='||''p_cvalue '';
OPEN cursor_variable_name FOR v_string;
.....
.....
EXECUTE IMMEDIATE 'DELETE FROM '|| p_tblname|| 'WHERE ROWID = REC.ROWID';--you need execute immediate for this
.....
.....