I've written a piece of SQL script to move all indexes inside a tablespace to other one, the code is OK and working fine. but I am confused about partitioned indexes that why some of them are inside DBA_SEGMENTS and some of them are inside DBA_IND_PARTITIONS and why the ones are inside DBA_IND_PARTITIONS are not inside DBA_SEGMENTS.
Could someone explain the difference and usage of these tables in Oracle
This is my script to move indexes from one tablespace to other
DECLARE
-- SETTING
SOURCE_TS VARCHAR2(30) := 'source_tablespace'; -- SOURCE TABLESPACE
DEST_TS VARCHAR2(30) := 'destination_tablespace'; --DESCTINATION TABLESPACE
-- SHOULD_DROPINDEX BOOLEAN := FALSE;
----------------------------------------------
TOP NUMBER := 2;
COUNTER NUMBER := 0;
SHOULD_MOVEINDEX BOOLEAN := TRUE;
PARALLEL_DEGREE NUMBER := 4;
ENABLE_PARALLEL BOOLEAN := TRUE;
INDEX_NAMES_TO_SCAPE VARCHAR2(1000) := '';
----------------------------------------------
-- VARIABLES
SQLCMD VARCHAR2(1000);
STARTTIME NUMBER;
BEGIN
STARTTIME := DBMS_UTILITY.GET_TIME();
DBMS_OUTPUT.PUT_LINE('STARTING TIME : ' ||
TO_CHAR(SYSDATE, 'YYYY/MM/DD HH24:MI:SS'));
DBMS_OUTPUT.PUT_LINE('----------------------------------------------------');
FOR IDXS IN (SELECT OWNER || '.' || INDEX_NAME FN,
INDEX_NAME IDN,
INDEX_TYPE IDT,
PARTITIONED,
(CASE
WHEN UNIQUENESS = 'UNIQUE' THEN
'Y'
ELSE
'N'
END) ISUNIQUE,
OWNER
FROM DBA_INDEXES
WHERE (TABLESPACE_NAME = SOURCE_TS OR
INDEX_NAME IN
(SELECT DISTINCT SEGMENT_NAME
FROM DBA_SEGMENTS
WHERE TABLESPACE_NAME = SOURCE_TS) OR
INDEX_NAME IN
(SELECT DISTINCT INDEX_NAME
FROM DBA_IND_PARTITIONS
WHERE TABLESPACE_NAME = SOURCE_TS))
ORDER BY OWNER ASC, INDEX_NAME ASC
) LOOP
IF COUNTER >= TOP THEN
DBMS_OUTPUT.PUT_LINE('EXIT ON COUNTER EXCEED');
EXIT;
END IF;
IF INSTR(INDEX_NAMES_TO_SCAPE, IDXS.IDN) >= 1 THEN
CONTINUE;
END IF;
IF IDXS.PARTITIONED = 'YES' THEN
FOR PIDX IN (SELECT *
FROM DBA_IND_PARTITIONS
WHERE INDEX_OWNER = IDXS.OWNER
AND INDEX_NAME = IDXS.IDN
AND TABLESPACE_NAME = SOURCE_TS) LOOP
-- ** EXECUTE LOCAL (PARTITIONED) INDEX REBUILD COMMAND **
SQLCMD := 'ALTER INDEX ' || IDXS.FN || ' REBUILD PARTITION ' ||
PIDX.PARTITION_NAME || ' TABLESPACE ' || DEST_TS;
IF ENABLE_PARALLEL = TRUE THEN
SQLCMD := SQLCMD || ' PARALLEL ' || PARALLEL_DEGREE;
END IF;
--SQLCMD := SQLCMD || ' NONLOGGING ';
DBMS_OUTPUT.PUT_LINE(SQLCMD);
EXECUTE IMMEDIATE SQLCMD;
END LOOP;
IF ENABLE_PARALLEL = TRUE THEN
SQLCMD := 'ALTER INDEX ' || IDXS.FN || ' NOPARALLEL ';
DBMS_OUTPUT.PUT_LINE(SQLCMD);
EXECUTE IMMEDIATE SQLCMD;
END IF;
ELSE
-- ** EXECUTE INDEX REBUILD COMMAND **
SQLCMD := 'ALTER INDEX ' || IDXS.FN || ' REBUILD TABLESPACE ' ||
DEST_TS;
IF ENABLE_PARALLEL = TRUE THEN
SQLCMD := SQLCMD || ' PARALLEL ' || PARALLEL_DEGREE;
END IF;
--SQLCMD := SQLCMD || ' NONLOGGING ';
DBMS_OUTPUT.PUT_LINE(SQLCMD);
EXECUTE IMMEDIATE SQLCMD;
IF ENABLE_PARALLEL = TRUE THEN
SQLCMD := 'ALTER INDEX ' || IDXS.FN || ' NOPARALLEL ';
DBMS_OUTPUT.PUT_LINE(SQLCMD);
EXECUTE IMMEDIATE SQLCMD;
END IF;
END IF;
COUNTER := COUNTER + 1;
DBMS_OUTPUT.PUT_LINE('#');
END LOOP;
DBMS_OUTPUT.PUT_LINE('----------------------------------------------------');
DBMS_OUTPUT.PUT_LINE('END TIME : ' ||
TO_CHAR(SYSDATE, 'YYYY/MM/DD HH24:MI:SS'));
DBMS_OUTPUT.PUT_LINE('DURATION: ' ||
TO_CHAR(DBMS_UTILITY.GET_TIME() - STARTTIME) ||
' MS.');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ERR MSG: ' || SQLERRM || ', CODE: ' || SQLCODE);
END;
Related
I have Billion's of records to updated and inserted to tables. i have created a procedure it's working but it's taking 10 min for 100k to updated, to reduce time i have used bulk collect and for all method in oracle. it's given me an error.
ERROR - Error(50,12): PLS-00435: DML statement without BULK In-BIND cannot be used inside FORALL
I have tried this Procedure but it's given the following error. how do i resove this error?
ERROR - Error(50,12): PLS-00435: DML statement without BULK In-BIND cannot be used inside FORAL
create or replace PROCEDURE bulkProcedure AS
start_time pls_integer;
CURSOR address_cursor_rs IS select * from cursor_temp;
TYPE address_ids_t IS TABLE OF add_bulk.addr_id%TYPE
INDEX BY PLS_INTEGER;
address_ids address_ids_t;
fetched address_ids_t;
cursor_row address_cursor_rs%ROWTYPE;
sq VarChar2(1) := '''';
mSql VarChar2(512) := '';
singleRowAddressSql VarChar2(512) := '';
mID add_bulk.addr_id%TYPE;
mAddr add_bulk.ADDRESS%TYPE;
mKey add_bulk.HASHKEY%TYPE;
address_rec add_bulk%ROWTYPE;
BEGIN
start_time := dbms_utility.get_time;
mId := 13563;
OPEN address_cursor_rs;
LOOP
FETCH address_cursor_rs INTO cursor_row;
EXIT WHEN address_cursor_rs%NOTFOUND;
mID := mID + 1;
singleRowAddressSql:= 'select * from add_bulk where ADDRESS =' || sq || cursor_row.ADDRESS || sq || 'AND HASHKEY= ' ||cursor_row.HASHKEY || ' FETCH FIRST ROWS ONLY';
Execute Immediate singleRowAddressSql INTO address_rec;
mSql := 'INSERT INTO add_bulk(addr_id, address, HASHKEY, ver_id ) VALUES (' || mID || ', ' || sq || address_rec.ADDRESS || sq || ', ' || address_rec.HASHKEY || ', -1)';
Execute Immediate mSql;
SELECT mSql
BULK COLLECT INTO address_ids
FROM add_bulk;
Begin
FORALL indx IN 1 .. address_ids.COUNT
UPDATE info_bulk SET addr_id = mID WHERE addr_id IN(Select addr_id FROM add_bulk WHERE ADDRESS = address_rec.ADDRESS And HASHKEY = address_rec.HASHKEY And VER_ID > 0)
and VER_ID IN (
Select VER_ID FROM add_bulk WHERE ADDRESS = address_rec.ADDRESS And HASHKEY = address_rec.HASHKEY And VER_ID > 0);
DELETE FROM add_bulk WHERE addr_id IN(Select addr_id FROM add_bulk WHERE ADDRESS = address_rec.ADDRESS And HASHKEY = address_rec.HASHKEY
And VER_ID > 0)and HASHKEY = address_rec.HASHKEY;
Commit;
Exception
WHEN OTHERS THEN
Rollback;
DBMS_OUTPUT.PUT_LINE('ERR - UPDATE or DELETE ' || Chr(10) || SQLERRM);
End;
END LOOP;
dbms_output.put_line((dbms_utility.get_time - start_time)/100 || ' seconds');
CLOSE address_cursor_rs;
Exception
WHEN OTHERS THEN
Rollback; -- if anything went wrong - Rollback and send a message
DBMS_OUTPUT.PUT_LINE('ERR - INSERT ' || Chr(10) || SQLERRM);
END;
I'm trying to write a PL/SQL script that searches the entire database for a string and report the tables and columns it finds it in. It looks like this:
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000) := 'result: ';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT a.table_name, a.column_name FROM all_tab_columns a WHERE a.data_type LIKE '%VARCHAR%')
LOOP
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcolumnname || ' = :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, vcol || ' ' || vtab || ', ');
END IF;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
When I run it, I get the following error:
It is essentially saying it doesn't recognize the table vtab in the line EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr.
So in order to see which table it's complaining about, I added the following exception block to the end of the script:
EXCEPTION
WHEN OTHERS
THEN
BEGIN
dbms_output.put_line('exception: ' || vtab);
END;
It tells me the table name is IND$.
I'm not sure what this table (or view) is and it doesn't look relevant anyway.
So my question is two fold: 1) If it is fetching IND$ in the FOR loop from k.table_name (which in turn is from all_tab_columns), why does it say it doesn't exist in the select query? 2) I'm not sure what IND$ is but I'm pretty sure I don't need to search it; So is there a way to limit my search to only relevant tables (not views)? By 'relevant', I mean tables that we created to store data for our application (as opposed to system tables or user tables, etc.).
Thanks very much.
all_tab_columns lists columns in all tables that your current schema has access to. In general, if you're going to use ALL_... views to build dynamic SQL, you should incorporate the OWNER column as well since it may return rows for tables in other schemas.
If you really only want to consider tables in your current schema, use user_tab_columns instead.
If you want to get more, or less, particular than that about which tables are 'relevant', then you'll probably need to hardcode specific rules into your query.
FYI: IND$ is part of the Oracle data dictionary and is in the SYS schema. (Although, it's always possible someone has created a table with that name in some other schema.) It's unusual that your application schema would have direct access to this.
If you know how to skip tables you don't want, do it - you might filter by OWNER, maybe table name, etc.
If you don't want to bother, include inner begin-exception-end block into the loop so that it skips errors (either silently, or display errors, or store them into some table). Here's one option:
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000) := 'result: ';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT a.table_name, a.column_name FROM all_tab_columns a WHERE a.data_type LIKE '%VARCHAR%')
LOOP
BEGIN
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcolumnname || ' = :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from ' || vtab || vwhere INTO ncount USING vsearctstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, vcol || ' ' || vtab || ', ');
END IF;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(vtab ||': '|| sqlerrm);
END;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
You need to:
Also get the owner from all_tab_columns
Use quoted identifiers around the owner, table_name and column_name so that the query does not fail for identifiers in different cases or with special characters.
Handle errors (i.e. when you do not have the permissions to SELECT from a table).
To make it faster, you can:
Filter the columns to only those that are long enough to contain the search string.
You can do that using:
DECLARE
ncount NUMBER;
vsearchstr VARCHAR2(1000) := 'search string here';
vresult VARCHAR2(10000);
BEGIN
FOR k IN (SELECT owner,
table_name,
column_name
FROM all_tab_columns
WHERE data_type LIKE '%VARCHAR%'
AND data_length >= LENGTH(vsearchstr))
LOOP
BEGIN
EXECUTE IMMEDIATE
'select count(1)
from "' || k.owner || '"."' || k.table_name || '"
where "' || k.column_name || '" = :1'
INTO ncount
USING vsearchstr;
IF (ncount > 0)
THEN
vresult := vresult || ','
|| k.owner || '.' || k.table_name || '.' || k.column_name;
END IF;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
END LOOP;
IF (LENGTH(vresult) > 1)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('not found');
END IF;
END;
/
db<>fiddle here
Here is the final query (which works):
DECLARE
ncount NUMBER;
vwhere VARCHAR2(1000) := '';
vsearchstr VARCHAR2(1000) := '%your string here%';
vresult VARCHAR2(10000) := '';
vtab VARCHAR2(1000) := '';
vcol VARCHAR2(1000) := '';
BEGIN
FOR k IN (SELECT table_name, column_name
FROM all_tab_columns
WHERE data_type LIKE '%VARCHAR%'
AND data_length >= LENGTH(vsearchstr)
AND table_name NOT IN (SELECT view_name FROM all_views))
LOOP
BEGIN
vtab := k.table_name;
vcol := k.column_name;
vwhere := ' where ' || vcol || ' like :vsearchstr ';
EXECUTE IMMEDIATE 'select count(1) from OWNER.' || vtab || vwhere INTO ncount USING vsearchstr;
IF (ncount > 0)
THEN
vresult := CONCAT(vresult, 'table: ' || vtab || ', column: ' || vcol || chr(13) || chr(10));
END IF;
EXCEPTION
WHEN OTHERS THEN NULL;
END;
END LOOP;
IF (LENGTH(vresult) > 0)
THEN
dbms_output.put_line(vresult);
ELSE
dbms_output.put_line('string not found');
END IF;
END;
I took Littlefoot's advice and added an exception block to catch the exception when the table is not found and just continue with the loop. I also took MT0's advice and checked the length of the column in the select query. I also included the owner in the select query.
This works. Thanks to everyone who gave their advice.
I'm still a relatively newbe when it comes to PL/SQL.
Using Oracle 12c on Linux RHEL 6.8, the following shell script will attempt to activate all RI constraints in a collection of tables, and if they fail with parent key failures, it will dump the first 100 rows (or less) of the offending data. Or at least that is the goal. Since the script deals mostly with system tables on 12c (with only a small user table list that is unique to my installation), I'm including the whole thing exactly from my environment.
The main work occurs in the exception handling where the system tables are queried for the constraint, and user queries are formed from those data.
As a extra goal, the output is rather messy and I want to clean it up, but first it has to work :)
The output / error I get for my tables is the following:
ERROR Handling here for table NRNG_MTC_VST Constraint Name:
SYS_C0011790 Final SQL = SELECT DISTINCT NRNG_MTC_VST.LOG_CRT_DT ,
NRNG_MTC_VST.NRRNG_MTC_LG_ID FROM ODB_PRIMARY.NRNG_MTC_VST WHERE NOT
EXISTS (SELECT 1 FROM ODB_PRIMARY.NRNG_MTC_LOG WHERE
NRNG_MTC_VST.LOG_CRT_DT = NRNG_MTC_LOG.LOG_CRT_DT AND
NRNG_MTC_VST.NRRNG_MTC_LG_ID = NRNG_MTC_LOG.NRRNG_MTC_LG_ID) FETCH
FIRST 100 rows only
---xxx End SQL DECLARE
* ERROR at line 1: ORA-01001: invalid cursor ORA-06512: at line 111 ORA-02298: cannot validate (ODB_PRIMARY.SYS_C0011790) - parent keys
not found
The output SQL from my print_line is correct, and would work if pasted directly into a SQLDeveloper session. There is just something silly about how the cursor is defined I don't understand.
The full text of the script. BYW, if you see other bonehead changes that should be made unrelated to the error, please suggest them as well.
cd $OGGHOME/scripts
export ORACLE_SID=odbod07 $ORACLE_HOME/bin/sqlplus <<-EOF / as sysdba
alter session set container=p01_odbod07;
set echo on set feedback on
set heading off
set serveroutput on size 10000
DECLARE finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
-- Weak cursor defs
my_cursor sys_refcursor;
BEGIN FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = 'ODB_PRIMARY'
and TABLE_NAME in
-- enter user tables with RI constraints here
('RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'CAR_CORE',
'NRNG_MTC_LOG'))
-- end user table definitions, rest of code should rely only on system tables
LOOP BEGIN
dbms_output.put_line('alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' ||
i.table_name || ' enable constraint '||i.constraint_name;
EXCEPTION
-- exception handling - dump offending data
WHEN OTHERS THEN -- take all exceptions for now
dbms_output.put_line ('ERROR Handling here for table ' ||
i.table_name || ' Constraint Name: ' ||i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME , uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable ||
'.'||constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.'
|| constraint.childcolumn || ' = '
|| constraint.parenttable || '.' ||
constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql ||
' FROM ' || ' ' || cownername ||
'.' || ctablename ||
' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename ||
' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = ' || finalsql);
dbms_output.put_line ('---xxx End SQL');
open my_cursor for finalsql;
dbms_sql.return_result(my_cursor);
close my_cursor;
-- EXECUTE IMMEDIATE finalsql;
END;
end loop; end;
/
EOF
Many thanks for any help provided.
Brian
Just to narrow this down to a simple test case, I think this is the error you are seeing:
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
close my_cursor; -- << Remove this line
end;
/
ERROR at line 1:
ORA-01001: invalid cursor
ORA-06512: at line 6
This is because you attempted to close the cursor when you have already passed it to dbms_sql for processing. Remove the line with close my_cursor.
declare
my_cursor sys_refcursor;
begin
open my_cursor for 'select ''Hello, world'' as message from dual';
dbms_sql.return_result(my_cursor);
end;
/
PL/SQL procedure successfully completed.
ResultSet #1
MESSAGE
------------
Hello, world
1 row selected.
I had same kind of issue when i tried to print Ref_cursor directly. Then i created a Record type variable and then fetched field values in that variable and then i used DBMS_OUTPUT for that record type variable.
Please see if below code and scenario can help you-
set serveroutput on;
declare
v_sql varchar2(1000);
v_cursor sys_refcursor;
type myrec is record(col1 varchar2(100),col2 varchar2(1000));
rec myrec;
begin
v_sql:='select name,status from t_employee where user_id in (''C001117'',''C001122'')';
open v_cursor for v_sql;
loop
fetch v_cursor
into rec;
exit when v_cursor%notfound;
dbms_output.put_line( rec.col1||':status '||rec.col2 );
end loop;
end;
/
The following is my semi-complete script. Given a table list, it will attempt to activate the RI Constraints, and if they fail it will print out the FK data records in the child table that prevent it from being applied.
The hardest part of this project was the fact that the FKs can be any number of columns and of any type, so the print the results of the select in this case was very tricky (IMO).
Thanks for the help people provided.
cd $OGGHOME/scripts
. ./functions.sh
$ORACLE_HOME/bin/sqlplus ${ORACLE_USERID}/${ORACLE_PASSWORD}#${ORACLE_SID} << EOF
set echo on
set feedback on
set heading off
set serveroutput on size unlimit
DECLARE
finalsql varchar2(2048);
part1sql varchar2(1024) ;
part2sql varchar2(1024) := ' ';
cownername varchar2(1024);
ctablename varchar2(1024);
pownername varchar2(1024);
ptablename varchar2(1024);
cnt number := 0;
desc_tab dbms_sql.desc_tab;
col_count INTEGER;
cursor_name INTEGER;
-- Weak cursor defs
my_cursor sys_refcursor;
col1 varchar2(50);
d number;
j number;
lineout varchar2(2048);
plineout varchar2(2048);
rows number;
eCount number := 0;
BEGIN
FOR i in (
select owner, table_name, constraint_name
from dba_constraints
where constraint_type = 'R'
and status = 'DISABLED'
and owner = '$DBSCHEMA'
and TABLE_NAME in (
'RRNG_MTC_STN_CPLY',
'NRNG_MTC_VST_MTRL_USG',
'NRNG_MTC_VST',
'MTC_TSK_HRHY'))
LOOP
BEGIN
dbms_output.put_line ('.');
dbms_output.put_line ('=====================================');
dbms_output.put('alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name);
execute immediate 'alter table '||i.owner|| '.' || i.table_name || ' enable constraint '||i.constraint_name;
dbms_output.put_line (' ... SUCCESS');
EXCEPTION -- exception handling - dump offending data
WHEN OTHERS THEN
eCount := eCount + 1;
dbms_output.put_line (' ... FAILED. Constraint Name: ' || i.constraint_name);
finalsql := 'SELECT DISTINCT ';
part1sql := '';
part2sql := ' ';
cnt := 0;
for constraint in (
SELECT ucc1.OWNER as childowner,
ucc1.TABLE_NAME as childtable,
ucc1.column_name as childcolumn,
ucc2.OWNER as parentowner,
ucc2.TABLE_NAME as parenttable,
ucc2.column_name as parentcolumn,
utc1.data_type as childdatatype,
utc1.data_length as childdatalen
FROM all_constraints uc ,
all_cons_columns ucc1 ,
all_cons_columns ucc2,
all_tab_columns utc1
WHERE
uc.constraint_name = ucc1.constraint_name
AND uc.r_constraint_name = ucc2.constraint_name
AND ucc1.POSITION = ucc2.POSITION
AND ucc1.table_name = utc1.table_name
AND ucc1.column_name = utc1.column_name
AND uc.constraint_type = 'R'
AND uc.constraint_name = i.constraint_name
ORDER BY ucc1.TABLE_NAME ,
uc.constraint_name)
loop
cownername := constraint.childowner;
ctablename := constraint.childtable;
pownername := constraint.parentowner;
ptablename := constraint.parenttable;
if cnt > 0 then
part1sql := part1sql || ' , ';
part2sql := part2sql || ' AND ';
end if;
part1sql := part1sql || constraint.childtable || '.' || constraint.childcolumn || ' ';
part2sql := part2sql || constraint.childtable || '.' || constraint.childcolumn || ' = '
|| constraint.parenttable || '.' || constraint.parentcolumn;
cnt := cnt + 1;
end loop;
finalsql := finalsql || part1sql || ' FROM ' || ' ' || cownername || '.' || ctablename || ' WHERE NOT EXISTS (SELECT 1 FROM ' ||
pownername || '.' || ptablename || ' WHERE ' || part2sql || ') FETCH FIRST 100 rows only';
dbms_output.put_line ('Final SQL = (' || finalsql || ')');
-- dbms_output.put_line ('---xxx End SQL');
lineout := 'Child Table: ' || ctablename || '(';
plineout := 'Parent Table: ' || ptablename;
cursor_name := dbms_sql.open_cursor;
dbms_sql.PARSE (cursor_name, finalsql, DBMS_SQL.NATIVE);
d := dbms_sql.execute (cursor_name);
dbms_sql.describe_columns (cursor_name, col_count, desc_tab);
for j in 1..col_count
LOOP
DBMS_SQL.DEFINE_COLUMN (cursor_name, j, col1, 30);
lineout := lineout || desc_tab(j).col_name || ' , ';
-- plineout := plineout || constraint.parentcolumn || ' ';
-- dbms_output.put_line ('Column 1: ' || j || ' is ' || desc_tab(j).col_name || ' type '
-- || desc_tab(j).col_type);
END LOOP j;
lineout := lineout || ')';
-- plineout := plineout || ')';
dbms_output.put_line (lineout);
dbms_output.put_line (plineout);
lineout := NULL;
for j in 1..col_count
LOOP
if j > 1 then
lineout := lineout || ' ';
end if;
lineout := lineout || desc_tab(j).col_name;
END LOOP;
dbms_output.put_line (lineout);
dbms_output.put_line ('----------------------------------------');
LOOP
rows := dbms_sql.fetch_rows (cursor_name);
EXIT WHEN rows = 0;
lineout := NULL;
for j in 1..col_count
LOOP
dbms_sql.column_value (cursor_name, j, col1);
if j > 1 then
lineout := ltrim(lineout || ' ' || col1);
else
lineout := col1;
END IF;
END LOOP;
dbms_output.put_line (lineout);
END LOOP;
dbms_sql.close_cursor (cursor_name);
END;
end loop;
end;
/
EOF
your FETCH FIRST 100 rows only would seem to be out of place.
This is part of the BULK COLLECT clause in a SELECT statement in PL/SQL; as far as I know, it is not part of a SQL statement you can pass into a cursor like this
This is resulting in the cursor statement being invalid
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
/* ... */
;
I have a dynamic search query that I would like to convert to make use of bind variables. The dynamic portion of the of query is in the where clause, and using a series of if statements to build a string that is concatenated to the rest of the query string. That query is then used in the for clause of a openstatement, which the result set is the return parameter. I'm not really sure how to accomplish this.
Here is the stored procedure:
PROCEDURE run_search(i_unit_id IN lu_unit.fsu_id%TYPE,
i_equipment IN tbl_component.component%TYPE,
i_equipment_status IN tbl_component.equipment_status%TYPE,
i_equipment_type IN tbl_component.equipment_type%TYPE,
i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
i_system_id IN tbl_component.system_id%TYPE,
i_association_code IN tbl_component_assc_code.assc_code%TYPE,
i_manufacturer IN lu_component_manu_model.equipment_manufacturer%TYPE,
i_manumodel IN lu_component_manu_model.equipment_model%TYPE,
o_results OUT sys_refcursor) AS
v_query VARCHAR2(32767) := '';
v_where VARCHAR2(32767) := ' 1= 1';
BEGIN
IF i_unit_id IS NOT NULL THEN
v_where := v_where || ' AND unit_id=''' || i_unit_id ||''' ';
END IF;
IF i_equipment IS NOT NULL THEN
v_where := v_where || ' AND lower(component) LIKE ''%' || lower(i_equipment) ||'%'' ';
END IF;
IF i_equipment_status IS NOT NULL THEN
v_where := v_where || ' AND equipment_status=''' || i_equipment_status ||''' ';
END IF;
IF i_equipment_type IS NOT NULL THEN
v_where := v_where || ' AND equipment_type=''' || i_equipment_type ||''' ';
END IF;
IF i_equipment_subtype IS NOT NULL THEN
v_where := v_where || ' AND equipment_sub_type=''' || i_equipment_subtype ||''' ';
END IF;
IF i_system_id IS NOT NULL THEN
v_where := v_where || ' AND system_id=''' || i_system_id || ''' ';
END IF;
IF i_association_code IS NOT NULL THEN
v_where := v_where || ' AND EXISTS ( select null from tbl_component_assc_code where assc_code = ''' || i_association_code || ''' and component_id = vcs.component_id )';
END IF;
IF i_manufacturer IS NOT NULL THEN
v_where := v_where || ' AND equipment_manufacturer=''' || i_manufacturer || ''' ';
END IF;
IF i_manuModel IS NOT NULL THEN
v_where := v_where || ' AND equipment_model=''' || i_manuModel || ''' ';
END IF;
v_query :=
' SELECT rownum, results.* '
||' FROM '
||' ( SELECT '
||' count(*) OVER () ' || ' as total_results, '
||''
||' site_id, site_display_name, '
||' unit_id, unit_display_name, '
||' system_id, system_display_name, '
||' component_id, component, component_description, equipment_description, '
||' equipment_status, equipment_model, equipment_serial_number, equipment_type, equipment_sub_type, '
||' template_ids '
||''
||' FROM vw_component_search '
||' WHERE ' || v_where
||' ORDER BY unit_display_name, component '
||' ) results '
;
OPEN o_results FOR v_query;
END run_search;
You can write the query without dynamically creating it so you include all the parameters and just ignore those which are NULL (please profile it to test whether there are any performance issues compared to a dynamic query):
PROCEDURE run_search(i_unit_id IN lu_unit.fsu_id%TYPE,
i_equipment IN tbl_component.component%TYPE,
i_equipment_status IN tbl_component.equipment_status%TYPE,
i_equipment_type IN tbl_component.equipment_type%TYPE,
i_equipment_subtype IN tbl_component.equipment_sub_type%TYPE,
i_system_id IN tbl_component.system_id%TYPE,
i_association_code IN tbl_component_assc_code.assc_code%TYPE,
i_manufacturer IN lu_component_manu_model.equipment_manufacturer%TYPE,
i_manumodel IN lu_component_manu_model.equipment_model%TYPE,
o_results OUT sys_refcursor)
AS
BEGIN
OPEN o_results FOR
SELECT rownum,
results.*
FROM ( SELECT count(*) OVER () as total_results,
site_id,
site_display_name,
unit_id,
unit_display_name,
system_id,
system_display_name,
component_id,
component,
component_description,
equipment_description,
equipment_status,
equipment_model,
equipment_serial_number,
equipment_type,
equipment_sub_type,
template_ids
FROM vw_component_search
WHERE ( i_unit_id IS NULL
OR unit_id= i_unit_id )
AND ( i_equipment IS NULL
OR lower(component) LIKE '%' || lower(i_equipment) || '%' )
AND ( i_equipment_status IS NULL
OR equipment_status= i_equipment_status )
AND ( i_equipment_type IS NULL
OR equipment_type= i_equipment_type )
AND ( i_equipment_subtype IS NULL
OR equipment_sub_type= i_equipment_subtype )
AND ( i_system_id IS NULL
OR system_id= i_system_id )
AND ( i_association_code IS NULL
OR EXISTS ( select null
from tbl_component_assc_code
where assc_code = i_association_code
and component_id = vcs.component_id ) )
AND ( i_manufacturer IS NULL
OR equipment_manufacturer= i_manufacturer )
AND ( i_manuModel IS NULL
OR equipment_model= i_manuModel )
ORDER BY unit_display_name, component
) results;
END run_search;
(I've not compiled the above code - so there may be some errors).
The best way is to avoid such headache at all. 'SP returning resultset' is a usual practise in a poor legacy things such as MSSQL2000, but in unnecessary and doubtful in Oracle.
If you wish to do this, I'd advice you to do something like this:
procedure MakeGarbage(value_mask varchar2) return sys_refcursor is
cur integer;
stmt varchar2(32000 byte);
type TParamTable is table of varchar2(1000) index by varchar2(20);
params TParamTable;
i varchar2(20);
begin
stmt := 'select * from table where 1 = 1 ';
if value_mask is not null then
stmt := stmt || ' and value like :value_mask ';
params('value_mask') := value_mask;
end if;
...
cur := dbms_sql.create_cursor;
dbms_sql.open_cursor(cur, stmt, dbms_sql.native);
i := params.first;
while i is not null loop
dbms_sql.bind_variable(i, params(i));
i := params.next(i);
end loop;
return dbms_sql.to_ref_cursor(cur);
end;
every reference to a PL/SQL variable is in fact a bind variable.
you can check this asktom link
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:2320123769177
and check "Dynamic SQL" from this link http://www.akadia.com/services/ora_bind_variables.html