PL/SQL can you INSERT INTO (SELECT GROUP BY)? - sql

I am tasked with creating an Oracle PL/SQL procedure "summarizer" that creates a summary table (mean, median etc. for each column) from a table.
The original table (mix of numeric and categorical columns):
column_1 | column_2| column_3| column_4
---------------------------------------
The desired output:
table_name | column_name | mean | tally
---------------------------------------
To do this, I am attempting to use a cursor that iterates over the USER_TAB_COLUMNS table (contains table_name, column_name) to INSERT the aggregations for each column_name INTO the SUMMARY_TABLE.
Here is my progress so far:
CREATE OR REPLACE PROCEDURE summarizer
IS
CURSOR column_cursor IS
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'BANK';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE SUMMARY_TABLE';
EXECUTE IMMEDIATE 'CREATE TABLE SUMMARY_TABLE (
table_name VARCHAR(20),
column_name VARCHAR(20),
data_type VARCHAR(20),
mean NUMBER,
tally NUMBER,
PRIMARY KEY (table_name, column_name))';
FOR colname IN column_cursor LOOP
INSERT INTO SUMMARY_TABLE
SELECT
colname.table_name AS table_name,
colname.column_name AS column_name,
colname.data_type AS data_type,
mean(colname.column_name) AS mean,
count(colname.column_name) AS tally
FROM BANK
GROUP BY table_name, column_name, data_type;
END LOOP;
COMMIT;
END summarizer;
This produces the error:
Error report -
20/12 PL/SQL: SQL Statement ignored
28/51 PL/SQL: ORA-00904: "DATA_TYPE": invalid identifier
I am unable to locate the cause of this error. I believe it could be something to do with a GROUP BY in the INSERT INTO statement. Can anyone help?

You need dynamic SQL to use variables in a query string.
Consider:
FOR colname IN column_cursor LOOP
EXECUTE IMMEDIATE
'INSERT INTO SUMMARY_TABLE
SELECT
''' || colname.table_name || ''' AS table_name,
''' || colname.column_name || ''' AS column_name,
''' || colname.data_type || ''' AS data_type,
avg(' || colname.column_name || ') AS mean,
count(' || colname.column_name || ') AS tally
FROM BANK';
END LOOP;
Side notes:
the constant values (table name, column name and datatype) need to be enclosed in quotes
there is no mean() aggregate function in Oracle (nor in most other RDBMS): you can use avg() instead
you don't actually need a group by clause in your insert ... select ... query, since the non-aggregated columns are constants
Demo on DB Fiddle:
-- create the table and insert a few records
create table bank(id int, val int);
begin
insert into bank values(1, 1);
insert into bank values(2, 1);
insert into bank values(3, 1);
end;
/
-- create the summary table (it must exist since the procedure drops it)
create table summary_table(id int);
-- create the procedure
CREATE OR REPLACE PROCEDURE summarizer
IS
CURSOR column_cursor IS
SELECT TABLE_NAME, COLUMN_NAME, DATA_TYPE
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = 'BANK';
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE SUMMARY_TABLE';
EXECUTE IMMEDIATE 'CREATE TABLE SUMMARY_TABLE (
table_name VARCHAR(20),
column_name VARCHAR(20),
data_type VARCHAR(20),
mean NUMBER,
tally NUMBER,
PRIMARY KEY (table_name, column_name))';
FOR colname IN column_cursor LOOP
EXECUTE IMMEDIATE
'INSERT INTO SUMMARY_TABLE
SELECT
''' || colname.table_name || ''' AS table_name,
''' || colname.column_name || ''' AS column_name,
''' || colname.data_type || ''' AS data_type,
avg(' || colname.column_name || ') AS mean,
count(' || colname.column_name || ') AS tally
FROM BANK';
END LOOP;
COMMIT;
END summarizer;
/
-- run the procedure
begin
summarizer;
end;
/
-- check the results
select * from summary_table
TABLE_NAME | COLUMN_NAME | DATA_TYPE | MEAN | TALLY
:--------- | :---------- | :-------- | ---: | ----:
BANK | ID | NUMBER | 2 | 3
BANK | VAL | NUMBER | 1 | 3

Related

How to perform a dynamic select query for multiple tables in oracle? using table name and column names as values from other table?

i have a table having columns such schema name,table name,column name.
schema name
table name
column name
dbo
product
colour
dbo
sales
quantity
dbo
customer
order
i want to perform an action such that to get a count of records based on column as column name and table as table name
select count(colour) as count from dbo.product
select count(quantity) as count from dbo.sales
select count(order) as count from dbo.customer
can u suggest me correct steps to achieve this using Oracle database. thanks in advance
expected output
count
5
50
150
If you need to get count per column per table, you can do this with plain SQL using DBMS_XMLGEN package, which essentially executes new cursors dynamically.
I think you can adapt the example query below to suit your needs (aggregate counts or convert them to another format).
with a as (
select
'all_tables' as table_name, 'table_name' as column_name
from dual
union all
select 'all_tables', 'tablespace_name' from dual union all
select 'all_tab_cols', 'column_name' from dual union all
select 'all_indexes', 'index_name' from dual union all
select 'all_indexes', 'tablespace_name' from dual
)
select
table_name,
column_name,
cast(extractvalue(
dbms_xmlgen.getxmltype(
'select count(' || column_name || ') as cnt' || chr(10) ||
'from ' || table_name
),
'/ROWSET/ROW/CNT'
) as int) as cnt
from a
TABLE_NAME | COLUMN_NAME | CNT
:----------- | :-------------- | ----:
all_tables | table_name | 71
all_tables | tablespace_name | 43
all_tab_cols | column_name | 20983
all_indexes | index_name | 81
all_indexes | tablespace_name | 73
db<>fiddle here
You cannot do this in pure sql, but you can pretty easily do this in pl/sql. In the code below I'm storing the results in a table called rowcounts and it assumes your table with tables and column names is called
CREATE table mytables (schema_name VARCHAR2(100), table_name, VARCHAR2(100), column_name VARCHAR2(100));
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','product','colour');
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','sales','quantity');
INSERT INTO mytables (schema_name, table_name, column_name) VALUES ('dbo','customer','order');
CREATE TABLE rowcounts (table_name VARCHAR2(500), rowcount NUMBER);
DECLARE
l_rowcount INT;
BEGIN
FOR r IN (SELECT * FROM mytables) LOOP
EXECUTE IMMEDIATE 'SELECT COUNT('||r.column_name||') FROM '||r.table_name INTO l_rowcount;
--dbms_output.put_line('SELECT COUNT('||r.column_name||') FROM '||r.table_name);
INSERT INTO rowcounts(table_name, rowcount) VALUES (r.table_name, l_rowcount);
END LOOP;
COMMIT;
END;
/
--DROP TABLE rowcounts;

Oracle get table names based on column value

I have table like this:
Table-1
Table-2
Table-3
Table-4
Table-5
each table is having many columns and one of the column name is employee_id.
Now, I want to write a query which will
1) return all the tables which is having this columns and
2) results should show the tables if the column is having values or empty values by passing employee_id.
e.g. show table name, column name from Table-1, Table-2,Table-3,... where employee_id='1234'.
If one of the table doesn't have this column, then it is not required to show.
I have verified with link, but it shows only table name and column name and not by passing some column values to it.
Also verified this, but here verifies from entire schema which I dont want to do it.
UPDATE:
Found a solution, but by using xmlsequence which is deprecated,
1)how do I make this code as xmltable?
2) If there are no values in the table, then output should have empty/null. or default as "YES" value
WITH char_cols AS
(SELECT /*+materialize */ table_name, column_name
FROM cols
WHERE data_type IN ('CHAR', 'VARCHAR2') and table_name in ('Table-1','Table-2','Table-3','Table-4','Table-5'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Employee_ID",
SUBSTR (table_name, 1, 14) "Table",
SUBSTR (column_name, 1, 14) "Column"
FROM char_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select "'
|| column_name
|| '" from "'
|| table_name
|| '" where upper("'
|| column_name
|| '") like upper(''%'
|| :val
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) t ORDER BY "Table"
/
This query can be done in one step using the (non-deprecated) XMLTABLE.
Sample Schema
--Table-1 and Table-2 match the criteria.
--Table-3 has the right column but not the right value.
--Table-4 does not have the right column.
create table "Table-1" as select '1234' employee_id from dual;
create table "Table-2" as select '1234' employee_id from dual;
create table "Table-3" as select '4321' employee_id from dual;
create table "Table-4" as select 1 id from dual;
Query
--All tables with the column EMPLOYEE_ID, and the number of rows where EMPLOYEE_ID = '1234'.
select table_name, total
from
(
--Get XML results of dynamic query on relevant tables and columns.
select
dbms_xmlgen.getXMLType(
(
--Create a SELECT statement on each table, UNION ALL'ed together.
select listagg(
'select '''||table_name||''' table_name, count(*) total
from "'||table_name||'" where employee_id = ''1234'''
,' union all'||chr(10)) within group (order by table_name) v_sql
from user_tab_columns
where column_name = 'EMPLOYEE_ID'
)
) xml
from dual
) x
cross join
--Convert the XML data to relational.
xmltable('/ROWSET/ROW'
passing x.xml
columns
table_name varchar2(128) path 'TABLE_NAME',
total number path 'TOTAL'
);
Results
TABLE_NAME TOTAL
---------- -----
Table-1 1
Table-2 1
Table-3 0
Just try to use code below.
Pay your attention that may be nessecery clarify scheme name in loop.
This code works for my local db.
set serveroutput on;
DECLARE
ex_query VARCHAR(300);
num NUMBER;
emp_id number;
BEGIN
emp_id := <put your value>;
FOR rec IN
(SELECT table_name
FROM all_tab_columns
WHERE column_name LIKE upper('employee_id')
)
LOOP
num :=0;
ex_query := 'select count(*) from ' || rec.table_name || ' where employee_id = ' || emp_id;
EXECUTE IMMEDIATE ex_query into num;
if (num>0) then
DBMS_OUTPUT.PUT_LINE(rec.table_name);
end if;
END LOOP;
END;
I tried with the xml thing, but I get an error I cannot solve. Something about a zero size result. How difficult is it to solve this instead of raising exception?! Ask Oracle.
Anyway.
What you can do is use the COLS table to know what table has the employee_id column.
1) what table from table TABLE_LIKE_THIS (I assume column with table names is C) has this column?
select *
from COLS, TABLE_LIKE_THIS t
where cols.table_name = t
and cols.column_name = 'EMPLOYEE_ID'
-- think Oracle metadata/ think upper case
2) Which one has the value you are looking for: write a little chunk of Dynamic PL/SQL with EXECUTE IMMEDIATE to count the tables matching above condition
declare
v_id varchar2(10) := 'JP1829'; -- value you are looking for
v_col varchar2(20) := 'EMPLOYEE_ID'; -- column
n_c number := 0;
begin
for x in (
select table_name
from all_tab_columns cols
, TABLE_LIKE_THIS t
where cols.table_name = t.c
and cols.column_name = v_col
) loop
EXECUTE IMMEDIATE
'select count(1) from '||x.table_name
||' where Nvl('||v_col||', ''##'') = ''' ||v_id||'''' -- adding quotes around string is a little specific
INTO n_c;
if n_c > 0 then
dbms_output.put_line(n_C|| ' in ' ||x.table_name||' has '||v_col||'='||v_id);
end if;
-- idem for null values
-- ... ||' where '||v_col||' is null '
-- or
-- ... ||' where Nvl('||v_col||', ''##'') = ''##'' '
end loop;
dbms_output.put_line('done.');
end;
/
Hope this helps

Count the number of null values into an Oracle table?

I need to count the number of null values of all the columns in a table in Oracle.
For instance, I execute the following statements to create a table TEST and insert data.
CREATE TABLE TEST
( A VARCHAR2(20 BYTE),
B VARCHAR2(20 BYTE),
C VARCHAR2(20 BYTE)
);
Insert into TEST (A) values ('a');
Insert into TEST (B) values ('b');
Insert into TEST (C) values ('c');
Now, I write the following code to compute the number of null values in the table TEST:
declare
cnt number :=0;
temp number :=0;
begin
for r in ( select column_name, data_type
from user_tab_columns
where table_name = upper('test')
order by column_id )
loop
if r.data_type <> 'NOT NULL' then
select count(*) into temp FROM TEST where r.column_name IS NULL;
cnt := cnt + temp;
END IF;
end loop;
dbms_output.put_line('Total: '||cnt);
end;
/
It returns 0, when the expected value is 6.
Where is the error?
Thanks in advance.
Counting NULLs for each column
In order to count NULL values for all columns of a table T you could run
SELECT COUNT(*) - COUNT(col1) col1_nulls
, COUNT(*) - COUNT(col2) col2_nulls
,..
, COUNT(*) - COUNT(colN) colN_nulls
, COUNT(*) total_rows
FROM T
/
Where col1, col2, .., colN should be replaced with actual names of columns of T table.
Aggregate functions -like COUNT()- ignore NULL values, so COUNT(*) - COUNT(col) will give you how many nulls for each column.
Summarize all NULLs of a table
If you want to know how many fields are NULL, I mean every NULL of every record you can
WITH d as (
SELECT COUNT(*) - COUNT(col1) col1_nulls
, COUNT(*) - COUNT(col2) col2_nulls
,..
, COUNT(*) - COUNT(colN) colN_nulls
, COUNT(*) total_rows
FROM T
) SELECT col1_nulls + col1_nulls +..+ colN_null
FROM d
/
Summarize all NULLs of a table (using Oracle dictionary tables)
Following is an improvement in which you need to now nothing but table name and it is very easy to code a function based on it
DECLARE
T VARCHAR2(64) := '<YOUR TABLE NAME>';
expr VARCHAR2(32767);
q INTEGER;
BEGIN
SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || LISTAGG('COUNT(' || COLUMN_NAME || ')', ' + ') WITHIN GROUP (ORDER BY COLUMN_ID) || ' FROM ' || T
INTO expr
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = T;
-- This line is for debugging purposes only
DBMS_OUTPUT.PUT_LINE(expr);
EXECUTE IMMEDIATE expr INTO q;
DBMS_OUTPUT.PUT_LINE(q);
END;
/
Due to calculation implies a full table scan, code produced in expr variable was optimized for parallel running.
User defined function null_fields
Function version, also includes an optional parameter to be able to run on other schemas.
CREATE OR REPLACE FUNCTION null_fields(table_name IN VARCHAR2, owner IN VARCHAR2 DEFAULT USER)
RETURN INTEGER IS
T VARCHAR2(64) := UPPER(table_name);
o VARCHAR2(64) := UPPER(owner);
expr VARCHAR2(32767);
q INTEGER;
BEGIN
SELECT 'SELECT /*+FULL(T) PARALLEL(T)*/' || COUNT(*) || ' * COUNT(*) OVER () - ' || listagg('COUNT(' || column_name || ')', ' + ') WITHIN GROUP (ORDER BY column_id) || ' FROM ' || o || '.' || T || ' t'
INTO expr
FROM all_tab_columns
WHERE table_name = T;
EXECUTE IMMEDIATE expr INTO q;
RETURN q;
END;
/
-- Usage 1
SELECT null_fields('<your table name>') FROM dual
/
-- Usage 2
SELECT null_fields('<your table name>', '<table owner>') FROM dual
/
Thank you #Lord Peter :
The below PL/SQL script works
declare
cnt number :=0;
temp number :=0;
begin
for r in ( select column_name, nullable
from user_tab_columns
where table_name = upper('test')
order by column_id )
loop
if r.nullable = 'Y' then
EXECUTE IMMEDIATE 'SELECT count(*) FROM test where '|| r.column_name ||' IS NULL' into temp ;
cnt := cnt + temp;
END IF;
end loop;
dbms_output.put_line('Total: '||cnt);
end;
/
The table name test may be replaced the name of table of your interest.
I hope this solution is useful!
The dynamic SQL you execute (this is the string used in EXECUTE IMMEDIATE) should be
select sum(
decode(a,null,1,0)
+decode(b,null,1,0)
+decode(c,null,1,0)
) nullcols
from test;
Where each summand corresponds to a NOT NULL column.
Here only one table scan is necessary to get the result.
Use the data dictionary to find the number of NULL values almost instantly:
select sum(num_nulls) sum_num_nulls
from all_tab_columns
where owner = user
and table_name = 'TEST';
SUM_NUM_NULLS
-------------
6
The values will only be correct if optimizer statistics were gathered recently and if they were gathered with the default value for the sample size.
Those may seem like large caveats but it's worth becoming familiar with your database's statistics gathering process anyway. If your database is not automatically gathering statistics or if your database is not using the default sample size those are likely huge problems you need to be aware of.
To manually gather stats for a specific table a statement like this will work:
begin
dbms_stats.gather_table_stats(user, 'TEST');
end;
/
select COUNT(1) TOTAL from table where COLUMN is NULL;

plsql List all table.column containing null values

I'd like to find all column of a set of table with null values in them.
I can find the table and column names
SELECT TABLE_NAME, COLUMN_NAME
FROM user_tab_cols
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
And also check if the are nulls
select count(*) from myTable where myColumn is null;
but how can I put this toghether to have as result
table_name column_name
myTable myColumn
myTable myCol2
myTable2 col4
An approach could be with some dynamic SQL, but this would require some PL/SQL code; the following only uses SQL to get the result you need:
select *
from (
select table_name,
column_name,
to_number(extractvalue(xmltype(dbms_xmlgen.getxml('select count(*) c from '||table_name || ' where ' || column_name || ' is null')),'/ROWSET/ROW/C')) as rowcount
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2')
)
where rowcount > 0
This could be an approach with dynamic SQL:
declare
type tableOfNumber is table of number;
type tableOfChar is table of varchar2(30);
--
vSQl varchar2(4000);
vListNumbers tableOfNumber;
vListTables tableOfChar;
vListColumns tableOfChar;
begin
select listagg( 'select ''' ||
table_name || ''' as table_name, ''' ||
column_name || ''' as column_name, count(*) as rowCount from ' ||
table_name ||
' where ' ||
column_name ||
' is null having count(*) > 0' ,
' UNION ALL '
) within group ( order by table_name, column_name)
into vSQL
from user_tab_columns
where nullable='Y'
and table_name in ('myTalbe', 'myTable2');
--
dbms_output.put_line(vSQL);
/* whatever you may want to do with the query */
/* for example, fetch into some variables and print the result */
execute immediate vSQL
bulk collect into vListTables, vListColumns, vListNumbers;
--
if vListTables.count() > 0 then
for i in vListTables.first .. vListTables.last loop
dbms_output.put_line('Table ' || vListTables(i) ||
', column ' || vListColumns(i) ||
', number of nulls: ' || vListNumbers(i)
);
end loop;
end if;
end;
Here's a routine I wrote a while back to do exactly that. It will output the required DDL to make those columns that are nullable (but do not contain any nulls) not nullable.
https://connormcdonald.wordpress.com/2016/03/11/tightening-up-your-data-model/
It can do the task for a schema or a single table.

SQL Variable, How to do it?

I have this SQL:
DROP TABLE MISSINGTABLE;
CREATE TABLE MISSINGTABLE (
TABLE_NAME VARCHAR2 (70),
DESCRIPTION VARCHAR2 (1000)
)
CREATE OR REPLACE PROCEDURE MISSINGTABLES AS
BEGIN
INSERT INTO MISSINGTABLE
((((SELECT TABLE_NAME, 'Missing Table on PEKA_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'ASE_ERP_001')
MINUS
(SELECT TABLE_NAME, 'Missing Table on PEKA_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'PEKA_ERP_001'))
UNION
((SELECT TABLE_NAME, 'Missing Table on ASE_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'PEKA_ERP_001')
MINUS
(SELECT TABLE_NAME, 'Missing Table on ASE_ERP_001' Description FROM ALL_TABLES WHERE OWNER = 'ASE_ERP_001'))));
END;
So, how u can see, I'm creating a Table and then a Procedure, which fills the Table.
Now I want 2 Variables for these Arguments: 'PEKA_ERP_001' and 'ASE_ERP_001' (so I don't always need to write it manually, because this values changes a lot)
I tried this (included only the first part of above Statement):
DECLARE
S1 VARCHAR2(100) := 'ASE_ERP_001';
S2 VARCHAR2(100) := 'PEKA_ERP_001';
TableMissing VARCHAR(100) := 'Missing Table on ';
Apostrophe VARCHAR(10) := '''';
BEGIN
EXECUTE IMMEDIATE ('CREATE OR REPLACE PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, ' || Apostrophe || TableMissing || S2 || Apostrophe || ' Description FROM ALL_TAB_COLUMNS WHERE OWNER = ' || Apostrophe || S1 || Apostrophe || ')' || ' END;');
END;
It creates The Procedure, but the Procedure contains the "CREATE OR REPLACE PROCEDURE" itself and its showing me an error... (I cannot execute the Procedure)
Can anyone help me? How can I write the first SQL Statement at the Head which works, only with 2 Variables more, ASE_ERP_001 and PEKA_ERP_001 ?
EDIT:
Statement:
DECLARE
S1 VARCHAR2(100) := 'ASE_ERP_001';
S2 VARCHAR2(100) := 'PEKA_ERP_001';
TabelleFehlt VARCHAR(100) := 'Diese Tabelle fehlt ';
Hochkomma VARCHAR(10) := '''';
BEGIN
EXECUTE IMMEDIATE ('CREATE OR REPLACE PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, ' || Hochkomma || TabelleFehlt || S2 || Hochkomma || ' Beschreibung FROM ALL_TAB_COLUMNS WHERE OWNER = ' || Hochkomma || S1 || Hochkomma || ') END;');
END;
The Statement Above Creates a Procedure.
But it also shows me this:
ORA-06512: in Row 7
24344. 00000 - "success with compilation error"
*Cause: A sql/plsql compilation error occurred.
*Action: Return OCI_SUCCESS_WITH_INFO along with the error code
And The PROCEDURE Itselfs Contains this:
create or replace
PROCEDURE MISSINGTABLES AS BEGIN INSERT INTO MISSINGTABLE (SELECT TABLE_NAME, 'Diese Tabelle fehlt PEKA_ERP_001' Beschreibung FROM ALL_TAB_COLUMNS WHERE OWNER = 'ASE_ERP_001') END;
But it should not Contain "Create or Replace Procedure MISSINGTABLES" etc. only the INSERT STatement, I cannot execute the Procedure anyway..
even better would be to use the script from bpgergo, if it would go.
I hope I did not mix the arguments up, you should check them again
CREATE OR REPLACE PROCEDURE MISSINGTABLES (p_1 in varchar2, p_2 in varchar2)
AS
BEGIN
INSERT INTO MISSINGTABLE
((((SELECT TABLE_NAME, 'Missing Table on '||p_1 Description FROM ALL_TABLES WHERE OWNER = p_2)
MINUS
(SELECT TABLE_NAME, 'Missing Table on '||p_1 Description FROM ALL_TABLES WHERE OWNER = p_1))
UNION
((SELECT TABLE_NAME, 'Missing Table on '||p_2 Description FROM ALL_TABLES WHERE OWNER = p_1)
MINUS
(SELECT TABLE_NAME, 'Missing Table on '||p_2 Description FROM ALL_TABLES WHERE OWNER = p_2))));
END;
EDIT
you would call this like:
begin
MISSINGTABLES ('PEKA_ERP_001', 'ASE_ERP_001');
end;
The SQL that you are trying to execute immediate will be evaluated as:
CREATE OR REPLACE PROCEDURE MISSINGTABLES AS
BEGIN
INSERT INTO MISSINGTABLE
(SELECT TABLE_NAME, COLUMN_NAME, 'Missing Table on PEKA_ERP_001' Beschreibung
FROM ALL_TAB_COLUMNS WHERE OWNER = 'ASE_ERP_001')
END;
This probably isn't the logic that you actually want, but the immediate problem is that you are trying to populate a non-existant third column called Beschreibung instead of populating the second column, DESCRIPTION .
Might I suggest an improvement to your SELECT?
Here's a possible alternative:
SELECT
TABLE_NAME,
'Missing Table on'
|| CASE MAX(OWNER) WHEN 'PEKA_ERP_001' THEN 'ASE_ERP_001' ELSE 'PEKA_ERP_001' END
AS Description
FROM ALL_TABLES
WHERE OWNER IN ('PEKA_ERP_001', 'ASE_ERP_001')
GROUP BY TABLE_NAME
HAVING COUNT(*) = 1
This query returns only rows where a TABLE_NAME has just one OWNER. The owner that is missing the table is then shown to be as the other one of the two being tested.
Using parameters, the entire CREATE PROCEDURE statement might look like this:
CREATE OR REPLACE PROCEDURE MISSINGTABLES
(
owner1 IN varchar2,
owner2 IN varchar2
)
AS
BEGIN
INSERT INTO MISSINGTABLE
(
SELECT
TABLE_NAME,
'Missing Table on'
|| CASE MAX(OWNER) WHEN owner1 THEN owner2 ELSE owner1 END
AS Description
FROM ALL_TABLES
WHERE OWNER IN (owner1, owner2)
GROUP BY TABLE_NAME
HAVING COUNT(*) = 1
);
END;