Insert data from pl/sql table type to another pl/sql table type - oracle

I am just wondering whether it is possible to insert data from one pl/sql table to another using bulkcollect?
I am trying it out but looks like it is not possible conceptually and the only way is to loop through the Pl/sql table .
Any insights would be really helpful.
Thanks
below is the simplified version of what i am trying. i think i am making some conceptual mistake here . hence it is not working:
DECLARE
TYPE ROWTBL IS TABLE OF BW_COLUMN.NAME%TYPE ;
PL_TBL_ROW ROWTBL;
TYPE COLNAME_TBL IS TABLE OF BW_COLUMN.NAME%TYPE ;
PL_TBL_COLNAME COLNAME_TBL;
BEGIN
SELECT NAME
BULK COLLECT INTO PL_TBL_ROW
FROM TBL_COL WHERE TBL_ID = 2000081;
SELECT NAME
BULK COLLECT INTO PL_TBL_COLNAME
FROM PL_TBL_ROW;
END;

BULK COLLECT is a mechanism for efficiently reading data into PL/SQL data structures so they can be processed by PL/SQL code. You can certainly use this approach for copying data from one table to another but I suspect it will probably take more time than the simpler approach of using an INSERT statement such as
BEGIN
INSERT INTO SOME_TABLE (COL_1, COL_2, COL_N)
SELECT COL_1, COL_2, COL_N
FROM SOME_OTHER_TABLE
WHERE SOME_FIELD = SOME_OTHER_VALUE;
END;
Best of luck.

Related

Oracle Forms compiler mark a type as function

I'm making a procedure inside an Oracle Form that calls a backtracking which inserts in a table (the solution table). That backtracking is fed by a varray (ítem_array) of ítems (beans). Problem is the compiler says that there is no function name ítem.
existing objects in the database:
CREATE TYPE item IS object( NUM_OPERACIO NUMBER, TITULS NUMBER);
CREATE TYPE item_array IS VARRAY(1000) OF item;
create table my_table (NUM_OPERACIO NUMBER, TITULS NUMBER);
insert into my_table ( NUM_OPERACIO,TITULS ) values (1,10);
insert into my_table ( NUM_OPERACIO,TITULS ) values (2,20)
insert into my_table ( NUM_OPERACIO,TITULS ) values (3,30)
procedure
PROCEDURE solver
IS
arr item_array;
BEGIN
SELECT item( NUM_OPERACIO,TITULS )
BULK COLLECT INTO arr
FROM my_table;
delete from solucion ;
backtra(arr,1,0,30);
END;
What can I do to solve this?
That is a PLS error number. From the documentation:
"PLS-00591: this feature is not supported in client-side programs
Cause: One of the following features was used in a wrong context: pragma AUTONOMOUS_TRANSACTION, dynamic SQL statements, (e.g. EXECUTE IMMEDIATE), and bulk binds. These listed features can only be used in server-side programs but not client-side programs."
Your code uses a bulk-bind, so the error fits. Forms PL/SQL and database PL/SQL are similar but they use different engines, and features supported in both is only a sub-set of all features in either. The solution is to pass the array population into a database procedure and call it from Forms as needed.
Without knowing precisely what problem you're trying to solve, it's harder to give a better answer.

Delete duplicated records using procedure in Oracle/PLSQL

As the title, I wanna create a procedure in Oracle/PLSQL to delete rows which share same values in some columns. I know how to implement it using Query, but how to do it using procedure? Do I have to use any loop? I am very new to PLSQL
Please help, thank you a lot!
If you want a simple procedure to delete from a particular table you can use the below piece of code:
CREATE OR REPLACE PROCEDURE DELETE_DUPLICATE AS
BEGIN
FOR I IN (SELECT TAB.A, TAB.B, MIN(ROWID) RID
FROM DUPLICATE_TABLE TAB
GROUP BY TAB.A, TAB.B
HAVING COUNT(*) > 1) LOOP
DELETE FROM DUPLICATE_TABLE TAB
WHERE I.RID <> TAB.ROWID
AND TAB.A = I.A
AND TAB.B = I.B;
COMMIT;
END LOOP;
END;
Here DUPLICATE_TABLE is the table having duplicate values. We are deleting rows having same values in columns A and B.
Hey. As per your question, although it is not advicable to create
procedure for this simpler task which can be easily done via Pure SQL.
But if its really imp to make it as a stored procedure then i would
suggest to use PURE SQL logic than using any kind of loop as there
will be Context Switching which will have a toll on the database.
Below is a snippet which i think will be useful also incorporated
Analytical function to suffice your issue. Let me know if it helps.
CREATE OR REPLACE PROCEDURE Dup_DELETE
AS
BEGIN
DELETE
FROM EMP
WHERE EMP.ROWID IN
-- Assuming that i am trying to segregate the duplicate values on Empno and ename
(SELECT A.ROWID
FROM
(SELECT ROW_NUMBER() OVER(PARTITION BY EMPNO,ENAME ORDER BY JOB DESC) RNK,
empno,
ename,
rowid
FROM EMP
)A
WHERE A.RNK <> 1
);
END;
Just put your SQL statement in a procedure. There's no rule that says you have to change the approach because it's PL/SQL. For example,
create or replace procedure dedupe_sometable
as
begin
delete sometable
where rowid in
( select lag(rowid) over (partition by id order by null)
from sometable );
end dedupe_sometable;
Add logging etc as needed.
(Ideally this would be within a package and not a standalone procedure.)
If you know how to do it in SQL, better to do it in sql. PL/SQL should be used only when you cannot write specific task in SQL statement or if there is performance issues in the query and can improve by writing the logic in PL/SQL (second scenario is very rare).
If you want to write PL/SQL procedure to parameterize so that any table can be passed to delete the duplicates from it, then it makes sense. You need to dynamically generate delete statement in the procedure and execute using execute immediate.
If your intention is to learn PL/SQL, then it is programming language and you need to spend some time as if you are learning new programming language.
It is not recommended to use plsql for something that can be done using plain sql.
Whenever you have a combination of sql and plsql, you are switching between sql and plsql engine. So it does not make sense to incur this overhead without proper requirement.
If for some reason there is still a need for doing this, you can atleast implement bulk delete to reduce some overhead. Please refer to the code below to find out how to do that -
DECLARE
TYPE t_del IS TABLE OF VARCHAR2(100);
l_del t_del;
CURSOR c IS
SELECT MIN(ROWID) RID
FROM test_tbl TAB
GROUP BY TAB.age, TAB.gender
HAVING COUNT(*) > 1;
BEGIN
OPEN c;
LOOP
FETCH c BULK COLLECT INTO l_del;
EXIT WHEN l_del.COUNT = 0;
FORALL i IN l_del.FIRST..l_del.last
DELETE FROM test_tbl WHERE ROWID = l_del(i);
END LOOP;
END;

how to select table name from a query in oracle

select SRI_PACK_INDEX_VAL
from (select TABLE_NAME
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%');
Hi,
The above query is not working in oracle.Can anyone help me to solve this
You can't dynamically select from tables like that as Oracle's parser can't figure it out before fetch to know what tables it needs to read. If you want to do a dynamic fetch you need to do dynamic sql. Something like (forgive any minor syntax errors-I'm away from my database at the moment
declare
index_val number; -- i am assuming it is a number, change as appropriate
begin
-- I'm doing this in a loop if there are multiple tables that meet your name mask.
-- If there will only be one, then just select it into a variable and use it that way instead
for tab_name in (SELECT TABLE_NAME
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%')
loop
execute immediate 'select SRI_PACK_INDEX_VAL from '||tab_name.table_name
into index_val;
-- do your stuff with it
end loop;
end;
now, that works if the select only brings back one row. if you are bringing back multiple rows, then you have to handle it differently. In that case you will either want to EXECUTE IMMEDIATE a pl/sql block and embed your processing of the results there, or execute immediate bulk collect into an array, an example on how to do that is here.
select
extractvalue(
xmltype(
dbms_xmlgen.getxml(
'select sri_pack_index_val a from '||owner||'.'||table_name
)
), '/ROWSET/ROW/A'
) sri_pack_index_val
from all_tables
where table_name like '%SE_RAO_INDEX_04_12_2015%';
This query is based on a post from Laurent Schneider. Here's a SQLFiddle demonstration.
This is a neat trick to create dynamic SQL in SQL, but it has a few potential issues. It's probably not as fast as the typical dynamic SQL approach as shown by Michael Broughton. And I've ran into some weird bugs when trying to use this for large production queries.
I recommend only using this approach for ad hoc queries.

Populating a cursor with a rowtype variable

No amount of googling seems to find the answer to this one...
I'm trying to modify Oracle sproc that that currently pulling values out of some tables and moving them to other tables.
It has a ROWTYPE variable that is defined like this:
myRow my_tbl%ROWTYPE;
Right now, the sproc does some logic that populates the rowtype variable and then uses it to populate a table:
INSERT INTO MY_TBL
( col1,
col2,
col3,
-snip-
)
VALUES (
myRow.aValue,
myRow.aValue2,
myRow.aValu3,
-snip-
)
Instead of populating a table, I want to use the ROWTYPE to populate a cursor that is returned to a Web app. However, I can't find a way to do this ROWTYPE -> REF CURSOR conversion. Is this possible? If not, is there a way to manually populate a cursor with data drawn from various tables and using some complex logic? I'm using Oracle 10g.
Thanks!
The tricky part about this is that you are using a REF CURSOR, which is intended for a set of rows to return data for just a single row. I imagine you are just doing this because your web app understands ref cursors and how to get them from Oracle, but not object types. I am also guessing that for some reason you can't just write a single select statment to retrieve and manipulate the data as needed (this is the easiest way, so with more info we can possibly help you out to achieve it).
There are a few ways I can think of to do this, none of them very pretty, so hopefully someone else will chime in with a better idea.
1) Create the cursor by selecting your calculated variables from dual
DECLARE
refcur SYS_REFCURSOR;
myRow TBL%ROWTYPE;
BEGIN
myRow.aValue := 1;
myRow.aValue2 := 3;
myRow.aValue3 := 5;
OPEN refcur
FOR
select
myRow.aValue,
myRow.aValue2,
myRow.aValue3
from
dual;
CLOSE refcur;
END;
2) Create a pipelined function that returns a table of your rowtype, and create your cursor from a select from that function.
The select from dual would be something like
select myRow.aValue,
myRow.aValue2,
myRow.aValu3
from dual;
You should be able to declare a cursor for that.
There is a good writeup of REF CURSOR at http://psoug.org/reference/ref_cursors.html

How do I check for a IN condition against a dynamic list in Oracle?

EDIT: changed the title to fit the code below.
I'm trying to retrieve a list of acceptable values from an Oracle table, then performing a SELECT against another while comparing some fields against said list.
I was trying to do this with cursors (like below), but this fails.
DECLARE
TYPE gcur IS REF CURSOR;
TYPE list_record IS TABLE OF my_table.my_field%TYPE;
c_GENERIC gcur;
c_LIST list_record;
BEGIN
OPEN c_GENERIC FOR
SELECT my_field FROM my_table
WHERE some_field = some_value;
FETCH c_GENERIC BULK COLLECT INTO c_LIST;
-- try to check against list
SELECT * FROM some_other_table
WHERE some_critical_field IN c_LIST;
END
Basically, what I'm trying to do is to cache the acceptable values list into a variable, because I will be checking against it repeatedly later.
How do you perform this in Oracle?
We can use collections to store values to suit your purposes, but they need to be declared as SQL types:
create type list_record is table of varchar2(128)
/
This is because we cannot use PL/SQL types in SQL statements. Alas this means we cannot use %TYPE or %ROWTYPE, because they are PL/SQL keywords.
Your procedure would then look like this:
DECLARE
c_LIST list_record;
BEGIN
SELECT my_field
BULK COLLECT INTO c_LIST
FROM my_table
WHERE some_field = some_value;
-- try to check against list
SELECT * FROM some_other_table
WHERE some_critical_field IN ( select * from table (c_LIST);
END;
"I see that you still had to perform a
SELECT statement to populate the list
for the IN clause."
If the values are in a table there is no other way to get them into a variable :)
"I'm thinking that there's a
significant performance gain using
this over a direct semi-join"
Not necessarily. If you're only using the values once then the sub-query is certainly the better approach. But as you want to use the same values in a number of discrete queries then populating a collection is the more efficient approach.
In 11g Enterprise Edition we have the option to use result set caching. This is a much better solution, but one which is not suited for all tables.
Why pull the list instead of using a semi-join?
SELECT *
FROM some_other_table
WHERE some_critical_field IN (SELECT my_field
FROM my_table
WHERE some_field = some_value);

Resources