Surround date with single quote in dynamic SQL Oracle - oracle

I am having this piece of code in an oracle package,
PROCEDURE GET_LOGIN
(
PIN_FROM_DATE IN DATE DEFAULT NULL
,PIN_TO_DATE IN DATE DEFAULT NULL
,CV_1 OUT SYS_REFCURSOR
)
AS
LV_SQL long ;
LV_SQL_DATE long ;
BEGIN
LV_SQL_DATE:='';
IF (PIN_FROM_DATE IS NOT NULL AND PIN_TO_DATE IS NOT NULL)
THEN
LV_SQL_DATE := ' WHERE TRUNC(LOGIN_DATE) BETWEEN '|| TO_DATE(PIN_FROM_DATE, 'yyyy-MM-dd') ||' AND '|| TO_DATE(PIN_TO_DATE, 'yyyy-MM-dd') ||' ';
END IF;
LV_SQL := 'SELECT * FROM LOGIN_DATA '||LV_SQL_DATE;
OPEN CV_1 FOR LV_SQL;
END GET_LOGIN;
I am passing the from date and to date to this PROCEDURE and running a query with between clause.
The default date format is this
select sysdate from dual; 14-FEB-18
LV_SQL_DATE := ' WHERE TRUNC(LOGIN_DATE) BETWEEN '|| TO_DATE(PIN_FROM_DATE, 'yyyy-MM-dd') ||' AND '|| TO_DATE(PIN_TO_DATE, 'yyyy-MM-dd') ||' ';
above query concatenates the converted date with sql string.
which will be like
WHERE TRUNC(LOGIN_DATE) BETWEEN 14-FEB-18 AND 14-FEB-18 ;
which will throw and error, because its expects like this
WHERE TRUNC(LOGIN_DATE) BETWEEN '14-FEB-18' AND '14-FEB-18' ;
How do i concatenate the date conversion with single quotes ?

Its Quite simple.
you have this code :
LV_SQL_DATE := ' WHERE TRUNC(LOGIN_DATE) BETWEEN '|| TO_DATE(PIN_FROM_DATE, 'yyyy-MM-dd') ||' AND '|| TO_DATE(PIN_TO_DATE, 'yyyy-MM-dd') ||' ';
Change it to:
LV_SQL_DATE := ' WHERE TRUNC(LOGIN_DATE) BETWEEN '''|| TO_DATE(PIN_FROM_DATE, 'yyyy-MM-dd') ||''' AND '''|| TO_DATE(PIN_TO_DATE, 'yyyy-MM-dd') ||''' ';
Difference is : use ''' instead of ' before appending the dates
This just answers your question of concatenating.

The variables PIN_FROM_DATE,PIN_TO_DATE are already of type date - so there is no need to convert them to date again.
I think that you could use something like this:
LV_SQL_DATE := 'WHERE TRUNC(LOGIN_DATE) BETWEEN :my_PIN_FROM_DATE and :my_PIN_TO_DATE' ;
And then
OPEN CV_1 FOR LV_SQL using PIN_FROM_DATE,PIN_TO_DATE;

I think you won't need dynamic SQL here. your final CURSOR is equivalent to.
OPEN CV_1 FOR SELECT *
FROM login_data
WHERE ( CASE
WHEN pin_from_date IS NOT NULL
AND pin_to_date IS NOT NULL THEN
CASE
WHEN TRUNC (login_date) BETWEEN pin_from_date AND pin_from_date
THEN 1
ELSE 0
END
ELSE 1
END ) = 1

Related

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';

Not able to execute with clause in dynamic sql

I'm trying to execute below statement using dynamic SQL but I'm getting this exception
"ORA-00911:
invalid character on ORA-06512".
When i remove semicolon(;) the query runs but it's taking a long time and is not able to display output.
How do I clear this problem? Is anywhere I'm doing wrong? if so please guide me.
Below is my code:
DECLARE
mapping_rule VARCHAR2 (10000)
:= 'SALES_REVIEW.NET_VALUE-SALES_REVIEW.GROSS_VALUE+SALES_REVIEW_TABLE.PLNT';
v_mapp_rule VARCHAR2 (10000);
v_mapp_rule_1 VARCHAR2 (10000);
v_chk_flag CHAR (1) := 'Y';
v_mapping_rule VARCHAR2 (10000);
v_str VARCHAR2 (30000);
BEGIN
<<dest_stmt>>
DBMS_OUTPUT.put_line (v_chk_flag);
IF v_chk_flag = 'Y'
THEN
v_mapping_rule := mapping_rule;
ELSE
v_mapping_rule := v_mapp_rule_1;
END IF;
v_str :=
'WITH sel_col (rowno, mapp_rule)
AS ( SELECT ROWNUM rowno,
REGEXP_SUBSTR ( ''' || v_mapping_rule
|| ''',''([+--*!#/#$%^&()=<>,?]+|[A-Z0-9_.'''']+|\s+)'',
1,
LEVEL)
mapp_rule
FROM DUAL
CONNECT BY REGEXP_SUBSTR ('''
|| v_mapping_rule
|| ''',''([+--*!#/#$%^&()=<>,?]+|[A-Z0-9_.'''']+|\s+)'',
1,
LEVEL)
IS NOT NULL)
SELECT listagg (
CASE WHEN B.MAPP_RULE IS NOT NULL THEN B.MAPP_RULE
ELSE D.MAPP_RULE END,
'''')
WITHIN GROUP (ORDER BY d.rowno)
FROM RRR_PROCESS_DTLS a
JOIN RRR_PROCESS_MAPPING_DTLS b
ON A.PROCESS_ID = B.PROCESS_ID
AND A.COMPANY_ID = B.COMPANY_ID
JOIN RRR_DEST_TABLE_DTLS C
ON A.DEST_TABLE = C.TABLE_ID
AND A.COMPANY_ID = C.COMPANY_ID
RIGHT JOIN sel_col d
ON CONCAT (CONCAT (C.TABLE_NAME, ''.''), B.DEST_COLUMN) =
d.mapp_rule
AND A.PROCESS_ID = 12
AND A.COMPANY_ID = 2
ORDER BY d.rowno;';
DBMS_OUTPUT.put_line (v_str);
EXECUTE IMMEDIATE v_str into v_mapp_rule_1 ;
DBMS_OUTPUT.put_line ('bf ' || v_mapp_rule_1);
IF v_mapp_rule_1 IS NOT NULL
THEN
v_chk_flag := 'N';
GOTO dest_stmt;
END IF;
DBMS_OUTPUT.put_line (v_mapp_rule_1);
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE (
SQLCODE
|| ' -ERROR- '
|| SQLERRM
|| ' on '
|| DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
END;

How do I get the data from the column_name? Oracle PL/SQL

How do I get the data(i.e rows) from the column_name I retrieved from SQL statement? (Completely new to PL/SQL).
Here is my code:
create or replace procedure com_coll_cur
as
type comColcur is ref cursor;
com_col_cur comColcur;
sql_stmt varchar2(4000);
type newtab_field is table of comColcur%TYPE;
begin
sql_stmt :=
'select column_name from all_tab_cols where table_name in (''TAB1'', ''TAB2'') ' ||
'group by column_name having count(*)>1';
open com_col_cur for sql_stmt;
loop
fetch com_col_cur into newtab_field;
exit when com_col_cur%NOTFOUND;
end loop;
close com_col_cur;
end;
What I'm trying to do here is first find the common columns between the two tables. This part only grabs column_name but I also want the data in that common columns. So I used cursor to "point" that common column_name and used loop(fetch) to get the data inside that common column_name. Finally, I want to create new table with this common columns only with their data.
I am new to everything here and any help will be appreciate it.
You don't understand how works cursors and fetch.
Fetch get the data from the cursor, so in your procedure example you get only column names, not the data in the columns. To get data you need another cursor - select from the target table or use the dynamic sql.
This is a code that do what you describe. It is not clear to me how you want to store data from two tables - subsequently or in another manner. Let's assume that we store them subsequently. Also this code suggests than columns with the same names have the same datatypes. Part of this code (to make datatype) I get from another stackoverflow post to save time to write it:
How do I get column datatype in Oracle with PL-SQL with low privileges?
dbms_output.put_line - used to print sql statements that we create
declare
cSql varchar2(4000);
cCols varchar2(4000);
cNewTableName varchar2(30) := 'AABBCC';
cTb1 varchar2(30) := 'TAB1';
cTb2 varchar2(30) := 'TAB2';
begin
for hc in (
select T.column_name, T.typ
from
(
select column_name,
data_type||
case when data_precision is not null and nvl(data_scale,0)>0 then '('||data_precision||','||data_scale||')'
when data_precision is not null and nvl(data_scale,0)=0 then '('||data_precision||')'
when data_precision is null and data_scale is not null then '(*,'||data_scale||')'
when char_length>0 then '('||char_length|| case char_used
when 'B' then ' Byte'
when 'C' then ' Char'
else null
end||')'
end||decode(nullable, 'N', ' NOT NULL') typ
from all_tab_cols
where table_name in (cTb1, cTb2) ) T
group by T.column_name, T.typ having count(*) > 1)
loop
cSql := cSql || case when cSql is null then null else ',' end || hc.column_name || ' ' || hc.typ;
cCols := cCols || case when cCols is null then null else ',' end || hc.column_name;
end loop;
if (cSql is not null) then
-- First drop table if it exists
for hc in (select * from all_objects where object_type = 'TABLE' and object_name = cNewTableName)
loop
execute immediate 'drop table ' || hc.object_name;
end loop;
-- create table
cSql := 'create table ' || cNewTableName || '(' || cSql || ')';
dbms_output.put_line(cSql);
execute immediate cSql;
-- insert data
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb1;
dbms_output.put_line(cSql);
execute immediate cSql;
cSql := 'insert into ' || cNewTableName || '(' || cCols || ') select ' || cCols || ' from ' || cTb2;
dbms_output.put_line (cSql);
execute immediate cSql;
end if;
end;

Sort Nested table based on dynamic information

I am having trouble sorted a nested table based on some dynamic information that would be in the order by clause.
Here is a sample of what I have found (https://technology.amis.nl/2006/05/31/sorting-plsql-collections-the-quite-simple-way-part-two-have-the-sql-engine-do-the-heavy-lifting/)
The only difference here is I need to dynamically define the column and direction in the order by clause
SELECT CAST(MULTISET(SELECT *
FROM TABLE(table_a)
ORDER BY P_SORT_COLUMN P_DIRECTION
) as table_typ)
INTO table_b
FROM dual;
So to get around think I thought of using dynamic SQL and put it in a proc as forms cannot do this dynamically
loc_sql_stmt VARCHAR2(500);
BEGIN
loc_sql_stmt := 'SELECT CAST(MULTISET(SELECT * ' ||
'FROM TABLE(P_TABLE_A) ' ||
'ORDER BY P_COLUMN P_DIRECTION || ) as table_typ) ' ||
'INTO P_TABLE_B' ||
'FROM dual;';
EXECUTE IMMEDIATE loc_sql_stmt
USING IN P_TABLE_A, P_COLUMN, P_DIRECTION, P_TABLE_B;
END;
There error I get from the EXECUTE IMMEDIATE line is "ORA-00936 missing expression
So is there a better way to sort a nest table by any given column and the direction or how do I get this dynamic SQL to work?
Here is a sample:
create this in DB:
CREATE OR REPLACE TYPE table_obj AS OBJECT(
column1 VARCHAR2(20),
column2 VARCHAR2(20));
CREATE OR REPLACE TYPE table_typ AS TABLE OF table_obj;
and then a sample run:
DECLARE
table_a table_typ := table_typ ();
table_b table_typ := table_typ ();
loc_idx NUMBER;
loc_sort_column INTEGER := 1;
loc_desc VARCHAR2 (4);
P_SORT_COLUMN VARCHAR2 (100) := 'column1';
P_DIRECTION VARCHAR2 (4) := 'DESC';
loc_sql_stmt VARCHAR2 (500);
BEGIN
FOR i IN 1 .. 5
LOOP
loc_idx := table_a.COUNT + 1;
table_a.EXTEND;
table_a (loc_idx) := table_obj (NULL, NULL);
table_a (loc_idx).column1 := TO_CHAR (loc_idx);
table_a (loc_idx).column2 := TO_CHAR (loc_idx);
END LOOP;
--
loc_sql_stmt :=
'SELECT CAST(MULTISET(SELECT * ' ||
'FROM TABLE(' || table_a || ') ' ||
'ORDER BY ' || P_SORT_COLUMN || ' '|| P_DIRECTION ||
' ) as table_typ) ' ||
'INTO :table_b' ||
'FROM dual';
EXECUTE IMMEDIATE loc_sql_stmt USING IN OUT table_a, table_b;
FOR i IN 1 .. table_b.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE (table_b (i).rx_number);
END LOOP;
END;
To pass variable to native dynamic SQL use : before parameter name, to build dynamic statement use concatenation, like this
loc_sql_stmt VARCHAR2(500);
BEGIN
loc_sql_stmt := 'SELECT CAST(MULTISET(SELECT * ' ||
'FROM TABLE('|| P_TABLE_A || ') ' ||
'ORDER BY ' || P_COLUMN || ', ' || P_DIRECTION || ' ) as table_typ) ' ||
'INTO :P_TABLE_B' ||
'FROM dual;';
EXECUTE IMMEDIATE loc_sql_stmt
USING OUT P_TABLE_B;
END;
EDITED version:
Now seeing your code I understand what you need. To make it work we need to use dynamic PL/SQL block, not Native SQL here is working code of your sample, and pay attention to what is variable and what is concatenated literal
DECLARE
table_a table_typ := table_typ();
table_b table_typ := table_typ();
loc_idx NUMBER;
loc_sort_column INTEGER := 1;
loc_desc VARCHAR2(4);
P_SORT_COLUMN VARCHAR2(100) := 'column1';
P_DIRECTION VARCHAR2(4) := 'desc';
loc_sql_stmt VARCHAR2(500);
BEGIN
FOR i IN 1 .. 5
LOOP
loc_idx := table_a.COUNT + 1;
table_a.EXTEND;
table_a(loc_idx) := table_obj(NULL, NULL);
table_a(loc_idx).column1 := TO_CHAR(loc_idx);
table_a(loc_idx).column2 := TO_CHAR(loc_idx);
END LOOP;
--
loc_sql_stmt := 'begin SELECT CAST(MULTISET(SELECT * ' ||
'FROM TABLE(:table_a ) ORDER BY ' || P_SORT_COLUMN || ' ' ||
P_DIRECTION || ' ) as table_typ ) ' || ' INTO :table_b ' ||
'FROM dual; end;';
EXECUTE IMMEDIATE loc_sql_stmt
USING table_a, IN OUT table_b;
FOR i IN 1 .. table_b.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE(table_b(i).column1);
END LOOP;
END;
If you have limited column/direction choices, try a case statement in your order by, simple example:
select * from tab
order by case when :order = 'c1_asc' then c1 else null end asc
, case when :order = 'c1_desc' then c1 else null end desc
, case when :order = 'c2_asc' then c2 else null end asc
, case when :order = 'c2_desc' then c2 else null end desc
/* ... */
;

Need to use dynamic variables in where clause used in EXECUTE IMMEDIATE statement in ORACLE

ORACLE (using SQL DEVELOPER).
I need to properly structure EXECUTE IMMEDIATE statement.
I do not have "create" priveleges. The task is to get number of rows per table per date for dynamic list of tables/dates.
I have the following:
DECLARE CURSOR cur_table_name IS SELECT TABLE_NAME
FROM ALL_TABLES WHERE TABLE_NAME IN ('table_a', 'table_b', 'table_c');
CURSOR cur_BEGIN_DATE IS
select to_date('2014-09-25 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + rownum -1 AS BEGIN_DATE,
to_date('2014-09-26 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + rownum -1 AS END_DATE from dual
Connect by level <= to_date('2014-09-30 00:00:00', 'YYYY-MM-DD HH24:MI:SS') - to_date('2014-09-25 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + 1;
var_total_rows NUMBER(15);
var_table_name VARCHAR2 (50);
var_bgn_date DATE;
var_end_date DATE;
BEGIN
OPEN cur_TABLE_NAME;
LOOP
FETCH cur_TABLE_NAME INTO var_table_name;
EXIT WHEN cur_TABLE_NAME%NOTFOUND;
--testing output
DBMS_OUTPUT.PUT_LINE ('Table: '|| var_table_name);
var_total_rows :=0;
OPEN cur_BEGIN_DATE;
LOOP
FETCH cur_BEGIN_DATE INTO var_bgn_date, var_end_date;
EXIT WHEN cur_BEGIN_DATE%NOTFOUND;
--TESTING OUTPUT
DBMS_OUTPUT.PUT_LINE ('DATES ARE: ' || var_bgn_date || ', ' ||var_end_date|| ' Table IS: '||var_table_name);
--------THIS IS THE NOT WORKING STATEMENT DUE TO VARIABLES IN THE WHERE STATEMENT:
execute immediate 'SELECT COUNT(*) FROM '||var_table_name || ' where DTM >= '|| var_bgn_date ||' and DTM < '||var_end_date INTO var_total_rows;
DBMS_OUTPUT.PUT_LINE (var_table_name||' '||var_bgn_date||' '||var_end_date ||' '||var_total_rows);
END LOOP;
CLOSE cur_BEGIN_DATE;
END LOOP;
CLOSE cur_TABLE_NAME;
END;
If I remove the variables from the where statement (just do 'Select * from || var_table_name into var_total_rows; ) this works. And if there is a static value in the where clause - it works (but loops through with the same date and I need the changing dates!). But I cannot make the syntax to work for dynamic variables in the where clause.
Can this be done?
Appreciate your help!
Your var_bgn_date and var_end_date variables are of DATE type, but are being plugged into the dynamic statement as unquoted strings with implicit formatting based on the session NLS_DATE_FORMAT value. You'll get a generated statement like:
SELECT COUNT(*) FROM table_a where DTM >= 2014-09-25 00:00:00 and DTM < 2014-09-26 00:00:00
You could add escaped single quotes to turn that into a valid statement, still relying on implicit conversion back using the same NLS settings:
EXECUTE immediate 'SELECT COUNT(*) FROM '||var_table_name
|| ' where DTM >= '''|| var_bgn_date ||''' and DTM < '''||var_end_date ||''''
INTO var_total_rows;
which would generate:
SELECT COUNT(*) FROM table_a where DTM >= '2014-09-25 00:00:00' and DTM < '2014-09-26 00:00:00'
But really you should use bind variables to avoid any conversion to or from strings:
EXECUTE immediate 'SELECT COUNT(*) FROM '||var_table_name
|| ' where DTM >= :bgn_date and DTM < :end_date'
INTO var_total_rows
USING var_bgn_date, var_end_date;

Resources