Iterate and execute through CTE - sql

In #proc_name table variable, I have stored procedure name and I am passing parameters using dynamic SQL. I am using a while loop to loop through all rows in #proc_name. Can I use a CTE here to improve performance?
SELECT *
FROM #proc_name
WHILE (#count <= #max)
BEGIN
SET #proc_exec = 'usp_Balance_'
+Replace((SELECT Description FROM #proc_name WHERE rn= #count),' ','')+' '+' '+''''
+Replace((SELECT TellerID FROM #proc_name WHERE rn= #count),' ','')+''''+' ,'+''''
+#LocationID+''''+' , '+''''+cONvert(VARCHAR(100),#BusinessDate)+''''
-- Update TDrawerSummary
PRINT (#proc_exec)
SET #count = #count + 1
END
Thank you.

To start with, replace the WHILE loop by a cursor as shown below:
DECLARE c CURSOR FOR
SELECT 'usp_Balance_'
+Replace(Description,' ','')+' '+' '+''''
+Replace(TellerID,' ','')+''''+' ,'+''''
+#LocationID+''''+' , '+''''+cONvert(VARCHAR(100),#BusinessDate)+''''
FROM #proc_name
ORDER BY rn
OPEN c
FETCH c INTO #proc_exec
WHILE ##FETCH_STATUS=0
BEGIN
-- Update TDrawerSummary
PRINT #proc_exec
FETCH c INTO #proc_exec
END
CLOSE c
DEALLOCATE c
In some cases you could use a CTE to help convert procedural code into a query. This could improve performance. If you can share the code that is building #proc_name I could check if there is a possibility for this.
Inside the cursor you should consider to take some measures that will help you track down errors. In the current code, it will be quite hard to know in which iteration an error happened. A simplified example is shown below:
BEGIN TRY
-- Update TDrawerSummary
PRINT #proc_exec
END TRY
BEGIN CATCH
PRINT 'Failed executing '+COALESCE(#proc_exec,'(null)')+': '+ERROR_MESSAGE()
END CATCH

Related

SQL LOOP Pass values from Temp Table as parameters to stored procedure

Using SQL Server 2016, I have a huge query for our Finance Dept that uses #Year and #FinPeriod to match up transactions with a period. And for reporting we normally pass a single value into the stored procedure. But now we are required to populate the underlying tables with the data that would normally be generated on the fly.
Is there a loop anyone can help with please? I have a temp table with year values column and a finperiod for each of those years. I am looking to loop through this table - passing in both year and period to the stored procedure, one after the other until they have all been ran.
The stored procedure element is fine for me, just getting the loop/passing part to work would be a help.
So far I have:
declare #fiscalyearid numeric(9)
declare #FiscalYear numeric(9)
declare #FiscalMonthOfYear numeric(9)
declare #Year numeric(9)
declare #FinPeriod numeric(9)
if object_id('tempdb..#dateloop','u') is not null
drop table #dateloop
select distinct
identity(int,1,1) as ID,
FiscalYear_int, FiscalMonthOfYear
into
#dateloop
from
[DW].[DDS].[dimDate]
where
FiscalYear_int = '2018'
DECLARE C CURSOR LOCAL FAST_FORWARD FOR --
SELECT
ID, FiscalYear_int, FiscalMonthOfYear
FROM
#dateloop;
OPEN C;
FETCH C INTO #FiscalYear, #FiscalMonthOfYear;
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [dbo].[Origen_Reporting->SSRS_Capex_Monitoring_Report_Expenditure] #Year, #FinPeriod
FETCH C INTO #Year,#FinPeriod
END
CLOSE C;
DEALLOCATE C;
Any tips would be brilliant. Thank you
I guess you want your Cursor logic to work. Below is the code you can use to loop through your dates and call proc in loop.
DECLARE C CURSOR LOCAL FAST_FORWARD FOR --
SELECT
FiscalYear_int, FiscalMonthOfYear
FROM
#dateloop;
OPEN C;
Fetch next from c into #FiscalYear, #FiscalMonthOfYear
WHILE ##FETCH_STATUS = 0
BEGIN
select #FiscalYear, #FiscalMonthOfYear --exec proc passing these values
EXEC [dbo].[Origen_Reporting->SSRS_Capex_Monitoring_Report_Expenditure] #FiscalYear, #FiscalMonthOfYear
FETCH next from c INTO #FiscalYear,#FiscalMonthOfYear
END
CLOSE C;
DEALLOCATE C;

iterative executing stored procedure with a set based approach

I have an issue where I am trying to replace the following code with a different solution. Currently I am using a cursor but it is running to slowly. I am under the understanding that iterative solutions can only be completed with cursors or while loops but I am trying to find a set based approach and running out of ideas. I was hoping that I could find some inspiration here. Thanks all.
--used to find a unique list of Some_ID
#Id1, #Id2, #Id3
DECLARE SomeCursor CURSOR FOR
SELECT SOME_ID FROM SomeTable
WHERE ID1=#Id1 AND ID2=#Id2 and ID3=#Id3
OPEN SomeCursor
FETCH NEXT FROM SomeCursor INTO #SomeID
WHILE ##Fetch_Status = 0
BEGIN
Print #SomeID
--simply populates a single table with values pulled from
--other tables in the database based on the give parameters.
EXEC SP_PART1 #SomeID, #parameters...
print 'part 2 starting'
EXEC SP_PART2 #SomeID, #parameters...
FETCH NEXT FROM SomeCursor INTO #SomeID
print getdate()
END
CLOSE SomeCursor;
DEALLOCATE SomeCursor;
Your only option to make this set-based is to rewrite the sps to make them set-based (using table-valed parameters intead of individual ones) or to write set based code in this proc instead of re-using procs designed for single record use. This is a case where code re-use is usually not appropriate.
I'm not too sure what you want, but why not use your select statement to create your sql scripts and execute them all at once with something like this.
DECLARE #sql VARCHAR(MAX);
SELECT #sql = COALESCE(#sql,'') + 'EXEC SP_Part1 ' + SOME_ID + '; EXEC SP_Part2 ' + SomeID + '; GO '
FROM SomeTable
WHERE ID1=#Id1 AND ID2=#Id2 and ID3=#Id3
EXEC (#sql)

How to get the first row details using rowcount?

I have a temp variable called #rows having nearly 10000 records in a stored procedure like this
Create Procedure input
as
begin
declare #input_data table(......)
insert into (.....) from ....
#rows= select ##rowcount
while(#rows > o)
begin
--- I need to process each row like
select ... where #row=1 --like this repeatedly upto #rows = 10000
end
How should I achieve this.Please help me
Thanks in advance
You can directly update the table based on some conditiuons and using CASE statement insted of using while loop.
You may achieve your goal using a CURSOR
DECLARE #ID AS INT
DECLARE TestCursor CURSOR
FOR (SELECT ID FROM Test)
OPEN TestCursor
FETCH NEXT FROM TestCursor INTO #ID
WHILE ##Fetch_Status = 0
BEGIN
--Your Code Here
PRINT #ID --Print For Testing
FETCH NEXT FROM TestCursor INTO #ID
END
CLOSE TestCursor
DEALLOCATE TestCursor
Remember : using cursors will lead to a performance loss
Instead use CASE statements in queries for conditional selections/updates, as described in another answer

In T-SQL / SQL Server 2000, referencing a particular row of a result set

I want to reference the nth row of the #temptable (at the second SQL comment is below). What expression will allow me to do so?
DECLARE #counter INT
SET #counter = 0
WHILE (#counter<count(#temptable))
--#temptable has one column and 0 or more rows
BEGIN
DECLARE #variab INT
EXEC #variab = get_next_ticket 3906, 'n', 1
INSERT INTO Student_Course_List
SELECT #student_id,
-- nth result set row in #temptable, where n is #count+1
#variab
SET #counter = #counter +1
END
Cursor (will this work?):
for record in (select id from #temptable) loop
--For statements, use record.id
end loop;
Normally in a relational database like SQL Server, you prefer to do set operations. So it would be best to simply have INSERT INTO tbl SOMECOMPLEXQUERY even with very complex queries. This is far preferable to row processing. In a complex system, using a cursor should be relatively rare.
In your case, it would appear that the get_next_ticket procedure performs some significant logic which is not able to be done in a set-oriented fashion. If you cannot perform it's function in an alternative set-oriented way, then you would use a CURSOR.
You would declare a CURSOR on your set SELECT whatever FROM #temptable, OPEN it, FETCH from the cursor into variables for each column and then use them in the insert.
Instead of using a while loop (with a counter like you are doing) to iterate the table you should use a cursor
Syntax would be:
DECLARE #id int
DECLARE c cursor for select id from #temptable
begin
open c
fetch next from c into #id
WHILE (##FETCH_STATUS = 0)
BEGIN
--Do stuff here
fetch next from c into #id
END
close c
deallocate c
end

Dynamic cursor used in a block in TSQL?

I have the following TSQL codes:
-- 1. define a cursor
DECLARE c_Temp CURSOR FOR
SELECT name FROM employees;
DECLARE #name varchar(100);
-- 2. open it
OPEN c_Temp;
-- 3. first fetch
FETCH NEXT FROM c_Temp INTO #name;
WHILE ##FETCH_STATUS = 0
BEGIN
print #name;
FETCH NEXT FROM c_Temp INTO #name; -- fetch again in a loop
END
-- 4. close it
....
I use the name value only in a loop block. Here I have to
define a cursor variable,
open it,
fetch twice and
close it.
In PL/SQL, the loop can be like this:
FOR rRec IN (SELECT name FROM employees) LOOP
DBMS_OUTPUT.put_line(rRec.name);
END LOOP;
It is much simpler than my TSQL codes. No need to define a cursor. It is created dynamically which is accessible within a loop block (much like C# for loop). Not sure if there something similar like this in TSQL?
Something along these lines might work for you, although it depends on having an ID column or some other unique identifier
Declare #au_id Varchar(20)
Select #au_id = Min(au_id) from authors
While #au_id IS NOT NULL
Begin
Select au_id, au_lname, au_fname from authors Where au_id = #au_id
Select #au_id = min(au_id) from authors where au_id > #au_id
End
Cursors are evil in Sql Server as they can really degrade performance - my favoured approach is to use a Table Variable (>= Sql Server 2005) with an auto inc ID column:
Declare #LoopTable as table (
ID int identity(1,1),
column1 varchar(10),
column2 datetime
)
insert into #LoopTable (column1, column2)
select name, startdate from employees
declare #count int
declare #max int
select #max = max(ID) from #LoopTable
select #count = 1
while #count <= #max
begin
--do something here using row number '#count' from #looptable
set #count = #count + 1
end
It looks pretty long winded however works in any situation and should be far more lightweight than a cursor
Since you are coming from an Oracle background where cursors are used frequently, you may not be aware that in SQl Server cursors are performance killers. Depending on what you are actually doing (surely not just printing the variable), there may be a much faster set-based solution.
In some cases, its also possible to use trick like this one:
DECLARE #name VARCHAR(MAX)
SELECT #name = ISNULL(#name + CHAR(13) + CHAR(10), '') + name
FROM employees
PRINT #name
For a list of employee names.
It can also be used to make comma-separated string, just replace + CHAR(13) + CHAR(10) with + ', '
Why not simply just return the recordset using a select statement. I assume the object is to copy and paste the values in the UI (based on the fact that you are simply printing the output)? In Management studio you can copy and paste from the grid, or press +T and then run the query and return the results as part of the messages tab in plain text.
If you were to run this via an application, the application wouldn't be able to access the printed statements as they are not being returned within a recordset.