Oracle PLSQL evaluate variable in dynamic SQL - oracle

I have the below code, which works fine because cols is evaluated before it's used on the second dynamic statement.
DECLARE
vstr_aux_cols CLOB;
vstr_aux CLOB;
cols CLOB;
BEGIN
vstr_aux_cols := q'[
SELECT
LISTAGG('"'
|| column_name
|| '"', ',') WITHIN GROUP(
ORDER BY
column_name
)
FROM
all_tab_columns
WHERE
lower(table_name) = 'd_dialler_brut'
AND column_name LIKE 'REASON%'
]'
;
EXECUTE IMMEDIATE vstr_aux_cols
INTO cols;
vstr_aux := q'[
WITH aux AS (
SELECT
some columns
FROM
d_dialler_brut UNPIVOT ( v
FOR reason
IN ( ]'
|| cols
|| q'[ ) ) u
WHERE
condition
]';
dbms_output.put_line(vstr_aux);
END;
However, if I try the below, it does not work because it first evaluates cols to blank and builds the vstr_aux with it empty. Is there a way to tweak the second code fragment to make it evaluate cols in vstr_aux after cols has been populated?
DECLARE
vstr_aux_cols CLOB;
vstr_aux CLOB;
cols CLOB;
BEGIN
vstr_aux_cols := q'[
SELECT
LISTAGG('"'
|| column_name
|| '"', ',') WITHIN GROUP(
ORDER BY
column_name
)
FROM
all_tab_columns
WHERE
lower(table_name) = 'd_dialler_brut'
AND column_name LIKE 'REASON%'
]'
;
vstr_aux := q'[
WITH aux AS (
SELECT
some columns
FROM
d_dialler_brut UNPIVOT ( v
FOR reason
IN ( ]'
|| cols
|| q'[ ) ) u
WHERE
condition
]';
EXECUTE IMMEDIATE vstr_aux_cols
INTO cols;
dbms_output.put_line(vstr_aux);
END;

Related

error ORA-00998: must name this expression with a column alias

I tried to create table from all_tab_columns but it throws me an error.
like error ORA-00998: must name this expression with a column alias.
i tried to figure it out but not working.
Declare
CURSOR c1 IS
SELECT COLUMN_NAME FROM (select 'standard_hash(MY_NAME) AS MY_NAME' COLUMN_NAME from DUAL
UNION
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS WHERE TABLE_NAME='TABLE1' AND COLUMN_NAME<>'MY_NAME');
cols c1%ROWTYPE;
sqlstmt VARCHAR2(4000);
BEGIN
OPEN c1;
LOOP
FETCH c1 into cols;
EXIT WHEN c1%NOTFOUND;
sqlstmt := sqlstmt ||cols.column_name||',';
END LOOP;
CLOSE c1;
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '||substr(sqlstmt, 1, length(sqlstmt)-1)||' FROM TABLE1';
DBMS_OUTPUT.PUT_LINE(sqlstmt);
EXECUTE IMMEDIATE sqlstmt;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('error '||sqlerrm);
END;
/
You just have to give the name to the expression as follows:
sqlstmt := 'CREATE TABLE TABLE2 AS SELECT '
||substr(sqlstmt, 1, length(sqlstmt)-1)
||' as column_name FROM TABLE1'; -- column_name will bre name of the column of new table
As it will become the name of the column of newly created table.
I think this can be simplified somewhat.
You don't need to declare an explicit cursor and a record type and fetch each row into it, as this is already built into the language as the Cursor FOR loop construction:
declare
sqlStmt long := 'create table table2 as select ';
begin
for r in (
select 'standard_hash(my_name) as my_name' column_name
from dual
union
select column_name
from all_tab_columns
where table_name = 'TABLE1'
and column_name <> 'MY_NAME'
)
loop
sqlStmt := sqlStmt || r.column_name || ', ';
end loop;
sqlStmt := sqlStmt || rtrim(sqlStmt,', ') || ' from table1';
dbms_output.put_line(sqlStmt);
execute immediate sqlStmt;
end;
/
But you can do this without any loop, as the listagg function can already build a comma-separated list for you. It also makes it easier to retain the order of columns in the original table.
declare
selectList long;
sqlStmt long;
begin
select listagg(
case column_name
when 'MY_NAME' then 'standard_hash(my_name) as my_name'
else lower(column_name)
end, ', '
) within group (order by column_id) as select_list
into selectList
from user_tab_columns c
where c.table_name = 'TABLE1';
sqlStmt :=
'create table table2 pctfree 0 nologging parallel as '||chr(10)||
'select '||selectList||chr(10)||
'from table1';
dbms_output.put_line(sqlStmt||';');
execute immediate sqlstmt;
end;

ORA-00904 invalid identifier error in dynamic SQL block

I'm running the below which once executed, an error is reported telling me that EST_ONE_ROW_MB is an invalid identifier.
I've been advised I perhaps need to get the dynamic SQL part running as a stand alone query to begin with as an initial troubleshooting exercise but I'm a bit stumped in terms of how to write a sub-query here that will produce the desired output and eliminate the error.
create or replace procedure JUST_ME is
--variables
l_dblink varchar2(100) := 'DB1';
file_handle UTL_FILE.file_type;
v_ts_name varchar2(30);
v_link_name varchar2(10);
v_csv_name varchar2(100);
EST_ONE_ROW_MB varchar2(100) ;
TOTAL_ROW_COUNT NUMBER;
SPACE_REQUIRED NUMBER;
TOT_OBJECT_SIZE_MB NUMBER;
v_Mv_name varchar2(100);
v_sql1 varchar2(1500);
cur SYS_REFCURSOR;
owner varchar2(100);
table_name varchar2(100);
driver_table varchar2(100);
mandatory_join varchar2(100);
C_TOTAL_ROW_COUNT varchar2(100);
v_total_driver_only varchar2(100);
--
begin
SELECT tablename into v_csv_name
FROM BOB01.BOB_new_table_tracker
WHERE
CREATED_AT = (select MAX(CREATED_AT) from BOB01.BOB_new_table_tracker);
SELECT mv_name into v_Mv_name
FROM BOB01.BOB_new_table_tracker_mv
WHERE
CREATED_AT = (select MAX(CREATED_AT) from BOB01.BOB_new_table_tracker_mv);
select link_name into v_link_name from link_and_mail where mdate = (select max(mdate) from link_and_mail);
select distinct targetschema into v_ts_name from BOB01.MV_BOB_TABLE;
v_sql1 := 'SELECT /*+ monitor parallel (4)*/ a.owner,
a.table_name,
b.driver_table,
b.mandatory_join,
sum(c.sum_bytes) TOT_OBJECT_SIZE_MB,
(TOT_OBJECT_SIZE_MB) / (:C_TOTAL_ROW_COUNT) AS "EST_ONE_ROW_MB",
(EST_ONE_ROW_MB) * (:TOTAL_ROW_COUNT) AS "SPACE_REQUIRED"
FROM dba_tables#DB1 a, '|| v_Mv_name ||' b, MV_PRD_SEG_DATA c
WHERE a.table_name IN ( SELECT table_name
FROM MV_BOB_TABLE
WHERE driver_table IS NOT NULL
AND additional_joins IS NULL
)
AND a.owner IN ( SELECT DISTINCT productionschema FROM MV_BOB_TABLE c )
and a.table_name = b.table_name
and a.table_name = c.segment_name
group by a.owner,a.table_name,b.driver_table,b.mandatory_join
ORDER BY table_name';
file_handle := utl_file.fopen('ESTIMATES_CSV', v_csv_name||'_EST_PROC.csv', 'w', 32767);
--
UTL_FILE.PUT_LINE(file_handle, ' ');
UTL_FILE.PUT_LINE(file_handle, 'The below report shows total row counts in PROD');
UTL_FILE.PUT_LINE(file_handle, ' for unjoined tables in the BOB document:');
UTL_FILE.PUT_LINE(file_handle, ' ');
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,MANDATORY_JOIN,TOT_OBJECT_SIZE_MB,EST_ONE_ROW_MB,TOTAL_ROW_COUNT,SPACE_REQUIRED');
--main loop
open cur for v_sql1 using TOTAL_ROW_COUNT,C_TOTAL_ROW_COUNT;
loop
fetch cur into OWNER,TABLE_NAME,MANDATORY_JOIN,TOT_OBJECT_SIZE_MB,EST_ONE_ROW_MB,TOTAL_ROW_COUNT,SPACE_REQUIRED;--,EST_ONE_ROW_MB;
exit when cur%NOTFOUND;
execute immediate' select /*+parallel (4)*/ count(*) from '||owner||'.'||table_name || '#' || l_dblink into TOTAL_ROW_COUNT;
execute immediate' select /*+monitor parallel (10)*/ count(*) from ' ||owner||'.'||table_name || '#' || l_dblink||' b '||','||
driver_table || '#' || l_dblink||' a ' ||' where ' ||mandatory_join into TOTAL_ROW_COUNT;
execute immediate' select /*+monitor parallel (10)*/ count(*) from ' ||owner||'.'||table_name || '#' || l_dblink into C_TOTAL_ROW_COUNT;
utl_file.put_line(file_handle,
OWNER || ',' ||
TABLE_NAME || ',' ||
TOT_OBJECT_SIZE_MB || ',' ||
TOTAL_ROW_COUNT || ',' ||
C_TOTAL_ROW_COUNT || ',' ||
round(TOT_OBJECT_SIZE_MB / C_TOTAL_ROW_COUNT,7)|| ',' ||
round(round(TOT_OBJECT_SIZE_MB / C_TOTAL_ROW_COUNT,7) * round(TOTAL_ROW_COUNT,0),0)
);
v_total_driver_only := v_total_driver_only + round(TOT_OBJECT_SIZE_MB / C_TOTAL_ROW_COUNT,7) * round(TOTAL_ROW_COUNT,0);
end loop;
UTL_FILE.PUT_LINE(file_handle, ' ');
utl_file.put_line(file_handle,
'Total Estimated Space Required '|| round(v_total_driver_only,0) ||' MB'
);
utl_file.fclose(file_handle);
end JUST_ME;
to use EST_ONE_ROW_MB on that way is not possible because its' not defied as column.
replace it by (TOT_OBJECT_SIZE_MB) / (:C_TOTAL_ROW_COUNT)
SELECT /*+ monitor parallel (4)*/ a.owner,
a.table_name,
b.driver_table,
b.mandatory_join,
sum(c.sum_bytes) TOT_OBJECT_SIZE_MB, -- will be the same problem
(sum(c.sum_bytes)) / (:C_TOTAL_ROW_COUNT) AS "EST_ONE_ROW_MB",
((sum(c.sum_bytes)) / (:C_TOTAL_ROW_COUNT)) * (:TOTAL_ROW_COUNT) AS "SPACE_REQUIRED"
...
you can do that if you have an Inline View. e.g.
select EST_ONE_ROW_MB * (:TOTAL_ROW_COUNT) AS "SPACE_REQUIRED"
from(
select (TOT_OBJECT_SIZE_MB) / (:C_TOTAL_ROW_COUNT) AS "EST_ONE_ROW_MB"
from ....
)

Dynamic SQL Hangs [duplicate]

Is it possible to search every field of every table for a particular value in Oracle?
There are hundreds of tables with thousands of rows in some tables so I know this could take a very long time to query. But the only thing I know is that a value for the field I would like to query against is 1/22/2008P09RR8.
<
I've tried using this statement below to find an appropriate column based on what I think it should be named but it returned no results.
SELECT * from dba_objects
WHERE object_name like '%DTN%'
There is absolutely no documentation on this database and I have no idea where this field is being pulled from.
Any thoughts?
Quote:
I've tried using this statement below
to find an appropriate column based on
what I think it should be named but it
returned no results.*
SELECT * from dba_objects WHERE
object_name like '%DTN%'
A column isn't an object. If you mean that you expect the column name to be like '%DTN%', the query you want is:
SELECT owner, table_name, column_name FROM all_tab_columns WHERE column_name LIKE '%DTN%';
But if the 'DTN' string is just a guess on your part, that probably won't help.
By the way, how certain are you that '1/22/2008P09RR8' is a value selected directly from a single column? If you don't know at all where it is coming from, it could be a concatenation of several columns, or the result of some function, or a value sitting in a nested table object. So you might be on a wild goose chase trying to check every column for that value. Can you not start with whatever client application is displaying this value and try to figure out what query it is using to obtain it?
Anyway, diciu's answer gives one method of generating SQL queries to check every column of every table for the value. You can also do similar stuff entirely in one SQL session using a PL/SQL block and dynamic SQL. Here's some hastily-written code for that:
SET SERVEROUTPUT ON SIZE 100000
DECLARE
match_count INTEGER;
BEGIN
FOR t IN (SELECT owner, table_name, column_name
FROM all_tab_columns
WHERE owner <> 'SYS' and data_type LIKE '%CHAR%') LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM ' || t.owner || '.' || t.table_name ||
' WHERE '||t.column_name||' = :1'
INTO match_count
USING '1/22/2008P09RR8';
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
There are some ways you could make it more efficient too.
In this case, given the value you are looking for, you can clearly eliminate any column that is of NUMBER or DATE type, which would reduce the number of queries. Maybe even restrict it to columns where type is like '%CHAR%'.
Instead of one query per column, you could build one query per table like this:
SELECT * FROM table1
WHERE column1 = 'value'
OR column2 = 'value'
OR column3 = 'value'
...
;
I did some modification to the above code to make it work faster if you are searching in only one owner.
You just have to change the 3 variables v_owner, v_data_type and v_search_string to fit what you are searching for.
SET SERVEROUTPUT ON SIZE 100000
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
v_owner VARCHAR2(255) :='ENTER_USERNAME_HERE';
-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking at
v_search_string VARCHAR2(4000) :='string to search here...';
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE '||t.column_name||' = :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
I know this is an old topic. But I see a comment to the question asking if it could be done in SQL rather than using PL/SQL. So thought to post a solution.
The below demonstration is to Search for a VALUE in all COLUMNS of all TABLES in an entire SCHEMA:
Search a CHARACTER type
Let's look for the value KING in SCOTT schema.
SQL> variable val varchar2(10)
SQL> exec :val := 'KING'
PL/SQL procedure successfully completed.
SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
2 SUBSTR (table_name, 1, 14) "Table",
3 SUBSTR (column_name, 1, 14) "Column"
4 FROM cols,
5 TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
6 || column_name
7 || ' from '
8 || table_name
9 || ' where upper('
10 || column_name
11 || ') like upper(''%'
12 || :val
13 || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
14 ORDER BY "Table"
15 /
Searchword Table Column
----------- -------------- --------------
KING EMP ENAME
SQL>
Search a NUMERIC type
Let's look for the value 20 in SCOTT schema.
SQL> variable val NUMBER
SQL> exec :val := 20
PL/SQL procedure successfully completed.
SQL> SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
2 SUBSTR (table_name, 1, 14) "Table",
3 SUBSTR (column_name, 1, 14) "Column"
4 FROM cols,
5 TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
6 || column_name
7 || ' from '
8 || table_name
9 || ' where upper('
10 || column_name
11 || ') like upper(''%'
12 || :val
13 || '%'')' ).extract ('ROWSET/ROW/*') ) ) t
14 ORDER BY "Table"
15 /
Searchword Table Column
----------- -------------- --------------
20 DEPT DEPTNO
20 EMP DEPTNO
20 EMP HIREDATE
20 SALGRADE HISAL
20 SALGRADE LOSAL
SQL>
Yes you can and your DBA will hate you and will find you to nail your shoes to the floor because that will cause lots of I/O and bring the database performance really down as the cache purges.
select column_name from all_tab_columns c, user_all_tables u where c.table_name = u.table_name;
for a start.
I would start with the running queries, using the v$session and the v$sqlarea. This changes based on oracle version. This will narrow down the space and not hit everything.
Here is another modified version that will compare a lower substring match. This works in Oracle 11g.
DECLARE
match_count INTEGER;
-- Type the owner of the tables you are looking at
v_owner VARCHAR2(255) :='OWNER_NAME';
-- Type the data type you are look at (in CAPITAL)
-- VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking at
v_search_string VARCHAR2(4000) :='%lower-search-sub-string%';
BEGIN
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE lower('||t.column_name||') like :1'
INTO match_count
USING v_search_string;
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
/
I modified Flood's script to execute once for each table rather than for every column of each table for faster execution. It requires Oracle 11g or greater.
set serveroutput on size 100000
declare
v_match_count integer;
v_counter integer;
-- The owner of the tables to search through (case-sensitive)
v_owner varchar2(255) := 'OWNER_NAME';
-- A string that is part of the data type(s) of the columns to search through (case-insensitive)
v_data_type varchar2(255) := 'CHAR';
-- The string to be searched for (case-insensitive)
v_search_string varchar2(4000) := 'FIND_ME';
-- Store the SQL to execute for each table in a CLOB to get around the 32767 byte max size for a VARCHAR2 in PL/SQL
v_sql clob := '';
begin
for cur_tables in (select owner, table_name from all_tables where owner = v_owner and table_name in
(select table_name from all_tab_columns where owner = all_tables.owner and data_type like '%' || upper(v_data_type) || '%')
order by table_name) loop
v_counter := 0;
v_sql := '';
for cur_columns in (select column_name from all_tab_columns where
owner = v_owner and table_name = cur_tables.table_name and data_type like '%' || upper(v_data_type) || '%') loop
if v_counter > 0 then
v_sql := v_sql || ' or ';
end if;
v_sql := v_sql || 'upper(' || cur_columns.column_name || ') like ''%' || upper(v_search_string) || '%''';
v_counter := v_counter + 1;
end loop;
v_sql := 'select count(*) from ' || cur_tables.table_name || ' where ' || v_sql;
execute immediate v_sql
into v_match_count;
if v_match_count > 0 then
dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
end if;
end loop;
exception
when others then
dbms_output.put_line('Error when executing the following: ' || dbms_lob.substr(v_sql, 32600));
end;
/
I was having following issues for #Lalit Kumars answer,
ORA-19202: Error occurred in XML processing
ORA-00904: "SUCCESS": invalid identifier
ORA-06512: at "SYS.DBMS_XMLGEN", line 288
ORA-06512: at line 1
19202. 00000 - "Error occurred in XML processing%s"
*Cause: An error occurred when processing the XML function
*Action: Check the given error message and fix the appropriate problem
Solution is:
WITH char_cols AS
(SELECT /*+materialize */ table_name, column_name
FROM cols
WHERE data_type IN ('CHAR', 'VARCHAR2'))
SELECT DISTINCT SUBSTR (:val, 1, 11) "Searchword",
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"
/
I would do something like this (generates all the selects you need).
You can later on feed them to sqlplus:
echo "select table_name from user_tables;" | sqlplus -S user/pwd | grep -v "^--" | grep -v "TABLE_NAME" | grep "^[A-Z]" | while read sw;
do echo "desc $sw" | sqlplus -S user/pwd | grep -v "\-\-\-\-\-\-" | awk -F' ' '{print $1}' | while read nw;
do echo "select * from $sw where $nw='val'";
done;
done;
It yields:
select * from TBL1 where DESCRIPTION='val'
select * from TBL1 where ='val'
select * from TBL2 where Name='val'
select * from TBL2 where LNG_ID='val'
And what it does is - for each table_name from user_tables get each field (from desc) and create a select * from table where field equals 'val'.
if we know the table and colum names but want to find out the number of times string is appearing for each schema:
Declare
owner VARCHAR2(1000);
tbl VARCHAR2(1000);
cnt number;
ct number;
str_sql varchar2(1000);
reason varchar2(1000);
x varchar2(1000):='%string_to_be_searched%';
cursor csr is select owner,table_name
from all_tables where table_name ='table_name';
type rec1 is record (
ct VARCHAR2(1000));
type rec is record (
owner VARCHAR2(1000):='',
table_name VARCHAR2(1000):='');
rec2 rec;
rec3 rec1;
begin
for rec2 in csr loop
--str_sql:= 'select count(*) from '||rec.owner||'.'||rec.table_name||' where CTV_REMARKS like '||chr(39)||x||chr(39);
--dbms_output.put_line(str_sql);
--execute immediate str_sql
execute immediate 'select count(*) from '||rec2.owner||'.'||rec2.table_name||' where column_name like '||chr(39)||x||chr(39)
into rec3;
if rec3.ct <> 0 then
dbms_output.put_line(rec2.owner||','||rec3.ct);
else null;
end if;
end loop;
end;
Procedure to Search Entire Database:
CREATE or REPLACE PROCEDURE SEARCH_DB(SEARCH_STR IN VARCHAR2, TAB_COL_RECS OUT VARCHAR2) IS
match_count integer;
qry_str varchar2(1000);
CURSOR TAB_COL_CURSOR IS
SELECT TABLE_NAME,COLUMN_NAME,OWNER,DATA_TYPE FROM ALL_TAB_COLUMNS WHERE DATA_TYPE in ('NUMBER','VARCHAR2') AND OWNER='SCOTT';
BEGIN
FOR TAB_COL_REC IN TAB_COL_CURSOR
LOOP
qry_str := 'SELECT COUNT(*) FROM '||TAB_COL_REC.OWNER||'.'||TAB_COL_REC.TABLE_NAME||
' WHERE '||TAB_COL_REC.COLUMN_NAME;
IF TAB_COL_REC.DATA_TYPE = 'NUMBER' THEN
qry_str := qry_str||'='||SEARCH_STR;
ELSE
qry_str := qry_str||' like '||SEARCH_STR;
END IF;
--dbms_output.put_line( qry_str );
EXECUTE IMMEDIATE qry_str INTO match_count;
IF match_count > 0 THEN
dbms_output.put_line( qry_str );
--dbms_output.put_line( TAB_COL_REC.TABLE_NAME ||' '||TAB_COL_REC.COLUMN_NAME ||' '||match_count);
TAB_COL_RECS := TAB_COL_RECS||'##'||TAB_COL_REC.TABLE_NAME||'##'||TAB_COL_REC.COLUMN_NAME;
END IF;
END LOOP;
END SEARCH_DB;
Execute Statement
DECLARE
SEARCH_STR VARCHAR2(200);
TAB_COL_RECS VARCHAR2(200);
BEGIN
SEARCH_STR := 10;
SEARCH_DB(
SEARCH_STR => SEARCH_STR,
TAB_COL_RECS => TAB_COL_RECS
);
DBMS_OUTPUT.PUT_LINE('TAB_COL_RECS = ' || TAB_COL_RECS);
END;
Sample Results
Connecting to the database test.
SELECT COUNT(*) FROM SCOTT.EMP WHERE DEPTNO=10
SELECT COUNT(*) FROM SCOTT.DEPT WHERE DEPTNO=10
TAB_COL_RECS = ##EMP##DEPTNO##DEPT##DEPTNO
Process exited.
Disconnecting from the database test.
I don't of a simple solution on the SQL promprt. Howeve there are quite a few tools like toad and PL/SQL Developer that have a GUI where a user can input the string to be searched and it will return the table/procedure/object where this is found.
There are some free tools that make these kind of search, for example, this one works fine and source code is available:
https://sites.google.com/site/freejansoft/dbsearch
You'll need the Oracle ODBC driver and a DSN to use this tool.
Modifying the code to search case-insensitively using a LIKE query instead of finding exact matches...
DECLARE
match_count INTEGER;
-- Type the owner of the tables you want to search.
v_owner VARCHAR2(255) :='USER';
-- Type the data type you're looking for (in CAPS). Examples include: VARCHAR2, NUMBER, etc.
v_data_type VARCHAR2(255) :='VARCHAR2';
-- Type the string you are looking for.
v_search_string VARCHAR2(4000) :='Test';
BEGIN
dbms_output.put_line( 'Starting the search...' );
FOR t IN (SELECT table_name, column_name FROM all_tab_cols where owner=v_owner and data_type = v_data_type) LOOP
EXECUTE IMMEDIATE
'SELECT COUNT(*) FROM '||t.table_name||' WHERE LOWER('||t.column_name||') LIKE :1'
INTO match_count
USING LOWER('%'||v_search_string||'%');
IF match_count > 0 THEN
dbms_output.put_line( t.table_name ||' '||t.column_name||' '||match_count );
END IF;
END LOOP;
END;
I found the best solution but it's a little slow. (It will work perfectly with all SQL IDE's.)
SELECT DISTINCT table_name, column_name, data_type
FROM user_tab_cols,
TABLE (xmlsequence (dbms_xmlgen.getxmltype ('select '
|| column_name
|| ' from '
|| table_name
|| ' where lower('
|| column_name
|| ') like lower(''%'
|| 'your_text_here'
|| '%'')' ).extract ('ROWSET/ROW/*') ) ) a
where table_name not in (
select distinct table_name
from user_tab_cols where data_type like 'SDO%'
or data_type like '%LOB') AND DATA_TYPE = 'VARCHAR2'
order by table_name, column_name;
--it run completed -- no error
SET SERVEROUTPUT ON SIZE 100000
DECLARE
v_match_count INTEGER;
v_counter INTEGER;
v_owner VARCHAR2 (255) := 'VASOA';
v_search_string VARCHAR2 (4000) := '99999';
v_data_type VARCHAR2 (255) := 'CHAR';
v_sql CLOB := '';
BEGIN
FOR cur_tables
IN ( SELECT owner, table_name
FROM all_tables
WHERE owner = v_owner
AND table_name IN (SELECT table_name
FROM all_tab_columns
WHERE owner = all_tables.owner
AND data_type LIKE
'%'
|| UPPER (v_data_type)
|| '%')
ORDER BY table_name)
LOOP
v_counter := 0;
v_sql := '';
FOR cur_columns
IN (SELECT column_name, table_name
FROM all_tab_columns
WHERE owner = v_owner
AND table_name = cur_tables.table_name
AND data_type LIKE '%' || UPPER (v_data_type) || '%')
LOOP
IF v_counter > 0
THEN
v_sql := v_sql || ' or ';
END IF;
IF cur_columns.column_name is not null
THEN
v_sql :=
v_sql
|| 'upper('
|| cur_columns.column_name
|| ') ='''
|| UPPER (v_search_string)||'''';
v_counter := v_counter + 1;
END IF;
END LOOP;
IF v_sql is null
THEN
v_sql :=
'select count(*) from '
|| v_owner
|| '.'
|| cur_tables.table_name;
END IF;
IF v_sql is not null
THEN
v_sql :=
'select count(*) from '
|| v_owner
|| '.'
|| cur_tables.table_name
|| ' where '
|| v_sql;
END IF;
--v_sql := 'select count(*) from ' ||v_owner||'.'|| cur_tables.table_name ||' where '|| v_sql;
--dbms_output.put_line(v_sql);
--DBMS_OUTPUT.put_line (v_sql);
EXECUTE IMMEDIATE v_sql INTO v_match_count;
IF v_match_count > 0
THEN
DBMS_OUTPUT.put_line (v_sql);
dbms_output.put_line('Match in ' || cur_tables.owner || ': ' || cur_tables.table_name || ' - ' || v_match_count || ' records');
END IF;
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'Error when executing the following: '
|| DBMS_LOB.SUBSTR (v_sql, 32600));
END;
/
Borrowing, slightly enhancing and simplifying from this Blog post the following simple SQL statement seems to do the job quite well:
SELECT DISTINCT (:val) "Search Value", TABLE_NAME "Table", COLUMN_NAME "Column"
FROM cols,
TABLE (XMLSEQUENCE (DBMS_XMLGEN.GETXMLTYPE(
'SELECT "' || COLUMN_NAME || '" FROM "' || TABLE_NAME || '" WHERE UPPER("'
|| COLUMN_NAME || '") LIKE UPPER(''%' || :val || '%'')' ).EXTRACT ('ROWSET/ROW/*')))
ORDER BY "Table";
The Oracle LIKE condition allows wildcards to be used in the WHERE clause of a SELECT, INSERT, UPDATE, or DELETE statement.
%: to match any string of any length
Eg-
SELECT last_name
FROM customer_tab
WHERE last_name LIKE '%A%';
-: to match on a single character
Eg-
SELECT last_name
FROM customer_tab
WHERE last_name LIKE 'A_t';

Using arithmetic division in a PL/SQL Procedure

My PL/SQL Procedure provides the values I want, in a CSV file. BUT I want to do some mathematical division using some of the values. I get no output in the relevant column though when I run the code and i'd like to know why.
I've tried putting the variable desctiption in the declaritive section of the procedure, and after 'BEGIN' and also using brackets in various places. The code compiles, and it runs. It just won't give any output in the column that I'm trying to get output for.
create or replace procedure THANOS is
--variables
l_dblink varchar2(100) := 'DB1';
TOTAL_ROW_COUNT varchar2(3000);
TOT_OBJECT_SIZE_MB varchar2(100);
EST_ONE_ROW varchar2(100);
file_handle UTL_FILE.file_type;
v_ts_name varchar2(30);
v_link_name varchar2(10);
v_csv_name varchar2(100);
--
begin
SELECT tablename into v_csv_name
FROM table_tracker
WHERE
CREATED_AT = (select MAX(CREATED_AT) from table_tracker);
EST_ONE_ROW := (TOTAL_ROW_COUNT / TOT_OBJECT_SIZE_MB);
select link_name into v_link_name from link_and_mail where mdate = (select max(mdate) from link_and_mail);
select distinct targetschema into v_ts_name from table;
file_handle := utl_file.fopen('ESTIMATES_CSV', v_csv_name||'_EST_PROC.csv', 'w', 32767);
UTL_FILE.PUT_LINE(file_handle, 'The below report shows total row counts in PROD');
UTL_FILE.PUT_LINE(file_handle, ' for all tables in the request:');
UTL_FILE.PUT_LINE(file_handle, ' ');
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,TOT_OBJECT_SIZE_MB,TOTAL_ROW_COUNT,EST_ONE_ROW');
for rws in (
select /*+parallel */ a.owner,a.table_name, sum(b.sum_bytes) TOT_OBJECT_SIZE_MB, EST_ONE_ROW
from dba_tables#DB1 a, V_SEG_DATA b
where a.table_name = b.segment_name
and a.table_name in
(select table_name from table)
and a.owner in (select distinct schema from table c)
group by a.owner,a.table_name
order by a.table_name
)
loop
execute immediate' select count(*) from ' ||rws.owner||'.'||rws.table_name || '#' || l_dblink into TOTAL_ROW_COUNT;
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
rws.TOT_OBJECT_SIZE_MB || ',' ||
TOTAL_ROW_COUNT || ',' ||
EST_ONE_ROW
);
end loop;
utl_file.fclose(file_handle);
end THANOS;
The result of this code is to provide a .csv file with the following columns:
OWNER TABLE_NAME TOT_OBJECT_SIZE_MB TOTAL_ROW_COUNT EST_ONE_ROW
However, the EST_ONE_ROW column is always empty.
I want it to have the value for the number of rows divided by the total object size as per what is written:
EST_ONE_ROW := (TOTAL_ROW_COUNT / TOT_OBJECT_SIZE_MB);
Disclaimer -- People may say that this isn't a good way of finding what I'm trying to find, etc etc, but, it'd be great if no-one judged that, and just lead me in the right direction when it comes to what's wrong with the logic of the code itself, what I'm doing wrong with the 'division' logic
Thank you stackies!! :-)
They are all VARCHAR2 and are all empty. So there wouldn't be any result. This would be like this:
set serveroutput on
declare
TOTAL_ROW_COUNT varchar2(3000);
TOT_OBJECT_SIZE_MB varchar2(100);
EST_ONE_ROW VARCHAR2(100);
begin
EST_ONE_ROW := (TOTAL_ROW_COUNT / TOT_OBJECT_SIZE_MB);
dbms_output.put_line('EST_ONE_ROW:'||EST_ONE_ROW);
end;
Output:
EST_ONE_ROW:
PL/SQL procedure successfully completed.
But it look like EST_ONE_ROW is part of V_SEG_DATA so you might need to change the statement:
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
rws.TOT_OBJECT_SIZE_MB || ',' ||
TOTAL_ROW_COUNT || ',' ||
rws.EST_ONE_ROW -- <<<<<<<<<<<<<<< change here
);
Another way if this is purely calculated:
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
rws.TOT_OBJECT_SIZE_MB || ',' ||
TOTAL_ROW_COUNT || ',' ||
(TOTAL_ROW_COUNT / rws.TOT_OBJECT_SIZE_MB)
);
The above fails if TOT_OBJECT_SIZE_MB is zero. As you will get a division by zero failure. You might wnat to handle with an if statement.
So this might work:
create or replace procedure THANOS is
--variables
l_dblink varchar2(100) := 'DB1';
TOTAL_ROW_COUNT varchar2(3000);
TOT_OBJECT_SIZE_MB varchar2(100);
EST_ONE_ROW varchar2(100);
file_handle UTL_FILE.file_type;
v_ts_name varchar2(30);
v_link_name varchar2(10);
v_csv_name varchar2(100);
--
begin
SELECT tablename into v_csv_name
FROM table_tracker
WHERE
CREATED_AT = (select MAX(CREATED_AT) from table_tracker);
select link_name into v_link_name from link_and_mail where mdate = (select max(mdate) from link_and_mail);
select distinct targetschema into v_ts_name from table;
file_handle := utl_file.fopen('ESTIMATES_CSV', v_csv_name||'_EST_PROC.csv', 'w', 32767);
UTL_FILE.PUT_LINE(file_handle, 'The below report shows total row counts in PROD');
UTL_FILE.PUT_LINE(file_handle, ' for all tables in the request:');
UTL_FILE.PUT_LINE(file_handle, ' ');
utl_file.put_line(file_handle, 'OWNER,TABLE_NAME,TOT_OBJECT_SIZE_MB,TOTAL_ROW_COUNT,EST_ONE_ROW');
for rws in (
select /*+parallel */ a.owner,a.table_name, sum(b.sum_bytes) TOT_OBJECT_SIZE_MB, EST_ONE_ROW
from dba_tables#DB1 a, V_SEG_DATA b
where a.table_name = b.segment_name
and a.table_name in
(select table_name from table)
and a.owner in (select distinct schema from table c)
group by a.owner,a.table_name
order by a.table_name
)
loop
execute immediate' select count(*) from ' ||rws.owner||'.'||rws.table_name || '#' || l_dblink into TOTAL_ROW_COUNT;
if rws.TOT_OBJECT_SIZE_MB then
EST_ONE_ROW := TOTAL_ROW_COUNT / rws.TOT_OBJECT_SIZE_MB;
else
EST_ONE_ROW := null;
end if;
utl_file.put_line(file_handle,
rws.OWNER || ',' ||
rws.TABLE_NAME || ',' ||
rws.TOT_OBJECT_SIZE_MB || ',' ||
TOTAL_ROW_COUNT || ',' ||
EST_ONE_ROW
);
end loop;
utl_file.fclose(file_handle);
end THANOS;
Well, the way the procedure looks now (i.e. code you posted), there's no way that EST_ONE_ROW is anything but NULL. Its value is calculated at the beginning of the procedure, when both TOTAL_ROW_COUNT and TOT_OBJECT_SIZE_MB are NULL as well.
See if it helps if you put
EST_ONE_ROW := (TOTAL_ROW_COUNT / TOT_OBJECT_SIZE_MB);
into the loop, right before the UTL_FILE.put_line call as - at that moment - variables used to calculate its value probably aren't NULL any more.

ORA-06512: at .MAX_MIN_HIST", line 37 ORA-06512: at line 1

create or replace procedure MAX_min_hist
as
sql_stmt varchar2(4000);
sql_stmt2 varchar2(4000);
sql_stmt3 varchar2(4000);
var_date_of_trade hist_data.date_of_trade%type;
sql_stmt4 varchar2(4000);
cursor c1 is SELECT date_of_trade FROM hist_data where date_of_trade>TO_DATE('2015-07-01','YYYY-MM-DD');
begin
open c1;
Loop
fetch c1 into var_date_of_trade;
sql_stmt3 := 'CREATE OR REPLACE VIEW V_HIST_DATA AS SELECT * FROM HIST_DATA WHERE DATE_OF_TRADE < ' || var_date_of_trade;
sql_stmt := 'create table MAX_min_hist_data as ( select * from (
SELECT
CODE,
((CLOSE_PRICE-"Worst")/nullif("Worst",0))*100 Upper_from_down,
(("Best"-CLOSE_PRICE)/nullif(CLOSE_PRICE,0))*100 Down_from_Best,
"Worst",
"Best",
Close_Price
from (
SELECT
DISTINCT
CODE,
MIN(close_price) OVER (PARTITION BY CODE) "Worst",
MAX(close_price) OVER (PARTITION BY CODE) "Best",
first_value(close_price) over (PARTITION BY CODE order by date_of_trade desc) CLOSE_PRICE
FROM V_HIST_DATA
)
)
)';
sql_stmt2 := ' drop table MAX_min_hist_data';
execute immediate sql_stmt3;
execute immediate sql_stmt2;
execute immediate sql_stmt;
end loop;
end;
/
exec MAX_min_hist
Execution of above code gives me the following errors-
ORA-00933: SQL command not properly ended
ORA-06512: at MAX_MIN_HIST", line 37
ORA-06512: at line 1
DATE_OF_TRADE is in 'YYYY-MM-DD' FORMAT .
thanks in advance
Replace this line:
sql_stmt3 := 'CREATE OR REPLACE VIEW V_HIST_DATA AS SELECT * FROM HIST_DATA WHERE DATE_OF_TRADE < ' || var_date_of_trade;
with this one:
sql_stmt3 :=
'CREATE OR REPLACE VIEW V_HIST_DATA AS '
|| 'SELECT * FROM HIST_DATA '
|| 'WHERE DATE_OF_TRADE < to_date( '''
|| to_char( var_date_of_trade, 'yyyy-mm-dd' )
|| ''', ''yyyy-mm-dd'' ) ';

Resources