I am creating a stored procedure to load data from source database: MYDB -> target database: NEWDB.
I will load the data in tables SCHEMA1.EMPLOYEE1, SCHEMA1.EMPLOYEE2, ...
Edit 1:
CREATE or replace PROCEDURE SCHEMA1.PROC_LOAD ()
SPECIFIC PROC_LOAD
LANGUAGE SQL
BEGIN
DECLARE v_table varchar(100);--
DECLARE truncate_stmt varchar(1000);--
DECLARE load_stmt varchar(1000);--
for v_table as select rtrim(tabname) as tabname from syscat.tables where tabschema='SCHEMA1' and tabname like '%EMPLOYEE%'
do
-- Truncate the table first
set truncate_stmt = 'ALTER TABLE SCHEMA1.'||v_table.tabname||' ACTIVATE NOT LOGGED INITIALLY WITH EMPTY TABLE';--
prepare s1 from truncate_stmt;--
execute s1;--
-- Load the data
set load_stmt = 'LOAD FROM (DATABASE MYDB SELECT * FROM SCHEMA1.'||v_table.tabname||'_HIST) OF CURSOR MESSAGES ON SERVER INSERT INTO SCHEMA1.'||v_table.tabname||' NONRECOVERABLE';--
CALL SYSPROC.ADMIN_CMD (load_stmt);--
end for;--
END;
Above is the code of my db2 stored procedure, I have created it successfully, but when I call it, it returns the error:
ERROR [24501] [IBM][DB2/NT64] SQL0501N The cursor specified in a FETCH statement or CLOSE statement is not open or a cursor variable in a cursor scalar function reference is not open.
In the target database, I select the data from SCHEMA1.EMPLOYEE1 and it shows that the data is loaded successfully, but for EMPLOYEE2,3,..., the old data is still there, it seems that only the first table in the loop is loaded successfully.
Any idea? Also my db2 platform is db2 11.1 on luw. Thanks in advance.
LOAD commits implicitly, so you have to use WITH HOLD clause of the FOR statement to not close the cursor upon such a commit.
Related
Need help on getting query on a text file using stored procedure in db2. I'm very new to this so please help.
Till now i am able to create a file and get get data in created file. But i want a complete query in the file not a single column data.
I have created a Stored Procedure like this
CREATE or replace PROCEDURE SCOPEMASTERP(my_statement varchar(10000))
AUTONOMOUS
BEGIN
DECLARE v_filehandle UTL_FILE.FILE_TYPE;
DECLARE v_dirAlias VARCHAR(50) DEFAULT 'mydir';
DECLARE v_filename VARCHAR(100) DEFAULT 'bluestar_transaction-';
DECLARE v_format VARCHAR(200);
SET v_format = '%s\n';
set v_filename = concat(v_filename, VARCHAR_FORMAT(current_date, 'DD-MM-YYYY'));
set v_filename = concat(v_filename, '.log');
CALL UTL_DIR.CREATE_OR_REPLACE_DIRECTORY('D:', '/archivelog/asd/');
SET v_filehandle = UTL_FILE.FOPEN(v_dirAlias,v_filename,'a');
CALL UTL_FILE.PUTF(v_filehandle,v_format, my_statement);
CALL DBMS_OUTPUT.PUT_LINE('Wrote to file: ' || v_filename);
CALL UTL_FILE.FCLOSE(v_filehandle);
END
and i have created a trigger inside trigger i am calling stored procedure
CREATE OR REPLACE TRIGGER SCOPEMASTER_Trigger
AFTER INSERT ON SERVEIT.SCOPENAMEMASTER
REFERENCING NEW AS N
FOR EACH ROW
BEGIN ATOMIC
call SCOPEMASTERP(N.SCOPENAMEID);
END
insert statement i am executing
Insert into SERVEIT.SCOPENAMEMASTER (SCOPENAMEID) values (1013)
GO
And file which is creating in "D" drive i am getting
But instead of just getting 1013 i need the complete query in the file
Insert into SERVEIT.SCOPENAMEMASTER (SCOPENAMEID) values (1013)
What changes i need to do please help. Thanks in advance!!
There are no special registers/variables/etc. available in DB2 that provide the SQL statement that is being executed, so what you're asking for is not possible.
DB2 Stored Procedure update record and select record
CREATE PROCEDURE DB2INST1.GETPEOPLE2(IN ids bigint )
SPECIFIC DB2INST1.GETPEOPLE2
DYNAMIC RESULT SETS 1
MODIFIES SQL DATA
LANGUAGE SQL
BEGIN
update test2 set a=a+1 where a>ids;
DECLARE rs1 CURSOR WITH RETURN TO CLIENT FOR
select * from db2inst1.test2;
OPEN rs1;
END
but it's not working.
error: DB21034E The command was processed as an SQL statement because it was not a valid Command Line Processor command. During SQL processing it returned: SQL0104N An unexpected token "rs1 CURSOR sele" was found following "ids; DECLARE". Expected tokens may include: "". LINE NUMBER=10. SQLSTATE=42601
ok,it work:
BEGIN
DECLARE rs1 CURSOR WITH RETURN TO CLIENT FOR
select * from db2inst1.test2;
update test2 set a=a+1 where a>ids;
OPEN rs1;
END
As you would most likely have deduced from the following question, I am new to DB2 in general. I am attempting to write my second ever stored procedure using IBM Data Studio and am running into an error when trying to deploy it. The point of the stored procedure is to search for a text string in fields across different tables. NOTE: The code is not complete and is not useful in its current form. I am attempting to test each step as I go along.
Here is all of the code I have so far:
CREATE OR REPLACE PROCEDURE sp_find_string (in in_search_string varchar(200), in in_schema varchar(50))
DYNAMIC RESULT SETS 1
P1: BEGIN
-- #######################################################################
-- #
-- #######################################################################
declare table_a varchar(200);
declare colname varchar(200);
declare sqlcmd varchar(2000);
declare eof smallint default 0;
declare not_found condition for sqlstate '02000';
-- Declare cursor
DECLARE cursor1 CURSOR WITH RETURN for
select tabname, colname from syscat.columns c
--inner join syscat.tables t on t.tabschema = c.tabschema
-- and t.tabname = c.tabname
where tabname like 'MERLIN%'
and tabschema = in_schema;
DECLARE CONTINUE HANDLER FOR SQLSTATE '42704' -- or SQLEXCEPTION
-------------------------------------------------
if (exists
(
select 1 from sysibm.systables
where creator = 'A2815'
and name = 'DBP_TEMP_SEARCH_RESULTS'
)
) then drop table A2815.DBP_TEMP_SEARCH_RESULTS;
end if;
create table A2815.DBP_TEMP_SEARCH_RESULTS
(text_to_match varchar(200)
,table_a varchar(200)
,colname varchar(200)
,match_count bigint);
-- Cursor left open for client application
OPEN cursor1;
while eof = 0 do
p2: begin
declare continue handler for not_found set eof = 1;
fetch from cursor1 into table_a, colname;
insert into A2815.DPB_TEMP_SEARCH_RESULTS
values(table_a, colname);
end p2;
end while;
close cursor1;
--return;
END P1
I am getting this error when attempting to deploy:
Deploy [TIO_D]A2815.SP_FIND_STRING(VARCHAR(200), VARCHAR(50))
Running
A2815.SP_FIND_STRING - Deploy started.
Create stored procedure returns SQLCODE: -204, SQLSTATE: 42704.
A2815.SP_FIND_STRING: 44: "A2815.DPB_TEMP_SEARCH_RESULTS" is an undefined name.. SQLCODE=-204, SQLSTATE=42704, DRIVER=4.18.60
"A2815.DPB_TEMP_SEARCH_RESULTS" is an undefined name.. SQLCODE=-204, SQLSTATE=42704, DRIVER=4.18.60
A2815.SP_FIND_STRING - Deploy failed.
A2815.SP_FIND_STRING - Roll back completed successfully.
When I comment out the insert statement, it deploys just fine (but of course the procedure doesn't do me much good without the ability to insert):
OPEN cursor1;
while eof = 0 do
p2: begin
declare continue handler for not_found set eof = 1;
fetch from cursor1 into table_a, colname;
--insert into A2815.DPB_TEMP_SEARCH_RESULTS
--values(table_a, colname);
end p2;
end while;
close cursor1;
It is true that the table does not exist yet, because it should be created by the procedure. However, if I create the table then deploy the procedure I get this error:
Deploy [TIO_D]A2815.SP_FIND_STRING(VARCHAR(200), VARCHAR(50))
Running
A2815.SP_FIND_STRING - Deploy started.
Create stored procedure returns SQLCODE: -601, SQLSTATE: 42710.
A2815.SP_FIND_STRING: 32: The name of the object to be created is identical to the existing name "A2815.DBP_TEMP_SEARCH_RESULTS" of type "TABLE".. SQLCODE=-601, SQLSTATE=42710, DRIVER=4.18.60
The name of the object to be created is identical to the existing name "A2815.DBP_TEMP_SEARCH_RESULTS" of type "TABLE".. SQLCODE=-601, SQLSTATE=42710, DRIVER=4.18.60
A2815.SP_FIND_STRING - Deploy failed.
A2815.SP_FIND_STRING - Roll back completed successfully.
Can anyone tell me how to get this procedure deployed either when the table exists, when it doesn't exist, or both?
Thank you very much and let me know what other detail is needed. Also, suggestions on how to improve the code in general would be great as well.
The simplest solution is simply to create that table so that it exists before you compile the procedure. If you just run the create table statement manually before compiling the procedure, then there will be no problem.
Commenters have suggested that you should use Declare Global Temporary Table, and I agree with this, since you appear to be using this as a temporary table. However, it doesn't actually solve your specific problem, since the procedure still won't compile if the temporary table doesn't exist at compile time. So, yes, use a temporary table, but you will still have to do the same thing.
Changing the insert statement to dynamic SQL would also work, though it is an ugly solution. Not necessary here, but sometimes it is needed.
Might be a bit late, but the best way to do this would be to create a string with your query, instead of using the query directly, and then using EXECUTE IMMEDIATELY
I have a very big Stored Procedure in iSeries DB2 v8 which does the following:
Calls other stored procedures inside the same schema
Prepares dynamic sql statments from strings and runs them
Calls other functions from the same schema
Uses various tables from the same schema
My problem is that this Stored Procedure and the accompanying functions may change from that schema into another (ie. from 'superlib' to 'restorelib') and the whole code is currently hardcoded to run with the named schema.
What I want is to be able to do one of the two: either pass the name of the schema where everything is located via a parameter to the stored procedure, or have the stored procedure detect the name of the schema and use it to run itself.
This is a sample of my current code:
SELECT COUNT(*) INTO TotalNotDone FROM superlib.PROCESSTABLES WHERE PROCESS_FLAG < 1;
WHILE TotalNotDone > 0 DO
SET SQLLOOPSTMT = 'select name_to_proces from ' CONCAT SOURCELIBRARY CONCAT '.processtables where process_flag = 0' ;
PREPARE LOOPSTMT FROM SQLLOOPSTMT ;
OPEN LOOPCUR ;
FETCH LOOPCUR INTO TABLETOPROCESS ;
CALL superlib.SP_RESTORE_INSERTS ( SOURCELIBRARY , DESTLIBRARY , TABLETOPROCESS, P_STARTTIME ) ;
CLOSE LOOPCUR;
SELECT COUNT(*) INTO TotalNotDone FROM superlib.PROCESSTABLES WHERE PROCESS_FLAG < 1;
END WHILE ;
What I want is for NOT to have to write superlib inside the stored procedure to call or reference the tables i'm using and just have the Stored Procedure recognize it currently IS living in the schema superlib.
I've tried SET CURRENT SCHEMA = 'SUPERLIB'; and SET SCHEMA = 'SUPERLIB'; but neither works when calling the TABLES.
I've also changed the path when creating the Stored Procedure from:
SET PATH "QSYS","QSYS2","SYSPROC","SYSIBMADM","PROGUSER1" ;
to
SET PATH "QSYS","QSYS2","SYSPROC","SYSIBMADM","SUPERLIB" ;
but that apparently does nothing.
i believe you'll need to set the current path on the connection that calls the stored proc, not just when creating it.
see this: Weblogic: Call DB2 stored procedure without schema name (property currentSchema)
current path documentation here: http://publib.boulder.ibm.com/infocenter/db2luw/v8//topic/com.ibm.db2.udb.doc/admin/r0005877.htm
I am trying to create a BEFORE INSERT trigger that will check the incoming value of a field, and replace it with the same field in another row if that the field is null. However, when I add the CALL statement to my trigger, an error is returned "The trigger "ORGSTRUCT.CSTCNTR_IN" is defined with an unsupported triggered SQL statement". I checked the documentation and saw that cursors weren't supported in the BEFORE (part of the reason for making the stored procedure in the first place), but even when I remove the cursor declaration from the stored procedure the call still generates the same error.
Trigger:
CREATE TRIGGER orgstruct.cstcntr_IN
NO CASCADE
BEFORE INSERT ON orgstruct.tOrgs
REFERENCING NEW AS r
FOR EACH ROW MODE DB2SQL
BEGIN ATOMIC
DECLARE prnt_temp BIGINT;
DECLARE cstcntr_temp CHAR(11);
SET prnt_temp = r.prnt;
SET cstcntr_temp = r.cstcntr;
CALL orgstruct.trspGetPrntCstCntr(prnt_temp,cstcntr_temp);
SET r.cstcntr = cstcntr_temp;
END
Stored procedure:
CREATE PROCEDURE orgstruct.trspGetPrntCstCntr (
IN p_prnt BIGINT,
OUT p_cstcntr CHAR(11)
)
SPECIFIC trGetPrntCstCntr
BEGIN
IF p_prnt IS NULL THEN
RETURN;
END IF;
BEGIN
DECLARE c1 CURSOR
FOR
SELECT cstcntr
FROM orgstruct.tOrgs
WHERE id = p_prnt
FOR READ ONLY;
OPEN c1;
FETCH FROM c1 INTO p_cstcntr;
CLOSE c1;
END;
END
According to the documentation, CALL is allowed in a BEFORE trigger, so I don't understand what the problem is.
A before trigger can call a stored procedure, but the stored proc can't do anything not allowed in the trigger.
In your case, the default level of data access for a SQL stored proc is MODIFIES SQL DATA, which is not allowed in the trigger. You could recreate your stored procedure, changing the data access level to READS SQL DATA; this will allow you to create the trigger.
However: There is no reason to call a stored procedure for something this simple; You can do it using a simple inline trigger:
create trigger orgstruct.cstcntr_IN
no cascade
before insert on orgstruct.tOrgs
referencing new as r
for each row
mode db2sql
set r.cstcntr = case
when r.p_prnt is not null
then (select cstcntr from tOrgs where id = r.p_prnt fetch first 1 row only)
else r.cstcntr
end;
This will be a LOT more efficient because it eliminates both the stored procedure call and the cursor processing inside the stored proc. Even if you wanted to use the stored proc, you could eliminate the cursor inside the stored proc and improve performance.
FYI: the logic that you posted contains an error, and will always set CSTCNTR to NULL. The trigger posted in this answer not do this. :-)