For Loop in PL/SQL - dynamic

I am new to PL/SQL. I have a scenario where we have one staging table STG_MAIN_CF_EVENT. It has the information that needs to be loaded into two different tables: CUSTOMER_FEEDBACK_STG and CF_EVENT_STG.
For every record in STG_MAIN_CF_EVENT, I need to create a primary key in CUSTOMER_FEEDBACK_STG and insert into this.
Can someone let me know how to achieve this in For looping dynamically.

DECLARE
CURSOR c_stg_main_cf_event IS
SELECT *
FROM stg_main_cf_event
;
BEGIN
FOR r_row IN c_stg_main_cf_event
LOOP
-- Access the fields by using 'r_row.fieldname'
END LOOP;
END;

Related

I want to make all the updates in pl sql without using update statement with the help of cursor for loop

I have create a big table named accounting_book I store data here from another table named Revenue
and now I want to update those records without using update statement with the help of cursor for loop in oracle database.
SET SERVEROUTPUT ON;
DECLARE
CURSOR SKC IS
SELECT TXNSEQ,TXNID,CASHAMT,CHECKAMT, PPAMT,CARDAMT
FROM REVENUE;
BEGIN
FOR a_REVENUE IN SKC LOOP
INSERT INTO ACCOUNTING_BOOK(TXNSEQ,TXNID,CASHAMT,
CHECKAMT,PPAMT,CARDAMT)
VALUES(a_REVENUE.TXNSEQ, a_REVENUE.TXNID, a_REVENUE.CASHAMT, a_REVENUE.CHECKAMT, a_REVENUE.PPAMT, a_REVENUE.CARDAMT);
END LOOP;
COMMIT;
END;
/
What should I do next please give me some suggetion...

Saving result set in variable using a cursor

I have a problem using Oracle SQL to loop over a result set twice.
The problem
I have a cursor that gets me all foreign keys to a given table name. Using the result form this cursor, I loop through all the constraints and disable them. Then I perform a data import and then I need to loop over the same result set and enable them.
CURSOR c_fkeys_inn(tabellnavn IN VARCHAR2)
IS
SELECT table_name,constraint_name, status
FROM user_constraints
WHERE (r_constraint_name,r_owner) IN
(SELECT constraint_name, owner
FROM user_constraints
WHERE owner ='POP'
AND table_name=upper(tabellnavn)
)
AND STATUS = 'ENABLED';
What I would like to do
My brain jumps directly to a variable. I would like to perform the cursor just once, and then save the result from the cursor to a variable.
Is this possible or are there anything I do to save the result from the cursor and loop twice?
Please try this code. I have sightly modified your code to just display the constraint's table names. You can modify the end part of the plsql according to your requirement. Please comment if you have come across any mistakes or issues, thank you.
CREATE or replace PROCEDURE a_proc(name_table varchar)
AS
CURSOR c_fkeys_inn(tabellnavn IN VARCHAR2)
IS
SELECT table_name,constraint_name, status
FROM user_constraints
WHERE STATUS = 'ENABLED'
AND TABLE_NAME=tabellnavn;
names_t c_fkeys_inn%ROWTYPE;
TYPE c_fkeys IS TABLE OF names_t%TYPE;
fkeys c_fkeys;
BEGIN
OPEN c_fkeys_inn(name_table);
FETCH c_fkeys_inn BULK COLLECT INTO fkeys;
CLOSE c_fkeys_inn;
FOR indx IN 1..fkeys.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(fkeys(indx).table_name);
END LOOP;
END a_proc;
To run the code please run a separate plsql block. Please find a simple and a sample plsql block given below.
begin
a_proc('SUPPLIER');
END;

Selecting numbers into array and then looping over them

I need to populate an array with numbers (it is the ID column of the table) and then loop over said array calling a stored procedure.
I am having trouble figuring out how to declare an array in which I won't know the size of until runtime and then populating it.
Currently, I have the following:
declare
type idArray is table of number index by pls_integer;
theIdArray idArray;
begin
select id into theIdArray from table_name_here where report_id = 3449;
end;
This is not working, but I don't know why. I then also need to loop through my array of numbers and call a stored procedure. Something like the following:
for i in theIdArray.FIRST..theIdArray.LAST LOOP
stored_proc_here(i);
END LOOP;
Can someone give me some insight on to how to accomplish this. What I have so far has been derived from examples I sorta-kinda understood.
your code fails because you are using clause into. For filling collections use bulk collect into instead.
declare
type idArray is table of number index by pls_integer;
theIdArray idArray;
begin
select id bulk collect into theIdArray from table_name_here where report_id = 3449;
end;
If all you are doing is looping it could be just as easy to use a cursor loop.
declare
cursor Ids is
select ID from table_name_here where report_id = 3449;
...
for Report in Ids loop
stored_proc_here( Report.ID );
end loop;
You don't have to worry about explicit opening, closing, fetching, allocating or deallocating. All that is handled by the loop.

Oracle equivalent for SQL Server INSERTED and DELETED tables

I am in the process of migrating a SQL Server database to Oracle, where I have to convert SQL Server procedure which uses special tables called INSERTED and DELETED in SQL Server.
As per my understanding these tables hold copies the data of last inserted/deleted records.
(find the msdn article here: http://msdn.microsoft.com/en-us/library/ms191300.aspx)
Are there any similar tables in Oracle to achieve this..? Please advise.
UPDATE:
Thanks for your answers and comments ,I think I need to explain the situation some more. Here is the full story to understand the real scenario;
Data base contains tables and shadow tables (shadow has an additional column).
When a table is updated same changes should be recorded in relevant shadow table with some additional data.
For this purpose they are having triggers for each table (these triggers copy data to relevant shadow table).
The above mentioned procedure generates these triggers dynamically for each and every table.
Now the real problem is I don't have the knowledge about the columns as triggers are dynamically generated for each table.
Basically I can’t get value like: NEW.col_1 or: OLD.col_1 as APC mentioned. Can I.?
Or else I have to write all those triggers manually using prefixes: NEW and: OLD rather than trying to generate them dynamically.
I am using Oracle 11g
Oracle triggers use pseudo-records rather than special tables. That is, instead of tables we can access the values of individual columns.
We distinguish pseudo-records in the affected table from records in (other) tables by using the prefixes :NEW and :OLD . Oracle allows us to declare our own names for these, but there is really no good reason for abandoning the standard.
Which column values can we access?
Action :OLD :NEW
------ ---- ----
INSERTING n/a Inserted value
UPDATING Superseded value Amended value
DELETING Deleted value n/a
You will see that :OLD is the same as the MSSQL table DELETED and :NEW is the same as table INSERTED
So, to trigger a business rule check when a certain column is updated:
create or replace trigger t23_bus_check_trg
before update on t23
for each row
begin
if :NEW.col_1 != :OLD.col_1 then
check_this(:NEW.col_1 , :OLD.col_1);
end if;
end t23_bus_check_trg;
There's a whole chapter on records in the PL/SQL Reference. Find out more.
There are many differences between Sql Server triggers and Oracle triggers. In Oracle, you can declare statement level or row level triggers. Sql Server only has statement level. In Oracle, you can declare before triggers or after triggers. Sql Server only has after triggers.
If you're going to be working with Oracle, although later versions have the compound trigger, get used to working with row level triggers. There you have the pseudo row designation of :old and :new, kinda like Deleted and Inserted except it's just the one row of data. It's like being in a cursor loop, something you can do in Sql Server, but cursor perform so poorly in Sql Server, developers go to great lengths to avoid them. They are commonly used in Oracle.
The general rule of thumb is this: if you need to examine the data and possibly alter it before it goes to the table, use a "before" trigger. If you want to perform an audit or logging procedure, use an "after" trigger.
The page I linked to above gives a lot of technical details, but it is absolutely atrocious at giving usable examples. For that, just google "oracle trigger tutorial" and you should get lots of handy, easy-to-learn-from examples.
Thanks for the answers and comments. here is the complete solution to my problem.If some one meet the exact problem this will help.
create or replace PROCEDURE CreateTrackingTriggers
(
-- take the target table and shadow user as agruments
v_TableName IN NVARCHAR2 DEFAULT NULL,
v_ShadowUser IN NVARCHAR2 DEFAULT 'SHADOW_USER'
)
AUTHID CURRENT_USER -- grant permission to create triggers
AS
v_TriggerName NVARCHAR2(500);
v_ColList NVARCHAR2(2000);
v_ColList_shadow NVARCHAR2(2000);
v_SQLCommand VARCHAR2(4000);
v_ColName NVARCHAR2(500);
v_ColSize NUMBER(10,0);
v_Prefix NVARCHAR2(500);
v_count NUMBER(1,0);
BEGIN
DECLARE
-- define a cursor to get the columns of the target table. order by COLUMN_ID is important
CURSOR Cols
IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM USER_TAB_COLS
WHERE TABLE_NAME = upper(v_TableName) order by COLUMN_ID;
-- define a cursor to get the columns of the target shadow table order by COLUMN_ID is important
CURSOR Shadow_Cols
IS SELECT COLUMN_NAME , CHAR_COL_DECL_LENGTH FROM ALL_TAB_COLS
WHERE TABLE_NAME = upper(v_TableName) and upper(owner)=upper(v_ShadowUser) order by COLUMN_ID;
BEGIN
-- generate the trigger name for target table
v_TriggerName := 'TRG_' || upper(v_TableName) || '_Track' ;
-- check v_count , determine whether shdow table exist if not handle it
select count(*) into v_count from all_tables where table_name = upper(v_TableName) and owner = upper(v_ShadowUser);
-- iterate the cursor. generating column names prefixing ':new.'
OPEN Cols;
FETCH Cols INTO v_ColName,v_ColSize;
WHILE Cols%FOUND
LOOP
BEGIN
IF v_ColList IS NULL THEN
v_ColList := ':new.'||v_ColName ;
ELSE
v_ColList := v_ColList || ',' || ':new.'||v_ColName;
END IF;
FETCH Cols INTO v_ColName,v_ColSize;
END;
END LOOP;
CLOSE Cols;
-- iterate the cursor. get the shadow table columns
OPEN Shadow_Cols;
FETCH Shadow_Cols INTO v_ColName,v_ColSize;
WHILE Shadow_Cols%FOUND
LOOP
BEGIN
IF v_ColList_shadow IS NULL THEN
v_ColList_shadow := v_ColName;
ELSE
v_ColList_shadow := v_ColList_shadow || ',' || v_ColName;
END IF;
FETCH Shadow_Cols INTO v_ColName,v_ColSize;
END;
END LOOP;
CLOSE Shadow_Cols;
-- create trigger command. This will generate the trigger that dupilicates target table's data into shdow table
v_SQLCommand := 'CREATE or REPLACE TRIGGER '||v_TriggerName||'
AFTER INSERT OR UPDATE OR DELETE ON '||upper(v_TableName)||'
REFERENCING OLD AS old NEW AS new
FOR EACH ROW
DECLARE
ErrorCode NUMBER(19,0);
BEGIN
-- v_ColList_shadow : shdow table column list
-- v_ColList : target table column list with :new prefixed
INSERT INTO '|| v_ShadowUser ||'.'||upper(v_TableName)||'('||v_ColList_shadow||') values ('||v_ColList||');
EXCEPTION
WHEN OTHERS THEN ErrorCode := SQLCODE;
END;';
EXECUTE IMMEDIATE v_SQLCommand;
END;
END;

need to write a trigger

I want to write a trigger for a table "TRANSACTION".When a new line is inserted, I want to trigger to update the field "TRANSACTIONID" to the maximum + 1 of all the previous records.
I on't know much about SQL. Can someone help me?
many thanks
This is a really bad idea for a multi-user environment, as it will serialise inserts into the table. The usual approach is to use an Oracle sequence:
create sequence transaction_seq;
create trigger transaction_bir before insert on transaction
for each row
begin
:new.id := transaction_seq.nextval;
end;
To write a trigger based solution that actually got the max current value plus 1, you would need to write a complex 3-trigger solution to avoid the "mutating table" issue. Or you could create a simpler solution using another table to hold the current maximum value like this:
create table transaction_max (current_max_id number);
insert into transaction_max values (0);
create trigger transaction_bir before insert on transaction
for each row
declare
l_current_max_id number;
begin
update transaction_max set current_max_id = current_max_id + 1
returning current_max_id into l_current_max_id;
:new.id := l_current_max_id;
end;
This will avoid the mutating table issue and will serialize (slow down) inserts, so I don't see any advantage of this over using a sequence.
CREATE TRIGGER trigger1 on TransactionTable
INSTEAD OF INSERT
AS
BEGIN
DECLARE #MaxTranId INT
SELECT
#MaxTranId = MAX(TransactionId)
FROM
TransactionTable
INSERT INTO TransactionTable
SELECT
#MaxTranId + 1 ,
RestOfYourInsertedColumnsHere ,
FROM
inserted
END
GO