I'm attempting to port the database for NetSqlAzMan to Azure. I'm running into a problem with a few of the stored procedures.
SET #member_cur = CURSOR STATIC FORWARD_ONLY FOR SELECT * FROM #RESULT
OPEN #member_cur
results in error message:
Msg 16948, Level 16, State 4
Procedure
netsqlazman_GetApplicationGroupSidMembers,
Line 118
The variable '#member_cur' is
not a cursor variable, but it is used
in a place where a cursor variable is
expected.
The store procedure script was created by exporting an empty NetSQLAzMan database in SQLAzure format. Any tips for handling cursor variables in SQL Azure? I don't see much for documentation on this.
Here is the stored procedure if that helps. Please pardon the verbosity. The error references the bottom of the stored procedure.
CREATE PROCEDURE [dbo].[netsqlazman_GetApplicationGroupSidMembers]
#ISMEMBER [bit],
#GROUPOBJECTSID [varbinary](85),
#NETSQLAZMANMODE [bit],
#LDAPPATH [nvarchar](4000),
#member_cur [int] OUTPUT
WITH EXECUTE AS CALLER
AS
DECLARE #RESULT TABLE (objectSid VARBINARY(85))
DECLARE #GROUPID INT
DECLARE #GROUPTYPE TINYINT
DECLARE #LDAPQUERY nvarchar(4000)
DECLARE #sub_members_cur CURSOR
DECLARE #OBJECTSID VARBINARY(85)
SELECT #GROUPID = ApplicationGroupId, #GROUPTYPE = GroupType, #LDAPQUERY = LDapQuery FROM [netsqlazman_ApplicationGroupsTable] WHERE objectSid = #GROUPOBJECTSID
IF #GROUPTYPE = 0 -- BASIC
BEGIN
--memo: WhereDefined can be:0 - Store; 1 - Application; 2 - LDAP; 3 - Local; 4 - Database
-- Windows SIDs
INSERT INTO #RESULT (objectSid)
SELECT objectSid
FROM dbo.[netsqlazman_ApplicationGroupMembersTable]
WHERE
ApplicationGroupId = #GROUPID AND IsMember = #ISMEMBER AND
((#NETSQLAZMANMODE = 0 AND (WhereDefined = 2 OR WhereDefined = 4)) OR (#NETSQLAZMANMODE = 1 AND WhereDefined BETWEEN 2 AND 4))
-- Store Groups Members
DECLARE #MemberObjectSid VARBINARY(85)
DECLARE #MemberType bit
DECLARE #NotMemberType bit
DECLARE nested_Store_groups_cur CURSOR LOCAL FAST_FORWARD FOR
SELECT objectSid, IsMember FROM dbo.[netsqlazman_ApplicationGroupMembersTable] WHERE ApplicationGroupId = #GROUPID AND WhereDefined = 0
OPEN nested_Store_groups_cur
FETCH NEXT FROM nested_Store_groups_cur INTO #MemberObjectSid, #MemberType
WHILE ##FETCH_STATUS = 0
BEGIN
-- recursive call
IF #ISMEMBER = 1
BEGIN
IF #MemberType = 0
SET #NotMemberType = 0
ELSE
SET #NotMemberType = 1
END
ELSE
BEGIN
IF #MemberType = 0
SET #NotMemberType = 1
ELSE
SET #NotMemberType = 0
END
EXEC dbo.[netsqlazman_GetStoreGroupSidMembers] #NotMemberType, #MemberObjectSid, #NETSQLAZMANMODE, #LDAPPATH, #sub_members_cur OUTPUT
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
WHILE ##FETCH_STATUS=0
BEGIN
INSERT INTO #RESULT VALUES (#OBJECTSID)
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
END
CLOSE #sub_members_cur
DEALLOCATE #sub_members_cur
FETCH NEXT FROM nested_Store_groups_cur INTO #MemberObjectSid, #MemberType
END
CLOSE nested_Store_groups_cur
DEALLOCATE nested_Store_groups_cur
-- Application Groups Members
DECLARE nested_Application_groups_cur CURSOR LOCAL FAST_FORWARD FOR
SELECT objectSid, IsMember FROM dbo.[netsqlazman_ApplicationGroupMembersTable] WHERE ApplicationGroupId = #GROUPID AND WhereDefined = 1
OPEN nested_Application_groups_cur
FETCH NEXT FROM nested_Application_groups_cur INTO #MemberObjectSid, #MemberType
WHILE ##FETCH_STATUS = 0
BEGIN
-- recursive call
IF #ISMEMBER = 1
BEGIN
IF #MemberType = 0
SET #NotMemberType = 0
ELSE
SET #NotMemberType = 1
END
ELSE
BEGIN
IF #MemberType = 0
SET #NotMemberType = 1
ELSE
SET #NotMemberType = 0
END
EXEC dbo.[netsqlazman_GetApplicationGroupSidMembers] #NotMemberType, #MemberObjectSid, #NETSQLAZMANMODE, #LDAPPATH, #sub_members_cur OUTPUT
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
WHILE ##FETCH_STATUS=0
BEGIN
INSERT INTO #RESULT VALUES (#OBJECTSID)
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
END
CLOSE #sub_members_cur
DEALLOCATE #sub_members_cur
FETCH NEXT FROM nested_Application_groups_cur INTO #MemberObjectSid, #MemberType
END
CLOSE nested_Application_groups_cur
DEALLOCATE nested_Application_groups_cur
END
ELSE IF #GROUPTYPE = 1 AND #ISMEMBER = 1 -- LDAP QUERY
BEGIN
EXEC dbo.[netsqlazman_ExecuteLDAPQuery] #LDAPPATH, #LDAPQUERY, #sub_members_cur OUTPUT
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
WHILE ##FETCH_STATUS=0
BEGIN
INSERT INTO #RESULT (objectSid) VALUES (#OBJECTSID)
FETCH NEXT FROM #sub_members_cur INTO #OBJECTSID
END
CLOSE #sub_members_cur
DEALLOCATE #sub_members_cur
END
SET #member_cur = CURSOR STATIC FORWARD_ONLY FOR SELECT * FROM #RESULT
OPEN #member_cur
GO
I don't think this problem is with cursors so much but more with the declaration of the output variable. If you look at the start of the stored procedure you have this:
#member_cur [int] OUTPUT
Where #member_cur is defined as being an integer. You're then trying to assign it to have the value of a cursor and it is rightly complaining. What I'm surprised about is that this is what was generated from a non-Azure SQL Server. Either way it looks like SQL Azure doesn't support this, so either change the type of the output parameter, or open your cursor in a different variable and assign #member_cur to be that value.
Related
I have a doubt regarding the variable declaration in a nested cursors scenario.
This is an small nested cursor sample that i found. In other samples I've seen I also find DECLARE clauses inside the first cursor.
DECLARE #ClientID int;
DECLARE Cur1 CURSOR FOR SELECT ClientID From Folder;
OPEN Cur1
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
WHILE #FETCH_Cur1 = 0
BEGIN
DECLARE #UID int;
DECLARE Cur2 CURSOR FOR SELECT UID FROM Attend Where ClientID=#ClientID;
OPEN Cur2;
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
WHILE #FETCH_Cur2 = 0
BEGIN
PRINT 'Found UID: ' + Cast(#UID as Varchar);
FETCH NEXT FROM Cur2 INTO #UID;
SELECT #FETCH_Cur2 = ##FETCH_STATUS
END;
CLOSE Cur2;
DEALLOCATE Cur2;
FETCH NEXT FROM Cur1 INTO #ClientID;
SELECT #FETCH_Cur1 = ##FETCH_STATUS
END;
PRINT 'DONE';
CLOSE Cur1;
DEALLOCATE Cur1;
The code works, but my doubt is if it's correct the DECLARATIONS inside the first cursor.
DECLARE #UID int;
Shouldn't Declarations be placed at the beginning of code, as is normally done for other programming languages?
You can DECLARE a variable inside a WHILE, yes; the latter DECLAREs will simply be ignored. If you declared the variable and assigned it a value at the time (for example DECLARE #UID int = 1; it would be assigned 1 in each iteration:
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; --10
GO
DECLARE #I int = 1;
WHILE #i < 10 BEGIN
DECLARE #W int = 0;
SET #W = ISNULL(#W,1) + 1;
SET #I = #I + 1
END
SELECT #W; -- 1
DB<>fiddle
Of course, I personally prefer to DECLARE the variables outside of the WHILE as I feel the code is "cleaner", but that doesn't mean you have to.
I am running an SQL procedure that has to update a certain table. When I run the procedure it says successfully completed, yet the records are not updated when I try to debug it, it run only the line SET ANSI ON, then it gives the successful message. I am using SQL server 2012
Am I missing something, is there anything I need to add? See my code here:
USE [CADDe_ProdCopy]
GO
/****** Object: StoredProcedure [dbo].[sp_sms_X203] Script Date: 2015/09/03 08:28:15 AM ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [dbo].[sp_sms_X203]
as
declare #lCount int
set #lCount = (select count(*) from tbl_X203_SMS where SMSSent = 0 )
if #lCount > 0
begin
DECLARE #cSMSeMail varchar(100)
declare #cSMSType varchar(10)
declare #cSMSSent int
declare #cRA varchar(10)
declare #cWizard varchar(7)
declare #cCName varchar(26)
declare #cContact varchar(30)
declare #cUsed_KM int
declare #cAmount_Due decimal(18, 2)
declare #cSMSMessage varchar(160)
declare #cvblf varchar(1)
declare #cCheckInDt datetime
declare #cCheckOutDt datetime
declare #err int
set #cvblf = '|'
declare lcursor CURSOR FOR
Select SMSType, RA, CName, Contact, Used_KM, Amount_Due, eMail, [CheckInDateTime] ,[CheckOutDateTime]
From tbl_X203_SMS WHERE SMSSent = 0
open lcursor
fetch next from lcursor into #cSMSType, #cRA, #cCName, #cContact, #cUsed_KM, #cAmount_Due, #cSMSeMail, #cCheckInDt, #cCheckOutDt
while ##FETCH_STATUS = 0
begin
--SET #cContact = '+27834115771'
--SET #cSMSeMail = 'amangelsdorf#avis.co.za'
-- Check that the date of the checkin is within same day
if rtrim(ltrim(#cSMSType)) = 'CheckIn'
begin
if datediff(day,#cCheckInDt,getdate()) = 0
begin
SET #cSMSMessage = left('Thank you '+ #cCName +' for renting with AVIS.',160)
SET #cSMSMessage = left( #cSMSMessage + ' RA#' + #cRA + 'Retrieve your invoice at http://www.avis.co.za/inv' ,160)
--if #cAmount_Due > 0
-- SET #cSMSMessage = left( #cSMSMessage + #cvbLf + 'AMT:R ' + cast(#cAmount_Due as varchar),160)
exec sp_sms_xml_post #cContact, #cSMSMessage, #cSMSeMail
end
end
-- Check that the date of the checkout is within same day
if rtrim(ltrim(#cSMSType)) = 'CheckOut'
begin
if datediff(day,#cCheckOutDt,getdate()) = 0
begin
--SET #cSMSMessage = left( 'Thank you for choosing AVIS.' + #cvbLf + 'For any assistance contact the AVIS Careline on Tel: 0800001669' ,160)
SET #cSMSMessage = left( 'Thank you for choosing AVIS. ' + #cvbLf + 'Kindly contact 0800001669 for any roadside or emergency assistance.' ,160)
exec sp_sms_xml_post #cContact, #cSMSMessage, #cSMSeMail
end
end
set #err = ##error
if #err = 0
begin
--print 'no error'
update tbl_X203_SMS set SMSSent = 1 where SMSType = #cSMSType and RA = #cRA
end
fetch next from lcursor into #cSMSType, #cRA, #cCName, #cContact, #cUsed_KM, #cAmount_Due, #cSMSeMail, #cCheckInDt, #cCheckOutDt
end
close lcursor
deallocate lcursor
end
`
You check your count value in
set #lCount = (select count(*) from tbl_X203_SMS where SMSSent = 0 )
if #lCount > 0
because may be you are getting value as 0 so it is not going inside the if condition, you can use print(#lCount ) before if and execute stored procedure from sql server.
The code that you have shown is the code to create / alter a stored procedure and won't execute it, hence the Successfully Compiled response.
In order to execute this procedure you will need to use the exec statement:
exec [dbo].[sp_sms_X203]
I have a query which processes XML data and I use a while(##FETCH_STATUS = 0) loop for data returned from the cursor.
When I run the query using Management Studio, ##FETCH_STATUS equals -1 and the code inside my loop is omitted. If I run the query using the debugger and press continue, it runs just fine and the ##FETCH_STATUS equals 0. When I run the query again, after running it in debug ##FETCH_STATUS equals 0 and changes to -1.
To sum up:
I run with SSMS - ##FETCH_STATUS = -1
I run with debugger - ##FETCH_STATUS = 0 (I want this value)
I run with SSMS once after running with debugger ##FETCH_STATUS still equals 0 but then changes to -1.
I use OPEN cursor, CLOSE cursor and DEALLOCATE cursor. Why does it work this way?
EDIT: Code you asked for:
IF (OBJECT_ID('dbo.XmlOrderResponses') IS NOT NULL)
DROP TABLE XmlOrderResponses;
CREATE TABLE XmlOrderResponses (
OrderResponseType INT
,OrderResponseNumber NVARCHAR(40)
,OrderResponseDate DATETIME
,DocumentFunctionCode NVARCHAR(40)
,Remarks INT
);
DECLARE CUR CURSOR
FOR
SELECT Subdirectory
FROM XMLFiles;
OPEN CUR
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #DocHandle AS INT;
DECLARE #TMP AS NVARCHAR(512);
FETCH NEXT
FROM Cur
INTO #TMP
DECLARE #XmlDocument AS NVARCHAR(MAX);
SET #XmlDocument = (
SELECT CAST(XMLSource AS NVARCHAR(max))
FROM XMLFiles
WHERE subdirectory = #TMP
);
EXEC sys.sp_xml_preparedocument #DocHandle OUTPUT
,#XmlDocument;
INSERT INTO XmlOrderResponses (
OrderResponseType
,OrderResponseNumber
,OrderResponseDate
,DocumentFunctionCode
,Remarks
)
SELECT *
FROM OPENXML(#DocHandle, '/Document-OrderResponse/*', 11) WITH (
OrderResponseType INT
,OrderResponseNumber NVARCHAR(40)
,OrderResponseDate DATETIME
,DocumentFunctionCode NVARCHAR(40)
,Remarks INT
);
EXEC sys.sp_xml_removedocument #DocHandle;
END
CLOSE CUR;
DEALLOCATE CUR;
--I know I shouldn't be doing that but I can't get rid of NULL records the other way.
DELETE
FROM XmlOrderResponses
WHERE OrderResponseType IS NULL
AND OrderResponseNumber IS NULL
AND OrderResponseDate IS NULL
AND DocumentFunctionCode IS NULL
AND Remarks IS NULL;
SELECT *
FROM XmlOrderResponses
SELECT ##FETCH_STATUS
The problem is that the first time you refer to ##FETCH_STATUS, you have not done a fetch with your cursor, so it is referring to the last cursor used. Imagine this simple example:
DECLARE C1 CURSOR
FOR
SELECT TOP 3 ID
FROM (VALUES ('1'), ('2'), ('3')) t (ID);
OPEN C1;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #c1 CHAR(1);
FETCH NEXT FROM C1 INTO #c1;
PRINT #c1;
END
CLOSE C1;
DEALLOCATE C1;
DECLARE C2 CURSOR
FOR
SELECT TOP 3 ID
FROM (VALUES ('1'), ('2'), ('3')) t (ID);
OPEN C2;
-- HERE ##FETCH_STATUS REFERS TO THE LAST FETCH FOR CURSOR `C1` NOT `C2`
SELECT ##FETCH_STATUS;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #c2 CHAR(1);
FETCH NEXT FROM C2 INTO #c2;
PRINT #c2;
END;
CLOSE C2;
DEALLOCATE C2;
At the commented line, even though you have closed, and deallocated C1, ##FETCH_STATUS is still referring to this cursor (since no other FETCH has been performed since), so you never enter your loop for C2
You should perform the Fetch before the loop, then at the end of each loop, rather than at the beginning.
DECLARE #TMP AS NVARCHAR(512);
OPEN CUR
-- DO FETCH FIRST
FETCH NEXT FROM Cur INTO #TMP
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #DocHandle AS INT;
-- DO ALL YOUR WORK WITH #TMP
--PERFORM THE FETCH AGAIN AT THE END OF THE LOOP
FETCH NEXT FROM Cur INTO #TMP
END
The other problem you have with doing FETCH at the start of each loop, is that the last item will be processed twice. Again a simple example (and assuming you enter the loop with ##FETCH_STATUS = 0)
DECLARE C1 CURSOR
FOR
SELECT ID = '1';
OPEN C1;
DECLARE #c CHAR(1);
WHILE (##FETCH_STATUS = 0)
BEGIN
DECLARE #c1 CHAR(1);
FETCH NEXT FROM C1 INTO #c1;
PRINT #c1;
END
This will print
1
1
Because, when ##FETCH_STATUS is -1, FETCH will just return the item at the current position.
Is it possible to execute a stored procedure for each row in a SELECT? This only executes the first row, looking for something to execute for all rows:
Declare
#Loop bit = 1, #ID int, #Exists bit, #ReturnValue bit = 0
WHILE (#Loop) = 1
BEGIN
SELECT #ID = ID FROM Table --Multiple Rows Returned
EXEC [dbo].[StoredProc1] --Exec SP for Each Row
#InputID = #ID
,#Exists = #Exists OUTPUT
IF #Exists = 1
BEGIN
SET #Loop = 0
SET #ReturnValue = 1
END
END
SELECT #ReturnValue [ReturnValue]
Use a cursor:
DECLARE #exists bit
DECLARE db_cursor CURSOR FOR
SELECT ID
FROM Table
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #ID
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC [dbo].[StoredProc1] --Exec SP for Each Row
#ID
, #exists OUTPUT
FETCH NEXT FROM db_cursor INTO #id
END
CLOSE db_cursor
DEALLOCATE db_cursor
I have an select statement that returns a single row.
After that I have written a cursor as for me ##fetch_status is -1 it does not go inside the cursor only now
open cur_mkt
print #init
While (#init = 0)
Begin
fetch next from cur_mkt into
#Desc,
#Divisions
print ##fetch_status
if (##fetch_status =-1)
BREAK
Is there any way I can go inside the cursor,
please help me out.
It doesn't sound like you need a cursor (which you should try to avoid anyway). If you're determining the presence of a result you could do:
SELECT #Desc = Desc, #Divisions = Divisions
FROM YourTable
WHERE ID = 1
IF ( ##ROWCOUNT > 0 )
BEGIN
-- Row was found
END
So I would recommend not using cursors.
To directly answer the question, the way you use cursors/iterate round the results is as follows:
DECLARE #A INTEGER
DECLARE cur_mkt CURSOR FOR
SELECT 1 AS A
UNION ALL
SELECT 2 AS A
OPEN cur_mkt
FETCH NEXT FROM cur_mkt INTO #A
WHILE (##FETCH_STATUS = 0)
BEGIN
PRINT #A
FETCH NEXT FROM cur_mkt INTO #A
END
CLOSE cur_mkt
DEALLOCATE cur_mkt