I want to run sp in a loop .
I am using db2.
For ex if there are 10 rows in my table and 8 rows contain status as approved ,I want those 8 rows to be executed 1 by 1 through my sp .
I have used this method
declare #v_count INT;
declare #row_count INT;
declare #v_actorCNUM CHAR(10);
declare #v_sessID CHAR(32);
declare #v_taskID SMALLINT;
declare #v_dest_citrusSCID INTEGER;
declare #v_src_citrusSCID INTEGER;
declare #v_CtrCnum CHAR(10);
declare #v_dest_security_grp_nm varchar(100);
declare #v_src_security_grp_nm varchar(100);
SET #v_count=1;
DECLARE GLOBAL TEMPORARY TABLE csc_trfr
(
Row_id INT GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1),
CNTRCTR_CNUM CHAR(10) NOT NULL ,
DEST_CSC_NM CHAR(30) ,
SRC_CSC_NM CHAR(30) ,
DEST_GRP_NM VARCHAR(100) ,
SRC_GRP_NM VARCHAR(100) ,
USR_LST_UPDT_ID CHAR(10)
) WITH REPLACE ON COMMIT PRESERVE ROWS NOT lOGGED;
insert into session.csc_trfr (cntrctr_cnum,dest_csc_nm,src_csc_nm,dest_grp_nm,src_grp_nm,usr_lst_updt_id)
select cntrctr_cnum,dest_csc_nm,source_csc_nm,dest_grp_nm,src_grp_nm,usr_lst_updt_id from citrus.cntrctr_csc_trfr where trfr_status='Approved';
select count(*) into #row_count from citrus.cntrctr_csc_trfr where trfr_status='Approved';
while #v_count<=#row_count
BEGIN
SET #v_actorCNUM=select usr_lst_updt_id from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only;
SET #v_taskID=166;
set #v_dest_citrusSCID =select citrus_suprt_id from citrus.citrus_suprt_cntr where citrus_suprt_nm=(select DEST_CSC_NM from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only );
set #v_src_citrusSCID=select citrus_suprt_id from citrus.citrus_suprt_cntr where citrus_suprt_nm=(select SRC_CSC_NM from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only );
set #v_CtrCnum=select cntrctr_cnum from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only;
set #v_dest_security_grp_nm =select DEST_GRP_NM from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only;
set #v_src_security_grp_nm=select DEST_GRP_NM from session.csc_trfr order by row_id offset #v_count rows fetch next 1 rows only;
call citrus.BAC020_cntrctrCSCTrfr(#v_actorCNUM,'',#v_taskID,#v_dest_citrusSCID,#v_src_citrusSCID,#v_CtrCnum,#v_dest_security_grp_nm,#v_src_security_grp_nm,?,?,?);
#v_count=#v_count+1;
end
I have name script as test.sql and executing script like :
db2 -tvf test.sql
but I am getting sql 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 "declare #v_actorCNUM CHAR" was found
following "BEGIN-OF-STATEMENT". Expected tokens may include: "".
LINE NUMBER=1. SQLSTATE=42601
error is same for all variables.
My code is count the rows in cntctr_csc_trfr table where transfer status is approved .and then I am putting those rows in temp table .For each row in temp table I want to run stored procedure.
I had the same problem
Here is an example :
use database1
go
DECLARE #RC int
DECLARE #RequestCode nvarchar(50)
Declare column_cursor Cursor For
select columnName from table_name
OPEN column_cursor
Fetch Next From column_cursor into #RequestCode
While ##Fetch_Status=0 Begin
EXECUTE #RC = [dbo].[stored_procedure_name] #RequestCode
Fetch Next From column_cursor Into #RequestCode
End
Close column_cursor
Deallocate column_cursor
Related
--Existing Data
create table tbl_Master
(
col_Date date not null,
id int not null,
value int not null
);
insert into tbl_Master values('2021-01-14',10,21);
insert into tbl_Master values('2020-09-30',11,22);
insert into tbl_Master values('2021-11-28',12,23);
--alter table name
alter table tbl_Master rename to tbl_Master_old;
--Create Master table again with Partition by range
create table tbl_Master(
col_Date date not null,
id int not null,
value int not null
) partition by range (col_Date);
--Function:
create or replace function fn_create_partition(col_Date date) returns void
as
$body$
declare v_startDate date := date_trunc('month', col_Date)::date;
declare v_EndDate date := (v_startDate + interval '1 month')::date;
declare tableName text := 'tbl_Master_Part_' || to_char(col_Date, 'YYYYmm');
begin
if to_regclass(tableName) is null then
execute format('create table %I partition of tbl_Master for values from (%L) to (%L)', tableName, v_startDate, v_EndDate);
end if;
end;
$body$
language plpgsql;
--Create Partition tables for existing data
do
$$
declare rec record;
begin
for rec in select distinct date_trunc('month', col_Date)::date yearmonth from tbl_Master_old
loop
perform fn_create_partition(rec.yearmonth);
end loop;
end
$$;
--Insert backup data
insert into tbl_Master (col_Date, id, value) select * from tbl_Master_old;
--When I insert record
insert into tbl_Master values('2021-02-22',12,22);
Getting an error:
ERROR: no partition of relation "tbl_master" found for row DETAIL: Partition key of the failing row contains (col_date) = (2021-02-22).
So I have created rule for this:
--Rule
create or replace rule rule_fn_create_partition as on insert
to tbl_Master
do instead
(
select fn_create_partition(NEW.col_Date);
insert into tbl_Master values(New.*)
);
--When I insert record
insert into tbl_Master values('2021-02-22',12,22);
Getting an error:
ERROR: infinite recursion detected in rules for relation "tbl_master"
The rule contains an INSERT, so that is an infinite recursion.
You cannot have PostgreSQL automatically create partitions as you insert data. It just doesn't work. Triggers won't work either. The problem is that that would require modifying the underlying table while the insert is in progress.
I've different different tables to categorically store data and a log table where all the transactions log are recorded
e.g. 1) VoucherNO, Add, ...
2) VoucherNO, Delete, ..
After I backup the database and restore in another server for my Reporting Purpose. That time I want to ensure all the log data and transaction are available in TestDB if not then I remove log from 'AUD_USER_ACTIVITY'.
To find the transaction exist or not, I create a dynamic sql select statement and check whether record is exist or not.
Basis on #RecExist Value I do the action like if records is not available in TestDB the log will be remove, if record exist immediately break this loop and going for next procedure
But #RecExist variable is not updating in Dynamic SQL Execution. Please guide me
declare #MvDocNo varchar(50)
DECLARE #SCtr as DECIMAL(10,0)
declare #LocationCode varchar(4)
declare #UName Nvarchar(40)
declare #toe varchar(30)
declare #QryTxt as nvarchar(MAX);
Declare #RecExist as INT =0;
SET #RecExist=0
WHILE #RecExist=0
BEGIN
select top 1 #MvDocNo=DOCNO, #SCtr=SrlNo,#LocationCode =DMLTYPE,#UName=TABLENAME
FROM R_AUDDB..AUD_USER_ACTIVITY
WHERE DBNAME='TestDB' and DMLTYPE not in ('AD','D','PD') ORDER BY SRLNO DESC;
select top 1 #toe=docno from TestDB..M_TYPEOFENTRY where TBLNAME=#UName;
set #QryTxt='Select #RecExist=1 From R_TestDB..'+#UName+ ' Where '+#toe+'='''+#MvDocNo+''''
exec (#QryTxt)
IF #RecExist=0
BEGIN
DELETE R_AUDDB..AUD_USER_ACTIVITY WHERE SRLNO=#SCtr
END
END
The following code sample demonstrates how to check for a row in a table with a specific column and value using dynamic SQL. You ought to be able to change the values of the first three variables to reference a table and column in your database for testing.
Note that SQL injection is still possible: there is no validation of the table or column names.
-- Define the table to check and the target column name and value.
declare #TableName as SysName = 'Things';
declare #ColumnName as SysName = 'ThingName';
declare #TestValue as NVarChar(32) = 'Beth';
-- Create a SQL statement to check for a row in the target table with the specified column name and value.
declare #SQL as NVarChar(1024);
declare #Result as Bit;
-- Note that only object names are substituted into the statement at this point and QuoteName() is used to reduce problems.
set #SQL = N'select #iResult = case when exists ( select 42 from dbo.' + QuoteName( #TableName ) +
N' where ' + QuoteName( #ColumnName ) + N' = #iTestValue ) then 1 else 0 end;'
select #SQL as SQL;
-- Execute the SQL statement.
-- Note that parameters are used for all values, i.e. the target value and return value.
execute sp_executesql #stmt = #SQL,
#params = N'#iTestValue NVarChar(32), #iResult Bit output',
#iTestValue = #TestValue, #iResult = #Result output
-- Display the result.
select #Result as Result;
CREATE OR REPLACE FUNCTION public.flowrate7(
)
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE 'plpgsql'
COST 100
VOLATILE
ROWS 1000
AS $BODY$
declare temp_omsId integer;
declare temp_flowrate numeric(20,3);
declare temp_chakno varchar(100);
begin
DROP TABLE IF EXISTS tbl_oms;
DROP TABLE IF EXISTS tbl_calFlow;
CREATE temporary TABLE tbl_oms(omsid__ integer) ON COMMIT DELETE ROWS;
CREATE temporary TABLE tbl_calFlow(OmsId_ integer,FlowRate_ numeric(20,3),ChakNo_ varchar(100)) ON COMMIT DELETE ROWS;
insert into tbl_oms (select OmsId from MstOms);
while (select count(*) from tbl_oms) <> 0 LOOP
select temp_omsId = omsid__ from tbl_oms LIMIT 1;
temp_flowrate = (select (case when(InLetPressure > 0.5) then 1 else 0 end) from MstOms where OmsId = temp_omsId);
temp_chakno = (select ChakNo from MstOms where OmsId = temp_omsId);
insert into tbl_calFlow values (temp_omsId,temp_flowrate,temp_chakno);
delete from tbl_oms where omsid__ = temp_omsId;
END LOOP;
return query (select OmsId_,FlowRate_,ChakNo_ from tbl_calFlow);
end;
$BODY$;
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function flowrate7() line 19 at SQL statement
SQL state: 42601
You are retrieving values into the variables incorrectly. To store the value of a query result into a variable (or two) you need to use select .. into variable ...
CREATE OR REPLACE FUNCTION public.flowrate7()
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE plpgsql
AS $BODY$
declare
temp_omsId integer;
temp_flowrate numeric(20,3);
temp_chakno varchar(100);
begin
DROP TABLE IF EXISTS tbl_oms;
DROP TABLE IF EXISTS tbl_calFlow;
CREATE temporary TABLE tbl_oms(omsid__ integer) ON COMMIT DELETE ROWS;
CREATE temporary TABLE tbl_calFlow(OmsId_ integer,FlowRate_ numeric(20,3),ChakNo_ varchar(100)) ON COMMIT DELETE ROWS;
insert into tbl_oms
select OmsId
from MstOms;
while (select count(*) from tbl_oms) <> 0 LOOP
select omsid__
into temp_omsId --<< here
from tbl_oms LIMIT 1;
select case when inletpressure> 0.5 then 1 else 0 end, chakno
into temp_flowrate, temp_chakno --<< here
from MstOms
where omsid = temp_omsId;
insert into tbl_calFlow values (temp_omsId,temp_flowrate,temp_chakno);
delete from tbl_oms where omsid__ = temp_omsId;
END LOOP;
return query select omsid_, flowrate_, chakno_
from tbl_calflow;
end;
$BODY$;
However the processing of that function is unnecessarily complicated
if first copies all rows MstOms to tmp_MstOms
retrieve the ID for one row from tbl_oms
retrieve one row from MstOms calculating the flowrate
stores that one row in the temp table
deletes the just processed row from the (other) temp table
counts the number or rows in tbl_oms and if that is not zero moves on to the "next" row
This is an extremely inefficient and complicate way of calculating a simple value which will not scale well for large tables.
Doing a row-by-row processing of tables in the database is an anti-pattern to begin with (and doing that by deleting, inserting and counting rows is making that even slower).
This is not how things are done in SQL. The whole inefficient loop can be replaced with a single query which also allows you to change the whole thing to a SQL function.
CREATE OR REPLACE FUNCTION public.flowrate7()
RETURNS TABLE(oms_id integer, flowrate numeric, chakno character varying)
LANGUAGE sql
AS $BODY$
select omsid,
case when inletpressure> 0.5 then 1 else 0 end as flowrate,
chakno
from mstoms;
$BODY$;
Actually a view would be more appropriate for this.
create or replace view flowrate7
as
select omsid,
case when inletpressure> 0.5 then 1 else 0 end as flowrate,
chakno
from mstoms;
I have a DB2 Stored Procedure that runs for nearly 4 minutes and fetches 6000 pages of data.
Please may I know if there are any possible query-performance tuning methods in DB2 so that the procedure could be made to execute faster.
The basic structure of the stored procedure created is
CREATE PROCEDURE SAMPLE_PROC(ID INT)
RESULT SETS 1
P1: BEGIN
DECLARE GLOBAL TEMPORARY TABLE SESSION.TEMP1
(
COLUMN_NAMES
)ON COMMIT DROP TABLE;
BEGIN
DECLARE V_PARAM_1 VARCHAR(8000);
DECLARE V_PARAM_2 VARCHAR(8000);
DECLARE CURSOR1 CURSOR WITH RETURN FOR
SELECT
COLUMN_NAMES
FROM SESSION.TEMP1
GROUP BY
COLUMN_NAMES
WITH UR;
SELECT
RTRIM(IFNULL(PARAM_1, '')) PARAM_1,
RTRIM(IFNULL(PARAM_2, '')) PARAM_2
INTO
V_PARAM_1,
V_PARAM_2
FROM
PARAM_TABLE
WHERE
PARAM_ID = ID;
INSERT INTO SESSION.TEMP1(COLUMN_NAMES)
SELECT COLUMNS FROM (....) WHERE(...);
OPEN CURSOR1;
END;
END P1#
The procedure consists of a select which returns values from the PARAM_TABLE to be passed as parameters to the original query. Multiple comma separated values are passed from the table to the query.
Thanks
In my stored procedure I have multiple similar variables #V1, #V2 ... #V20 (let's say 20 of them) FETCHED from a record. How would I use dynamic SQL to make 20 calls to another stored procedure using those variables as parameters?
Of course #V[i] syntax is incorrect, but it expresses the intent
fetch next from maincursor into #status, #V1, #V2, ...
while #i<21
begin
-- ??? execute sp_executesql 'SecondSP', '#myParam int', #myParam=#V[i]
-- or
-- ??? execute SecondSP #V[i]
set #i = #i+1
end
As others have said, set up a temporary table, insert the values that you need into it. Then "iterate" through it executing the necessary SQL from those values. This will allow you to have 0 to MANY values to be executed, so you don't have to set up a variable for each.
The following is a complete sample of how you may go about doing that without cursors.
SET NOCOUNT ON
DECLARE #dict TABLE (
id INT IDENTITY(1,1), -- a unique identity column for reference later
value VARCHAR(50), -- your parameter value to be passed into the procedure
executed BIT -- BIT to mark a record as being executed later
)
-- INSERT YOUR VALUES INTO #dict HERE
-- Set executed to 0 (so that the execution process will pick it up later)
-- This may be a SELECT statement into another table in your database to load the values into #dict
INSERT #dict
SELECT 'V1Value', 0 UNION ALL
SELECT 'V2Value', 0
DECLARE #currentid INT
DECLARE #currentvalue VARCHAR(50)
WHILE EXISTS(SELECT * FROM #dict WHERE executed = 0)
BEGIN
-- Get the next record to execute
SELECT
TOP 1 #currentid = id
FROM #dict
WHERE executed = 0
-- Get the parameter value
SELECT #currentvalue = value
FROM #dict
WHERE id = #currentid
-- EXECUTE THE SQL HERE
--sp_executesql 'SecondSP', '#myParam int', #myParam =
PRINT 'SecondSP ' + '#myParam int ' + '#myParam = ' + #currentvalue
-- Mark record as having been executed
UPDATE d
SET executed = 1
FROM #dict d
WHERE id = #currentid
END
Use a #TempTable
if you are at SQL Server 2005 you can create a #TempTable in the parent stored procedure, and it is available in the child stored procedure that it calls.
CREATE TABLE #TempTable
(col1 datatype
,col2 datatype
,col3 datatype
)
INSERT INTO #TempTable
(col1, col2, col3)
SELECT
col1, col2, col3
FROM ...
EXEC #ReturnCode=YourOtherProcedure
within the other procedure, you have access to #TempTable to select, delete, etc...
make that child procedure work on a set of data not on one element at a time
remember, in SQL, loops suck performance away!
Why not just use the table variable instead, and then just loop through the table getting each value.
Basically treat each row in a table as your array cell, with a table that has one column.
Just a thought. :)
This seems like an odd request - will you always have a fixed set of variables? What if the number changes from 20 to 21, and so on, are you constantly going to have to be declaring new variables?
Is it possible, instead of retrieving the values into separate variables, to return them each as individual rows and just loop through them in a cursor?
If not, and you have to use the individual variables as explained, here's one solution:
declare #V1 nvarchar(100)
set #V1 = 'hi'
declare #V2 nvarchar(100)
set #V2 = 'bye'
declare #V3 nvarchar(100)
set #V3 = 'test3'
declare #V4 nvarchar(100)
set #V4 = 'test4'
declare #V5 nvarchar(100)
set #V5 = 'end'
declare aCursor cursor for
select #V1
union select #V2 union select #V3
union select #V4 union select #V5
open aCursor
declare #V nvarchar(100)
fetch next from aCursor into #V
while ##FETCH_STATUS = 0
begin
exec TestParam #V
fetch next from aCursor into #V
end
close aCursor
deallocate aCursor
I don't really like this solution, it seems messy and unscalable. Also, as a side note - the way you phrased your question seems to be asking if there are arrays in T-SQL. By default there aren't, although a quick search on google can point you in the direction of workarounds for this if you absolutely need them.