How do I select multiple columns in a cursor?
I tested the below code but it's returning/printing nothing.
DECLARE #DateAdded VARCHAR(50)
DECLARE #IdEmployee NVARCHAR(20)
DECLARE #EmailAddress VARCHAR(100)
DECLARE #Subject VARCHAR(50)
DECLARE #Message VARCHAR(100)
DECLARE #DateSent VARCHAR(50)
-- 2 - Declare Cursor
DECLARE db_cursor CURSOR FOR
-- Populate the cursor with your logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
SELECT
#DateAdded, #IdEmployee, #EmailAddress,
#Subject, #Message, #DateSent
FROM #tblLeaveNotifToApprover
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #DateAdded, #IdEmployee, #EmailAddress, #Subject, #Message, #DateSent
WHILE ##FETCH_STATUS = 0
BEGIN
-- 4 - Begin the custom business logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
PRINT CONVERT(VARCHAR,#DateAdded)
FETCH NEXT FROM db_cursor INTO #DateAdded, # IdEmployee, #EmailAddress, #Subject, #Message, #DateSent
END
CLOSE db_cursor
DEALLOCATE db_cursor
Initially wanted to print all that columns in each row, but after running the snippet/code it nothing displays, I also tried converting the variables to varchar same result is thrown.
I expect that all the columns of my 7 rows will be print out.
This is wrong
DECLARE db_cursor CURSOR FOR
-- Populate the cursor with your logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
SELECT
#DateAdded, #IdEmployee, #EmailAddress,
#Subject, #Message, #DateSent
FROM #tblLeaveNotifToApprover
You need to replace the variables in this SELECT statement with the actual column names from the table. You don't refer to your local variables until the FETCH statement.
Think of your CURSOR as a selected set of data from the source tables that you will then iterate through.
Your snippet contains the hints to identify what is in the CURSOR, in your case refer to
-- Populate the cursor with your logic
-- * UPDATE WITH YOUR SPECIFIC CODE HERE *
In your case, your are trying to populate your CURSOR using the values from the variables you have declared using the statement:
SELECT #DateAdded,
#IdEmployee,
#EmailAddress,
#Subject,
#Message,
#DateSent
FROM #tblLeaveNotifToApprover
Now, since you have not populated those variables yet they are all NULL
What you then do is SELECT the variables that you have just declared back into the variables !!!!!
FETCH NEXT FROM db_cursor INTO #DateAdded,#IdEmployee,#EmailAddress,#Subject,#Message,#DateSent
WHILE ##FETCH_STATUS = 0
So it is little surprise that when you try to print these you get nothing returned.
Go back to your DECLARE CURSOR and ensure that you are actually selecting a data set to iterate through. a CURSOR should generally always be kind of like a temporary table. If we assume that your temp table #tblLeaveNotifToApprover has column names that match your variable names then you need:
DECLARE db_cursor CURSOR FOR
SELECT DateAdded,
IdEmployee,
EmailAddress,
Subject,
Message,
DateSent
FROM #tblLeaveNotifToApprover
Related
I'm trying to iterate over some tables and clear all records.
My code is the following :
DECLARE #table varchar(100)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
print #table
delete from #table
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
But I receive "Must declare the table variable "#table" at the line "delete..."
I can't see the error.
Thank you
You shoud use dynamic query,
DECLARE #table varchar(100)
,#v_str nvarchar(200)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
print #table
set #v_str = 'delete from '+#table
exec(#v_str)
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
You need dynamic delete statement... Try this :
DECLARE #cmd VARCHAR(4000)
DECLARE #table varchar(100)
DECLARE db_cursor CURSOR FOR select name from sys.tables where name like '%cfe_%'
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'DELETE FROM '+#table
EXEC (#cmd)
FETCH NEXT FROM db_cursor INTO #table
END
CLOSE db_cursor
DEALLOCATE db_cursor
Even better would be to not use a cursor here. Looping in sql is a last resort. Also, your query is not going to do exactly what you think it will because you are using like and wanting to find an underscore. The underscore in a LIKE predicate requires it to be escaped with square brackets. As posted your query will return any table with cfe in the name not cfe_.
Once you are comfortable that the dynamic sql string is what you want you can uncomment it to execute it.
declare #SQL nvarchar(max) = ''
select #SQL = #SQL + 'delete from ' + name + ';'
from sys.tables
where name like '%cfe[_]%'
select #SQL
--exec sp_executesql #SQL
We can also use while loop for this process
DECLARE #Min Int,#max Int
IF Object_id('Tempdb..#DeleteList')IS NOT NULL
DROP TABLE #DeleteList
CREATE TABLE #DeleteList (Id Int Identity,Name varchar(100))
INSERT INTO #DeleteList(Name)
SELECT name FROM sys.tables WHERE CHARINDEX('cfe_',name)>0
SELECT #Min=Min(Id) FROm #DeleteList
SELECT #max=MAX(Id) FROm #DeleteList
While(#Min<=#max)
Begin
Declare #TableName Varchar(50),
#Sql Nvarchar(max)
SELECT #TableName=Name From #DeleteList Where id=#Min
SET #Sql='DELETE From '+#TableName
Exec (#Sql)
SET #Min=#Min+1
END
But if the deleting tables have foreign key refrences it will throw error so that first you need delete records from child and then go to Parent table
You will need to do something like;
EXEC sp_executesql #statement = N'DELETE FROM ' + #table
because currently you are trying to delete from a String variable, not the table named the same as the variable
I have the table contents as shown in this picture:
The formula column contains the the column names as variables in the formula expression.
I am trying to get a computed column Score corresponding for each emp_id,Tsk_id and processed date which would be calculated dynamically based on the formulae given in the formula column and push it to a temp table
I have tried to achieve it by dynamic SQL within a cursor using the below code which has been successful. Can Anyone suggest a better way to do it, maybe using a CTE or something ?
DECLARE #EMP_ID nVARCHAR(255)
DECLARE #TSK_ID nVARCHAR(255)
DECLARE #PROCESSEDDATE nVARCHAR(255)
DECLARE #SQLCMD nVARCHAR(max)
DECLARE #SQLTEXT nvarchar(max)
DECLARE db_cursor CURSOR FOR
SELECT EMP_ID,TSK_ID,PROCESSEDDATE from dbo.[Formula_Cal] group by EMP_ID,TSK_ID,PROCESSEDDATE
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #EMP_ID,#TSK_ID,#PROCESSEDDATE
WHILE ##FETCH_STATUS = 0
BEGIN
Set #SQLTEXT = (Select formula from dbo.[Formula_Cal] where EMP_ID=#EMP_ID and TSK_ID =#TSK_ID and PROCESSEDDATE=#PROCESSEDDATE )
Set #SQLCMD ='select emp_id,TSK_ID,convert(decimal(18,2),( ' +#SQLTEXT+ ')) As Score FROM [Formula_Cal] where TSK_ID = '+#TSK_ID+' and EMP_ID = '+#EMP_ID
--Select #SQLTEXT
--Select #SQLCMD
insert into dbo.TMP_ACHSCR(emp_id,TSK_id,Score)
Exec sp_executesql #SQLCMD
FETCH NEXT FROM db_cursor INTO #EMP_ID,#TSK_ID,#PROCESSEDDATE
END
CLOSE db_cursor
DEALLOCATE db_cursor
No this is easiest way (with minimal rewriting). You can access this with rewriting our formulas into function.
I have two cursors, I want to loop through first one, and use its values as parameters for the second cursor and then do some processing. I know how to do this in PL-SQL, but T-SQL puzzles me.
I have gotten so far. For some reason the nested cursor does not print anything.
DECLARE #period DATE
DECLARE #table_type VARCHAR(2)
DECLARE #clmn_clr VARCHAR(100)
DECLARE #clmn_per VARCHAR(100)
DECLARE #period_num INT
DECLARE #period_date DATE
DECLARE #table_type2 VARCHAR(2)
DECLARE c_table CURSOR FOR SELECT period,
table_type
FROM #period_table
DECLARE c_period CURSOR LOCAL FAST_FORWARD FOR SELECT clmn_clr,
clmn_per,
period_num,
period_date,
table_type
FROM #column_period
WHERE table_type = #table_type
OPEN c_table
FETCH NEXT FROM c_table
INTO #period, #table_type
WHILE ##FETCH_STATUS = 0
BEGIN
print #period
print #table_type
print '------------'
FETCH NEXT FROM c_table
INTO #period, #table_type
--Nested cursor
OPEN c_period
FETCH NEXT FROM c_period
INTO #clmn_clr, #clmn_per, #period_num, #period_date, #table_type2
BEGIN
print #clmn_clr
print #clmn_per
print #period_num
print #period_date
print #table_type2
FETCH NEXT FROM c_period
INTO #clmn_clr, #clmn_per, #period_num, #period_date, #table_type2
END
CLOSE c_period
DEALLOCATE c_period
END
CLOSE c_table
DEALLOCATE c_table
It does print the first line from cursor c_table but that is all.
Also, any comments for how to do this in T-SQL better are very welcomed.
There is no need in nesting, here is the only cursor that can do everything:
DECLARE c_period CURSOR LOCAL FAST_FORWARD FOR
SELECT p.period,
c.clmn_clr,
c.clmn_per,
c.period_num,
c.period_date,
c.table_type
FROM #period_table p
INNER JOIN #column_period c
ON c.table_type = p.table_type
ORDER BY p.table_type
I never tested it but id does not seem to me that MSSQL allows something like "dynamic parameter binding" for cursor. Since you opened it - it's done. It only can react on data modifications if appripriate options applied.
You may reopen cursor if you still need nesting for any purpose. Fetch #table_type from outer cursor, then DECLARE ... OPEN nested cursor.
Also note that strings to print (if this is your real subject) can be obtained with single select, withot any cursors (search "string aggregation" for mssql).
Here is working solution I have found - I only moved the nested cursor inside.
DECLARE #period DATE
DECLARE #table_type VARCHAR(2)
DECLARE #clmn_clr VARCHAR(100)
DECLARE #clmn_per VARCHAR(100)
DECLARE #period_num INT
DECLARE #period_date DATE
DECLARE #table_type2 VARCHAR(2)
DECLARE c_table CURSOR FOR SELECT period,
table_type
FROM #period_table
--Fetch first row
OPEN c_table
FETCH NEXT FROM c_table
INTO #period, #table_type
--Loop starts here, until it is empty
WHILE ##FETCH_STATUS = 0
BEGIN
print #period
print #table_type
print '----------'
--Nested cursor
DECLARE c_period CURSOR LOCAL FAST_FORWARD FOR SELECT clmn_clr,
clmn_per,
period_num,
period_date,
table_type
FROM #column_period
WHERE table_type = #table_type
OPEN c_period
FETCH NEXT FROM c_period
INTO #clmn_clr, #clmn_per, #period_num, #period_date, #table_type2
WHILE ##FETCH_STATUS = 0
BEGIN
print #clmn_clr
print #clmn_per
print #period_num
print #period_date
print #table_type2
FETCH NEXT FROM c_period
INTO #clmn_clr, #clmn_per, #period_num, #period_date, #table_type2
END
CLOSE c_period
DEALLOCATE c_period
FETCH NEXT FROM c_table
INTO #period, #table_type
END
CLOSE c_table
DEALLOCATE c_table
I am writing a Scalar function for my web application. I want to calculate the date difference between an employee's Join Date and Resign Date.
I have got most of my code working, but I just cannot figure out how to use parameter variables in cursor.
Let say I have this block of code
Declare myCursor Cursor for
Declare #join_date datetime
Declare #resign_date datetime
Declare #emp_stat nvarchar(50)
--What I have been trying to do. (Not working)
Select #join_date = Convert(datetime, join_date), #emp_stat = Convert(datetime, emp_stat) from t_emp_info where .....
OPEN product_cursor
WHILE ##FETCH_STATUS = 0
BEGIN
if (#emp_stat = 'P')
Begin
//DateDiff .....
End
FETCH NEXT FROM vendor_cursor
INTO #join_date , #emp_stat
End
Close myCursor
DEALLOCATE myCursor;
I can't get this working, but what I want is I want to store the values in the parameter so I can use it in the if condition statement. Not sure how to fix this. Help will be appreciated
Your cursor names are mixed up and your assignment is in the wrong place. Try:
Declare myCursor Cursor for
Select join_date, emp_stat, resign_date from t_emp_info where .....
Declare #join_date datetime
Declare #resign_date datetime
Declare #emp_stat nvarchar(50)
OPEN myCursor
FETCH NEXT FROM myCursor
INTO #join_date , #emp_stat , #resign_date
WHILE ##FETCH_STATUS = 0
BEGIN
//Your logic here
FETCH NEXT FROM myCursor
INTO #join_date , #emp_stat, #resign_date
End
Close myCursor
DEALLOCATE myCursor;
However you should avoid the cursor. What is wrong with:
SELECT DATEDIFF(day, MAX(join_date), case when emp_stat = 'P' then getdate() else MAX(resign_date) end)
from t_emp_info
GROUP BY employee_id
Or similar?
We have triggers on a table called OSPP to save specific data to a table for later use.
I get the following error in SAP when adding more than one line at a time to the table.
Invalid Cursor State
We have SQL Server 2005 SP3 (but I tried it on a clean 2005 install, on SP1 and SP2)
The one trigger :
CREATE TRIGGER [dbo].[tr_OSPP_Insert]
ON [dbo].[OSPP]
FOR INSERT
AS
BEGIN
Declare #ItemCode varchar(255)
Declare #CardCode varchar(255)
Declare #Price decimal(18,2)
Declare #ListNum bigint
Declare #ID bigint
Declare #Remote char(1)
DECLARE db_cursor CURSOR FOR
SELECT ItemCode, CardCode, Price, ListNum
FROM INSERTED
OPEN db_cursor
FETCH NEXT
FROM db_cursor INTO #ItemCode, #CardCode, #Price, #ListNum
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Remote = isnull(U_Remote, 'N') FROM OITM WHERE ItemCode = #ItemCode
IF ltrim(rtrim(upper(#Remote))) = 'Y'
BEGIN
SELECT #ID = U_ID FROM [dbo].[#BDS_MAINTENANCE]
UPDATE [dbo].[#BDS_MAINTENANCE] set U_ID = U_ID + 1
INSERT INTO [dbo].[#BDS_REMOTESPECIALPRICELIST]
(
Code,
[Name],
U_ID,
U_ItemCode,
U_CardCode,
U_Price,
U_ListNum,
U_TransactionType,
U_Uploaded
) VALUES (
#ID,
'_' + cast(#ID as VARCHAR(50)),
#ID,
#ItemCode,
#CardCode,
#Price,
#ListNum,
1,
0
)
FETCH NEXT
FROM db_cursor INTO #ItemCode, #CardCode, #Price, #ListNum
END
CLOSE db_cursor
DEALLOCATE db_cursor
END
END
We also tried :
CREATE TRIGGER [dbo].[tr_OSPP_Insert]
ON [dbo].[OSPP]
FOR INSERT
AS
BEGIN
SELECT * INTO [#TEMPTABLE222] FROM INSERTED
END
But still get the same error.
Do you guys have any idea what is wrong?
Thanks in advance!
I count three Begins, and three Ends. But it's the second pair that represent the cursor loop - so I'd move your Close/Deallocate to be after the second End, rather than before. E.g:
FETCH NEXT
FROM db_cursor INTO #ItemCode, #CardCode, #Price, #ListNum
END
CLOSE db_cursor
DEALLOCATE db_cursor
END
Probably needs to be:
END
FETCH NEXT
FROM db_cursor INTO #ItemCode, #CardCode, #Price, #ListNum
END
CLOSE db_cursor
DEALLOCATE db_cursor
(I've also moved the fetch next one level out, since otherwise you only move the cursor forwards inside your IF condition)
And one style comment (can't resist). It's generally considered good practice to SET NOCOUNT ON within the body of a trigger, to avoid sending lots of extra n rows affected messages.