Checking for the existence of a column in a view before searching for a value - sql

I am trying to search every view in the database for a specific value of something like '%THIS%'
I came up with this plsql code
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT name FROM user_dependencies where type = 'VIEW') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '|| t.name || ' Where Column_Name LIKE ''%THIS%'' '
INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( t.name ||' '||match_count );
END IF;
END LOOP;
END;
But when I try to run it, I get an invalid identifier error for the column name in the execute immeadiate query.
The problem to me is obvious that not every view has the Column_Name, but I can't figure out how I can check to see if the column exists before running the query as I loop through all of the views.
I was also able to use a slightly modified version of this to run through all of the tables, and while they do not all have that column, I did not run in to this issue.
Edit: I am including the plsql code that I was able to use to loop through the tables.
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_columns
where table_name LIKE 'THIS%' and data_type = 'VARCHAR2' AND column_name = 'Column_name') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name || ' Where Column_name LIKE ''%THIS%'' '
INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;

I'm assuming this is Oracle since you tagged pl/sql. You can use Oracle's metadata tables to see what columns a table/view has. Specifically USER_TAB_COLUMNS.
select count(*)
from user_tab_columns
where table_name = [name of view]
and column_name = [name of column]

This should query every view that has a column_name like '%START%'
declare
cursor getViews is
select table_name, column_name from user_tab_cols where table_name in
(select view_name from user_views)
and column_name like '%START%';
myResult number;
BEGIN
for i in getViews
LOOP
execute immediate 'select count(*) from '||i.table_name into myResult;
END LOOP;
END;

Related

Why does Execute Immediate throw this PLS-00201 error?

I was trying to run the following code and it failed in the execute immediate block. So, am I going wrong with the syntax?
DECLARE
l_data long;
emp_rec EMP%rowtype;
begin
select * INTO emp_rec from EMP A WHERE A.EMP_NO = '001322';
for x in ( select column_name, data_type
from user_tab_columns
where table_name = 'EMP' )
loop
execute immediate
'begin
:x := emp_rec.' || x.column_name || ';
end;' using OUT l_data;
dbms_output.put_line( x.column_name || ' = ' || l_data );
end loop;
end;
I get this error
PLS-00201: Identifier EMP_REC.EMP_NO must be declared
Your emp_rec variable is a local PL/SQL record. When you do this, even with a static field name reference:
execute immediate 'begin :x := emp_rec.emp_no; end;'
the dynamic SQL runs in a separate context to the block that calls it. You then run a new anonymous PL/SQL block within that context.
Any variables from your outer anonymous block, specifically emp_rec here, are out of scope to the dynamic SQL context. They just do not exist to the code that is trying to assign the value to :x.
You could possibly do something with dbms_sql to make this dynamic, but if you know the table columns it would be easier to do:
declare
l_data varchar2(4000); -- long is deprecated; how big does this really need to be?
emp_rec EMP%rowtype;
begin
select * INTO emp_rec from EMP A WHERE A.EMP_NO = '001322';
for x in (
select column_name, data_type
from user_tab_columns
where table_name = 'EMP'
)
loop
case x.column_name
when 'EMP_NO' then
l_data := emp_rec.emp_no;
-- when clauses for each column in your real table
when 'FIRST_NAME' then
l_data := emp_rec.first_name;
when 'LAST_NAME' then
l_data := emp_rec.last_name;
-- list other columns and assignments
-- else ...
end case;
dbms_output.put_line( x.column_name || ' = ' || l_data );
end loop;
end;
/
although as #APC pointed out, the loop is now a bit pointless, since you can just do:
declare
emp_rec EMP%rowtype;
begin
select * INTO emp_rec from EMP A WHERE A.EMP_NO = '001322';
dbms_output.put_line( 'EMP_NO = ' || emp_rec.emp_no );
dbms_output.put_line( 'FIRST_NAME = ' || emp_rec.first_anme );
dbms_output.put_line( 'LAST_NAME = ' || emp_rec.last_name );
-- ... any other columns you want to show
end;
/
The emp_rec in the EXECUTE IMMEDIATE statement exists in a different namespace from the emp_rec in the calling code.
Not sure exactly what you're trying to achieve but it might be something like this:
DECLARE
l_data long;
emp_rec EMP%rowtype;
begin
select * INTO emp_rec from EMP A WHERE A.EMP_NO = '001322';
for x in ( select column_name, data_type
from user_tab_columns
where table_name = 'EMP' )
loop
execute immediate
'declare
lrec EMP%rowtype;
begin
lrec := :emp_rec;
:x := lrec.' || x.column_name || ';
end;' using emp_rec, OUT l_data;
dbms_output.put_line( x.column_name || ' = ' || l_data );
end loop;
end;
Note: I tested a version of this code in 12C and it does work there. Alas it doesn't work in 11gR2 (and presumably earlier versions too); it hurls PLS-00457. Still, 11gR2 is pretty much out of support except for folks with deep pockets, everybody ought to be using 12c by now:)
My guess would be that you have a hidden column in your emp table. Those are columns that've been marked as unused but not yet dropped, and as such, they are not available to be selected from.
You could update your cursor to use:
select column_name, data_type
from user_tab_cols
where table_name = 'EMP'
and hidden_column != 'NO'
or:
select column_name, data_type
from user_tab_columns
where table_name = 'EMP';
Note the different view name used in both queries - user_tab_columns doesn't output rows for hidden columns, whereas user_tab_cols does so you have to explicitly filter them out if you don't want to see them.

Oracle 11g "Bind variable does not exist"

I am getting an "ORA01006 Bind variable does not exist at line 15 "error in the following code:
DECLARE
v_search_string varchar2(4000) := 'OK';
v_query_str VARCHAR2(4000);
match_count integer;
BEGIN
FOR t IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type in ('CHAR', 'VARCHAR2', 'NCHAR', 'NVARCHAR2') And TABLE_NAME = 'T1' And OWNER = 'O1')
LOOP
Begin
v_query_str := 'SELECT COUNT(*) FROM '|| t.table_name || ' WHERE ' || t.column_name || ' Like ''' || '%:1%' || '''';
dbms_output.put_line(v_query_str);
EXECUTE Immediate v_query_str
INTO match_count
USING v_search_string;
IF match_count >= 0 THEN
dbms_output.put_line( t.owner || '.' || t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END;
END LOOP;
END;
I'm just trying to loop through all the character columns in the table and count how many values in each match the v_search_string value.
The line "dbms_output.put_line(v_query_str);" prints one line:
SELECT COUNT(*) FROM T1 WHERE Col1 Like '%:1%'
There are 10 columns in the table that are the specified types.
There is obviously a bind variable there (%1), so I can't figure out what's going on.
Form the string like this below.
t.column_name || ' Like ''%''||:1||''%'''
Bind variable should not be included within single quotes, as it would be treated as a String literal instead. So when you used USING it ended up with this excpetion.

query all tables in Oracle

I have this plsql code that will give me all tables in the database with name CUSTOMERS. Now I am struck how to insert another loop in to this. I want to get the output from this code and pass it on to next loop where I want to query something like, select count(*) from Schema.customers; for each schema.
DECLARE
--c_id customers.id%type;
c_name all_tables.table_name%type;
c_tabs all_tables.owner%type;
CURSOR c_tables is
SELECT table_name, owner FROM all_tables where table_name='CUSTOMERS';
BEGIN
OPEN c_tables;
LOOP
FETCH c_tables into c_name, c_tabs;
dbms_output.put_line(c_tabs || '.' || c_name );
EXIT WHEN c_tables%notfound;
END LOOP;
CLOSE c_tables;
END;
/
------- Sample output of my code: ------------
UMICH2.CUSTOMERS
TRINITYDC.CUSTOMERS
BUFFALO.CUSTOMERS
SNOW.CUSTOMERS
PULASKITECH.CUSTOMERS
RARITANVAL.CUSTOMERS
STMARYSCA.CUSTOMERS
You can get the same result in a single SQL statement
SELECT table_name
,to_number
(extractvalue
(xmltype
(dbms_xmlgen.getxml
('SELECT count(*) c FROM ' || owner || '.' || table_name)
)
,'/ROWSET/ROW/C'
)
) Count
FROM all_tables
WHERE table_name = 'CUSTOMERS'
This way is one possibility that you can do that.
DECLARE
--c_id customers.id%type;
c_name all_tables.table_name%TYPE;
c_tabs all_tables.owner%TYPE;
v_value PLS_INTEGER;
CURSOR c_tables IS
SELECT table_name
FROM all_tables
WHERE table_name = 'CUSTOMERS';
CURSOR c_owner IS
SELECT DISTINCT owner
FROM all_tables
WHERE table_name = 'CUSTOMERS';
BEGIN
OPEN c_tables;
LOOP
FETCH c_tables INTO c_name;
OPEN c_owner;
LOOP
FETCH c_owner INTO c_tabs;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(1) FROM ' || c_tabs || '.' || c_name INTO v_value;
EXCEPTION
WHEN other THEN
NULL;
END;
DBMS_OUTPUT.put_line ( v_value );
EXIT WHEN c_owner%NOTFOUND;
END LOOP;
CLOSE c_owner;
DBMS_OUTPUT.put_line ( c_tabs || '.' || c_name );
EXIT WHEN c_tables%NOTFOUND;
END LOOP;
CLOSE c_tables;
END;
Any question just let me know.
Thanks.

DROP all tables starting with "EXT_" in Oracle SQL

I know this question may ask many times but I could not find one line SQL statement.
I remember I did it before but now I could not remember how I did
I want to drop all tables whose name starts with "EXT_". Is it possibile to make it happen with one line SQL statement.
You could use a short anonymous block to do this.
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT_%' )
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
It's not possible with only one statement. Usually, I write a sql to get all the tables and then execute the results:
select 'drop table ' || table_name || ';'
from user_tables
where table_name like 'EXT_%';
This code will DROP not only EXT_% tables, it will act as DROP EXT% also.
Underscore is as special wildcard that acts as '%' but for a single character.
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT_%' )
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
In order to achieved desired results you should change your code the way below
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'EXT\_%' ESCAPE '\')
LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;
It escapes underscore char in order to be trated literally, ESCAPE '\' modifier indicates that escape char is '\'
In most of cases you will find contraints violations. In that case, this script can help you:
DECLARE
c_action CONSTANT VARCHAR2(10) := 'DROP';
BEGIN
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'STARTINGTEXT_%' )
LOOP
FOR reg IN (SELECT uc.table_name,
uc.constraint_name
FROM user_constraints uc
WHERE uc.table_name IN (c.table_name)) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE ' || reg.table_name || ' ' || c_action ||
' CONSTRAINT ' || reg.constraint_name ;
END LOOP;
END LOOP;
COMMIT;
FOR c IN ( SELECT table_name FROM user_tables WHERE table_name LIKE 'STARTINGTEXT_%' )
LOOP
EXECUTE IMMEDIATE 'TRUNCATE TABLE ' || c.table_name;
EXECUTE IMMEDIATE 'DROP TABLE ' || c.table_name;
END LOOP;
END;

Finding tables where there is data

I am working on a system where there are 50/60 tables. Each has the same unique key (call it MEMBID for this example)
Is there a query I can run that will show me the names of all tables that have at least one row where the MEMBID exists?
Or do I need to cursor through the USER_TABLES table and then build a dynamic query to build up an "array"?
Many thanks
Mike
I'd go for dynamic SQL - that's pretty straightforward:
declare
l_cnt_membid number;
l_cnt_overall number;
begin
for cur in (select table_name from user_tab_cols where column_name = 'MEMBID')
loop
execute immediate 'select count(*), count(membid) from ' || cur.table_name
into l_cnt_overall, l_cnt_membid;
dbms_output.put_line(cur.table_name || ', overall: ' || l_cnt_overall ||
', membid: ' || l_cnt_membid);
end loop;
end;
EDIT:
If your table statistics are up-to-date, you can obtain this information from user_tab_cols directly:
select table_name,
(case when num_distinct > 0
then 'YES'
else 'NO' end) has_nonnull_membid
from user_tab_cols
where column_name = 'MEMBID'