So I have the problem, that there is a variable, which could contains different column names
and then in the SELECT I want to compare the column with a specific word.
But then it seems like the apostrophe make problems:
query := 'SELECT value FROM table WHERE ' || variable || ' like ''word''';
EXECUTE IMMEDIATE query INTO rec;
EXCEPTION WHEN OTHERS THEN htp.p(dbms_utility.format_error_stack);
SQL> set serveroutput on;
SQL> DECLARE
2 VAR VARCHAR2(20);
3 REC NUMBER;
4 query VARCHAR2(1000);
5 BEGIN
6 var := 'TABLE_NAME';
7 QUERY := 'SELECT count(*) FROM USER_TABLES WHERE ' || VAR || ' like ''%EMP%''';
8 dbms_output.put_line(query);
9 EXECUTE IMMEDIATE QUERY INTO REC;
10 dbms_output.put_line(rec);
11 END;
12 /
SELECT count(*) FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
1
PL/SQL procedure successfully completed.
With REC as collection type :
SQL> DECLARE
2 var VARCHAR2(20);
3 TYPE rec_typ
4 IS TABLE OF user_tables%ROWTYPE;
5 rec REC_TYP;
6 query VARCHAR2(1000);
7 BEGIN
8 var := 'TABLE_NAME';
9
10 query := 'SELECT * FROM USER_TABLES WHERE '
11 || var
12 || ' like ''%EMP%''';
13
14 dbms_output.Put_line(query);
15
16 EXECUTE IMMEDIATE query bulk collect INTO rec;
17
18 FOR i IN 1..rec.count LOOP
19 dbms_output.Put_line(Rec(i).table_name);
20 END LOOP;
21 END;
22 /
SELECT * FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
EMP
PL/SQL procedure successfully completed.
If you're searching for values in column that are equal to word then change like to =.
If you're searching for values in column that contains word then change like ''word'' to like ''%word%'' (don't forget about the third apostrophe that's closing dynamic query).
Related
I need to create an anonymous block which will calculate the number of rows in the table with the name and selection criterion determined at runtime.
This is my attempt:
DECLARE
tableName VARCHAR2(45) :=:numeTable;
col VARCHAR2(45):=:campul;
val VARCHAR2(30):=:idValue;
BEGIN
EXECUTE IMMEDIATE
'select count(*) from :tableName where :col = :val'
USING tableName, col, val;
END;
You can't bind table or column names; concatenate them (and beware of SQL injection).
SQL> declare
2 tablename varchar2(30) := 'emp';
3 col varchar2(30) := 'job';
4 val varchar2(30) := 'CLERK';
5 l_cnt number;
6 begin
7 execute immediate 'select count(*) from ' || dbms_assert.sql_object_name(tableName) ||
8 ' where ' || dbms_assert.simple_sql_name(col) || ' = :a'
9 into l_cnt
10 using val;
11
12 dbms_output.put_line('count = ' || l_cnt);
13 end;
14 /
count = 4
PL/SQL procedure successfully completed.
SQL>
In SQL Oracle, is there a way to filter an ALL_TAB_COLUMNS SELECT statement, by the values in a specific column?
Yes; you'll need PL/SQL with dynamic SQL to do that.
Here's an example I use to search through current user's tables, check the ones that contain ENAME column which contains SCOTT string within. The result says that two tables (EMPLOYEE and EMP) contain one row with such a value.
Adjust it to your needs.
SQL> SET SERVEROUTPUT ON
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'ENAME'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%SCOTT%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
1 : EMPLOYEE
1 : EMP
PL/SQL procedure successfully completed.
SQL>
I've got a table named "F_ParqueInfra", that I'd like to count all values in it where the value is equal to -1.
So, this table has 11 columns and 833 rows = 9.163 number of data in this table.
I'd like to know, how many "-1" values has in the whole table (all columns), in the simplest way.
Also I'll do that with a lot of tables in my Data Warehouse.
Really thanks!
One option is to use dynamic SQL. For example:
SQL> select * from f_parqueinfra;
ID_USUARIO ID_EMPRESA ID_DEPARTAMENTO
---------- ---------- ---------------
250 32 12
-1 -1 -1
0 -1 1
5 2 -1
SQL> set serveroutput on;
SQL> declare
2 l_table_name varchar2(30) := 'F_PARQUEINFRA';
3 l_value number := -1; -- search value
4 l_str varchar2(200); -- to compose SELECT statement
5 l_cnt number := 0; -- number of values in one column
6 l_sum number := 0; -- total sum of values
7 begin
8 for cur_r in (select table_name, column_name
9 from user_tab_columns
10 where table_name = l_table_name
11 and data_type = 'NUMBER'
12 )
13 loop
14 l_str := 'select count(*) from ' ||cur_r.table_name ||
15 ' where ' || cur_r.column_name || ' = ' || l_value;
16 execute immediate l_str into l_cnt;
17 l_sum := l_sum + l_cnt;
18 end loop;
19 dbms_output.put_line('Number of ' || l_value ||' values = ' || l_sum);
20 end;
21 /
Number of -1 values = 5
PL/SQL procedure successfully completed.
SQL>
If you change l_value (line #3), you can search for some other value, e.g.
SQL> l3
3* l_value number := -1; -- search value
SQL> c/-1/250
3* l_value number := 250; -- search value
SQL> /
Number of 250 values = 1
PL/SQL procedure successfully completed.
SQL>
Or, you can change table name (line #2) and search some other table.
Basically, you'd probably want to turn that anonymous PL/SQL code into a function which would accept table name and search value and return number of appearances. That shouldn't be too difficult so I'll leave it to you, for practice.
[EDIT: converting it into a function]
Although far from being perfect, something like this will let you search for some numeric and string values in tables in current schema. Dates are more complex, depending on formats etc. but - for simple cases - this code might be OK for you. Have a look:
SQL> create or replace function f_cnt (par_table_name in varchar2,
2 par_data_type in varchar2,
3 par_value in varchar2)
4 return sys.odcivarchar2list
5 is
6 l_data_type varchar2(20) := upper(par_data_type);
7 l_retval sys.odcivarchar2list := sys.odcivarchar2list();
8 l_str varchar2(200); -- to compose SELECT statement
9 l_out varchar2(200); -- return value
10 l_cnt number := 0; -- number of values in one column
11 l_sum number := 0; -- total sum of values
12 begin
13 -- loop through all tables in current schema
14 for cur_t in (select table_name
15 from user_tables
16 where table_name = upper(par_table_name)
17 or par_table_name is null
18 )
19 loop
20 -- reset the counter
21 l_sum := 0;
22 -- loop through all columns in that table
23 for cur_c in (select column_name
24 from user_tab_columns
25 where table_name = cur_t.table_name
26 and data_type = l_data_type
27 )
28 loop
29 -- pay attention to search value's data type
30 if l_data_type = 'VARCHAR2' then
31 l_str := 'select count(*) from ' ||cur_t.table_name ||
32 ' where ' || cur_c.column_name || ' = ' ||
33 chr(39) || par_value ||chr(39);
34 elsif l_data_type = 'NUMBER' then
35 l_str := 'select count(*) from ' ||cur_t.table_name ||
36 ' where ' || cur_c.column_name || ' = ' || par_value;
37 end if;
38
39 execute immediate l_str into l_cnt;
40 l_sum := l_sum + l_cnt;
41 end loop;
42
43 if l_sum > 0 then
44 l_out := cur_t.table_name ||' has ' || l_sum ||' search values';
45 l_retval.extend;
46 l_retval(l_retval.count) := l_out;
47 end if;
48 end loop;
49 return l_retval;
50 end;
51 /
Function created.
Testing:
SQL> select * From table(f_cnt(null, 'number', -1));
COLUMN_VALUE
-----------------------------------------------------------------
F_PARQUEINFRA has 5 search values
SQL> select * From table(f_cnt(null, 'varchar2', 'KING'));
COLUMN_VALUE
-----------------------------------------------------------------
EMP has 1 search values
SQL>
This might be a good place to use the unpivot syntax. This still requires you to type all the column names once - but not more.
Here is an example for 4 columns:
select count(*) cnt
from mytable
unpivot(myval for mycol in (col1, col2, col3, col4))
where myval = -1
As a bonus, you can easily modify the query to get the number of -1 per column:
select mycol, count(*) cnt
from mytable
unpivot(myval for mycol in (col1, col2, col3, col4))
where myval = -1
group by mycol
This should give you what you need.
Notes:
performs true numeric comparison (for example would match -1.00 also) using an inline function to prevent error should the value in a compared cell be non-numeric. (if all your compared values are guaranteed to be numeric the inline function can be simplified dramatically)
searches only varchar2 and number column types (this can be changed if desired).
The code follows:
set serveroutput on size 10000
declare
vMyTableName varchar2(200) := 'F_ParqueInfra';
vMyValue number := -1;
vSQL varchar2(4000);
vTotal pls_integer;
vGrandTotal number(18) := 0;
cursor c1 is
select *
from user_tab_columns
where table_name = vMyTableName;
vInlineFn varchar2(4000) := 'with
function matchesMyValue(value varchar2) return pls_integer
is
begin
return case
when to_number(value) = '||vMyValue||' then
1
else
0
end;
exception
when value_error then
return 0;
end;
';
begin
for x in c1 loop
if x.data_type in ('VARCHAR2','NUMBER') then -- only looking in these column data types for -1 but you can flex this to suit your column type definitions
vSQL := 'select sum(matchesMyValue('||x.column_name||')) from '||vMyTableName;
execute immediate vInlineFn||vSQL into vTotal;
vGrandTotal := vGrandTotal + vTotal;
end if;
end loop;
dbms_output.put_line('Total cells containing -1 = '||vGrandTotal);
end;
/
I have to update all the tables having column name like '%DIV%' with a value DD wherever it is MG , I have written the script for it , but I am not getting the idea of how to verify if columns of all the tables are updated to value DD after the activity is performed. I have written this query .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''MG'' ;'
FROM RADHA.CHANGE_TABLE
WHERE VALID_FLAG='Y'
I was planning to make a table structure like
OWNER TABLE_NAME PREV_COUNT
The PREV_COUNT will hold the count of rows having Column Value as MG and after the activity is performed , I will verify with following query if the corresponding rows have been updated to DD .
SELECT 'SELECT '||OWNER||'.'||TABLE_NAME||', '||COLUMN_NAME||' FROM '||OWNER||'.'||TABLE_NAME||' WHERE '||COLUMN_NAME||' = ''DD'' ;' FROM RADHA.CHANGE_TABLE WHERE VALID_FLAG='Y'
And the output of this query would go into table
OWNER TABLE_NAME NEW_COUNT
But I am not able to get how to fetch records from the Select query as it is the string which is written inside the select query but I want the result set such that I can insert the records in my table mentioned above, please guide how to approach further
I don't have your tables, but - based on Scott's sample schema, here's a script which search through all its tables for a column named JOB (line #8) and checks how many of them have value that looks like (hint: like) CLERK in it (line #12).
See how it works, adjust it so that it works for you.
SQL> DECLARE
2 l_str VARCHAR2(500);
3 l_cnt NUMBER := 0;
4 BEGIN
5 FOR cur_r IN (SELECT u.table_name, u.column_name
6 FROM user_tab_columns u, user_tables t
7 WHERE u.table_name = t.table_name
8 AND u.column_name = 'JOB'
9 )
10 LOOP
11 l_str := 'SELECT COUNT(*) FROM ' || cur_r.table_name ||
12 ' WHERE ' || cur_r.column_name || ' like (''%CLERK%'')';
13
14 EXECUTE IMMEDIATE (l_str) INTO l_cnt;
15
16 IF l_cnt > 0 THEN
17 dbms_output.put_line(l_cnt ||' : ' || cur_r.table_name);
18 END IF;
19 END LOOP;
20 END;
21 /
4 : EMP --> there are 4 CLERKs in the EMP table
PL/SQL procedure successfully completed.
SQL>
I have a query like this:
v_sql:= 'select count(1) from '||v_tbl||' where col1 = ' || month_var || ' and ds =''T''';
execute immediate v_sql into v_row_cnt;
for j in 1..v_row_cnt
loop
for i in (select b.* from
(select a.*, rownum as rn
from (select * from MY_TABLE where col1 = month_var and DS = 'T') a
) b
where rn=j)
loop
do_something;
end loop;
end loop;
Now, my problem is, I can't hard code MY_TABLE here. I need to use a variable. I am currently doing it this way because I need to process data row by row.
Any ideas how to do this?
Thanks.
Ronn
You would use dynamic SQL to build a cursor.
There is an inefficiency in your logic here: you start by counting the number of rows in a given table, then you execute the same query once for each row (another example of Schlemiel the Painter's algorithm).
You don't need to do that, just loop over the cursor, this will execute the query once only. For example:
SQL> DECLARE
2 l_cursor SYS_REFCURSOR;
3 l_table VARCHAR2(32) := 'ALL_OBJECTS';
4 l_name VARCHAR2(32);
5 BEGIN
6 OPEN l_cursor FOR 'SELECT object_name
7 FROM ' || l_table
8 || ' WHERE rownum <= 5 ';
9 LOOP
10 FETCH l_cursor INTO l_name;
11 EXIT WHEN l_cursor%NOTFOUND;
12 -- do_something
13 dbms_output.put_line(l_name);
14 END LOOP;
15 CLOSE l_cursor;
16 END;
17 /
C_OBJ#
I_OBJ#
TAB$
CLU$
C_TS#
You don't need to count the number of rows beforehand, if the cursor is empty the loop with exit immediately.