Removal of cursor which is processing bad data - sql

How can we remove cursor in the following scenario:
DECLARE #Tablesq TABLE (numbercol INT)
INSERT INTO #Tablesq
SELECT 25 UNION all
SELECT -25 UNION all
SELECT 25 UNION all
SELECT 36
DECLARE #number INT
DECLARE sqrtcur CURSOR FOR SELECT numbercol FROM #tablesq
OPEN sqrtcur
FETCH NEXT FROM sqrtcur INTO #number
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
SELECT SQRT(#number)
END TRY
BEGIN CATCH
print ERROR_MESSAGE();
END CATCH
FETCH NEXT FROM sqrtcur INTO #number
END
CLOSE sqrtcur
DEALLOCATE sqrtcur
Here -25 value is causing the error and because of which I am forced to use the cursor. Can we do a set-based operation to achieve the same result?

SQL Server does not provide very good error handling within a SELECT statement. The one exception is for type conversion and the try_ functions -- but even that is relatively recently in the history of the database.
SQL is not alone in this. The standard just does not address this point.
The one thing you can do is use a case expression, if you understand the conditions when you will get an error:
select (case when numbercol >= 0 then sqrt(numbercol) end)
from tablesq;
SQL Server guarantees that the when conditions are evaluated in order, stopping at the first match, and the then is evaluated after then when. I should say "under almost all circumstances"; there are some optimizations made in aggregation queries that somewhat weaken that statement.
Many other databases have similar limitations. And in most of them, you can use a user-defined function that catches the exception. Alas, SQL Server does not allow try/catch blocks in UDFs, so this is not an option.

Are you looking for this:
SELECT SQRT(numbercol)
FROM #Tablesq
WHERE numbercol > 0;

You can do it this way :
DECLARE #tablesq TABLE (numbercol INT)
INSERT INTO #tablesq
SELECT 25 UNION all
SELECT -25 UNION all
SELECT 25 UNION all
SELECT 36;
DECLARE #number INT;
DECLARE sqrtcur CURSOR FOR SELECT numbercol FROM #tablesq;
DECLARE #TEST FLOAT;
OPEN sqrtcur;
FETCH NEXT FROM sqrtcur INTO #number;
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
SET #TEST = SQRT(#number);
SELECT SQRT(#number);
END TRY
BEGIN CATCH
print ERROR_MESSAGE();
END CATCH;
FETCH NEXT FROM sqrtcur INTO #number;
END;
CLOSE sqrtcur;
DEALLOCATE sqrtcur;

Related

SQL - SELECT multiple rows and then INSERT them INTO another table/database

I want to SELECT multiple rows and then INSERT them INTO another table/database.
My current query only works with 1 result, I need it to work with for example, 100:
DECLARE #var INT;
SELECT
#var = column
FROM
database.dbo.table1
-- this will produce for example, 100 results
IF (#var IS NULL) -- which it is all 100 times
INSERT INTO database.dbo.table2
(column)
VALUES
(#var)
How do I do this, can this even be done?
I'm using Microsoft SQL Server Management Studio 2016.
I assume you want:
INSERT INTO database.dbo.table2(column)
SELECT column
FROM database.dbo.table1
WHERE column IS NULL;
You can use cursor for insert the data like below
DECLARE #var INT;
Declare AIX Cursor for
SELECT column FROM database.dbo.table1;
Open AIX;
Fetch Next from AIX into #var;
-- this will produce for example, 100 results
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#var IS NULL) -- which it is all 100 times
INSERT INTO database.dbo.table2
(column)
VALUES
(#var)
FETCH NEXT FROM AIX
INTO #var;
END
CLOSE AIX;
DEALLOCATE AIX;

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;

TSQL not causing infinite loop

Please see the code below:
declare #crimeurn varchar(20)
DECLARE #finalresults TABLE (crime_urn varchar(20))
DECLARE #potentialresults TABLE (crime_urn varchar(20))
insert into #finalresults values ('1')
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
OPEN finalresults_cursor
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
WHILE ##FETCH_STATUS = 0
BEGIN
print #crimeurn
INSERT INTO #finalresults
values ('2')
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
END
select * from #finalresults --line 16
CLOSE finalresults_cursor
DEALLOCATE finalresults_cursor
Line 16 displays 5137 or 12,342 rows in SQL studio manager (it randomly varies). I expected the TSQL to cause an infinite loop because there is an insert into the table variable on every iteration of the cursor.
Why does it not cause an infinite loop? i.e. why are there 5,137 or 12,342 rows returned.
You are inserting into a heap.
A heap is unordered. There is no particular guarantee that the row will be inserted after the current row and picked up on the next fetch.
I made a slight amend to your test framework and added an IDENTITY column. In my case it got to row 592,353 before terminating.
As you can see from the results below this final row happened to be inserted on an earlier page in the file (jumped from 1623 to 184) so an allocation ordered scan starting from the penultimate row wouldn't find it.
Code to reproduce.
declare #crimeurn varchar(20)
DECLARE #finalresults TABLE (crime_urn varchar(20), ID int identity)
DECLARE #potentialresults TABLE (crime_urn varchar(20))
insert into #finalresults values ('1')
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
OPEN finalresults_cursor
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
WHILE ##FETCH_STATUS = 0
BEGIN
print #crimeurn
INSERT INTO #finalresults
--OUTPUT INSERTED.ID
values ('2')
FETCH NEXT FROM finalresults_cursor INTO #crimeurn
END
select *, sys.fn_PhysLocFormatter(%%physloc%%) from #finalresults --line 16
ORDER BY ID
CLOSE finalresults_cursor
DEALLOCATE finalresults_cursor
Edit: The information below is wrong, but I've left it because that's how I believe it's supposed to work.
By default, cursors do not run in INSENSITIVE or STATIC mode. By default cursors are DYNAMIC and OPTIMISTIC. The documentation on cursors doesn't mention how dynamic cursors behave with respect to INSERTS. INSERT behavior appears to be undocumented.
You may be able to fix this with the SCROLL_LOCKS option, which guarantees order preservation.
Because the cursor's definition is fixed when you run
DECLARE finalresults_cursor CURSOR FOR
SELECT crime_urn FROM #finalresults
It's static after that point. Updating the table variable #finalresults doesn't change the cursor finalresults_cursor.
It's like this:
X = 10
Y = X
X = 20
PRINT X, Y
Outputs this:
20 10
However, if you do not care or know the type of the cursor, you can use the ##CURSOR_ROWS inside your loop to do some "cursor" logic :) .
Here is some documentation on the possible values the ##CURSOR_ROWS variable can have, depending on the cursor's type: .

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