How to check if cursor exists (open status) - sql

How do I check if a cursor is open or not? Because many times I am encountering the error 'Cursor already exists'. Please let me know how can I check whether a cursor is already in open status.
In fact I have closed as well as Deallocated it at the end (CLOSE ppm_cursor; DEALLOCATE ppm_cursor;) But Still i am getting the same error what could be the reason.

You can use the CURSOR_STATUS function to determine its state.
IF CURSOR_STATUS('global','myCursor')>=-1
BEGIN
DEALLOCATE myCursor
END

Close the cursor, if it is empty then deallocate it:
IF CURSOR_STATUS('global','myCursor') >= -1
BEGIN
IF CURSOR_STATUS('global','myCursor') > -1
BEGIN
CLOSE myCursor
END
DEALLOCATE myCursor
END

Just Small change to what Gary W mentioned, adding 'SELECT':
IF (SELECT CURSOR_STATUS('global','myCursor')) >= -1
BEGIN
DEALLOCATE myCursor
END
http://social.msdn.microsoft.com/Forums/en/sqlgetstarted/thread/eb268010-75fd-4c04-9fe8-0bc33ccf9357

I rarely employ cursors, but I just discovered one other item that can bite you here, the scope of the cursor name.
If the database CURSOR_DEFAULT is global, you will get the "cursor already exists" error if you declare a cursor in a stored procedure with a particular name (eg "cur"), and while that cursor is open you call another stored procedure which declares and opens a cursor with the same name (eg "cur"). The error will occur in the nested stored procedure when it attempts to open "cur".
Run this bit of sql to see your CURSOR_DEFAULT:
select is_local_cursor_default from sys.databases where name = '[your database name]'
If this value is "0" then how you name your nested cursor matters!

This happened to me when a stored procedure running in SSMS encountered an error during the loop, while the cursor was in use to iterate over records and before the it was closed. To fix it I added extra code in the CATCH block to close the cursor if it is still open (using CURSOR_STATUS as other answers here suggest).

Expanding on a previous answer, this proc is useful to call if you are worried that the cursor may have been left open or allocated
CREATE OR ALTER PROCEDURE dbo.CloseAndDeallocateCursor
#cursorName NVARCHAR(80)
AS
BEGIN
IF CURSOR_STATUS('global', #cursorName) >= -1
BEGIN
DECLARE #SQL NVARCHAR(91)
IF CURSOR_STATUS('global', #cursorName) > -1
BEGIN
SET #SQL = N'CLOSE ' + #cursorName
EXEC sp_executeSQL #SQL
END
SET #SQL = N'DEALLOCATE ' + #cursorName
EXEC sp_executeSQL #SQL
END
END
GO
... and then sample usage ...
EXEC dbo.CloseAndDeallocateCursor 'myCursor'
DECLARE myCursor STATIC
FOR SELECT * FROM blah

Related

Unable to Modify User-Defined Table Type

I have a SQL User-Defined Table Type. It used in many
stored procedures.Now i need to change a column in that table type.
I tried to drop and recreate the User-Defined Table Type.But SQL Server
doesn't Allow that. It shows up following error.
Msg 3732, Level 16, State 1, Line 3
Cannot drop type 'dbo.UserDefinedTableType' because it is being referenced by object 'SP_DoSomething'. There may be other objects that reference this type.
Msg 219, Level 16, State 1, Line 3
The type 'dbo.UserDefinedTableType' already exists, or you do not have permission to create it.
How to alter the User-Defined Table Type without modifying all the Stored procedure that uses User-Defined Table Type ?
You have binding in SP_DoSomething stored procedure. The type you want to change is used in that stored procedure.
You need to save script of that procedure. Drop it. Change dbo.UserDefinedTableType and create procedure again.
There is a similar post here. Check is some of the answers can help you. Answer of #norlando seems promising.
In total you should delete all Functions and Stored Procedures which use this User-Defined Table Type. Then you can drop User-Defined Table Type and recreate it. Then you should recreate all Stored Procedures and Functions which you deleted in previous step.
You can use this command for drop and recreate all SPs and Functions.
I suggest you to run this command with Print line to create Drop(s) and Create(s) command. Then you can put between Drop(s) command and Create(s) command your modification.
Declare #fullObjectName NVarChar(1000) = 'ref.Employee'
Declare #CreateCommand VarChar(Max), #DropCommand VarChar(Max)
Declare #ProcList Table
(
RowId Int,
CreateCommand NVarChar(Max),
DropCommand NVarChar(Max)
)
Insert Into #ProcList
SELECT ROW_NUMBER() OVER (ORDER BY OBJECT_NAME(m.object_id)) RowId,
definition As CreateCommand,
'DROP ' +
CASE OBJECTPROPERTY(referencing_id, 'IsProcedure')
WHEN 1 THEN 'PROC '
ELSE
CASE
WHEN OBJECTPROPERTY(referencing_id, 'IsScalarFunction') = 1 OR OBJECTPROPERTY(referencing_id, 'IsTableFunction') = 1 OR OBJECTPROPERTY(referencing_id, 'IsInlineFunction') = 1 THEN 'FUNCTION '
ELSE ''
END
END
+ SCHEMA_NAME(o.schema_id) + '.' +
+ OBJECT_NAME(m.object_id) As DropCommand
FROM sys.sql_expression_dependencies d
JOIN sys.sql_modules m
ON m.object_id = d.referencing_id
JOIN sys.objects o
ON o.object_id = m.object_id
WHERE referenced_id = TYPE_ID(#fullObjectName)
-----
Declare cur_drop SCROLL Cursor For Select CreateCommand, DropCommand From #ProcList
OPEN cur_drop
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
While ##FETCH_STATUS = 0
Begin
--Exec sp_executesql #DropCommand
PRINT #DropCommand
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
End
/*
Drop And ReCreate User Defined Table Type
*/
Fetch First From cur_drop Into #CreateCommand, #DropCommand
While ##FETCH_STATUS = 0
Begin
--Exec sp_executesql #CreateCommand
PRINT #CreateCommand
Fetch Next From cur_drop Into #CreateCommand, #DropCommand
End
Close cur_drop
Deallocate cur_drop
The code below while incomplete should be a good start. Please note that among many other things:
you must adapt it (I am using user type, not table type) and test it.
It only handles procs.
If your procs definition start with alter, you need to add code and logic to control this and deal with it in the cursor (create empty proc first then alter).
Using it will also remove all granted rights on the procs.
...
Begin Try
Begin Tran
Declare #procs Table(code nvarchar(max), pname sysname, pschema sysname)
Declare #sql nvarchar(max), #code nvarchar(max), #pname sysname, #pschema sysname
Declare cur_drop Cursor For
Select sp.definition, obj.name, schema_name(obj.schema_id) From sys.sql_modules as sp
Inner Join sys.objects as obj on obj.object_id = sp.object_id
Inner Join sys.dm_sql_referencing_entities ('dbo.TestType', 'TYPE') as dep on dep.referencing_id = sp.object_id
Where obj.Type = 'P'
Open cur_drop
Fetch Next From cur_drop Into #code, #pname, #pschema
While ##FETCH_STATUS = 0
Begin
Print 'Drop '+#pname
Insert into #procs(code, pname, pschema) Select #code, #pname, #pschema
Set #sql = 'Drop proc ['+#pschema+'].['+#pname+']'
Exec sp_executesql #sql
Fetch Next From cur_drop Into #code, #pname, #pschema
End
Close cur_drop
Deallocate cur_drop
-- Drop Type
-- Create Type
Declare cur_create Cursor For
Select code, pname, pschema From #procs
Open cur_create
Fetch Next From cur_create Into #code, #pname, #pschema
While ##FETCH_STATUS = 0
Begin
Print 'Create '+#pname
Exec sp_executesql #code
Fetch Next From cur_create Into #code, #pname, #pschema
End
Close cur_create
Deallocate cur_create
Commit
End Try
Begin Catch
rollback;
throw;
End Catch
You could automate the process of temporary deleting the dependencies and then re-creating them, so you shouldn't bother if you have many dependencies. For insrutctions on how to automate this process, see my answer here.

using Sql Server OPENROWSET with Stored Procedure [duplicate]

I want pass dynamic URL of excel to "OPENROWSET".
NOTE - I am passing returned result of excel file to cursor.
I want to pass file path to "#excelpath",
I have tried many ways but its giving syntax error.
ALTER procedure [dbo].[import_excel]
(
#excelpath as nvarchar(max)
)
as
begin
set nocount on
DECLARE insert_cursor CURSOR FOR
select * FROM OPENROWSET('Microsoft.ACE.OLEDB.12.0',
'Excel 12.0;Database=C:\memberdata.xlsx', [Sheet1$])
OPEN insert_cursor;
FETCH NEXT FROM insert_cursor
INTO #id_number, #memberName
WHILE ##FETCH_STATUS = 0
BEGIN
-- body of cursor
FETCH NEXT FROM insert_cursor
INTO #id_number, #memberName
END
CLOSE insert_cursor;
DEALLOCATE insert_cursor;
END
You have to build your query using dynamic SQL, as shown in this question. It would probably be simplest for you to insert the data from your query into a permanent table, then run the cursor over the permanent table. In that way you minimize the amount of SQL you need to work with dynamically.

sql server cursor stuck in loop

Ok, I'm totally at a loss - this bit of code used to work, and now suddenly doesn't....
declare GetAllCodes cursor local for
select ac.activationCodePKID from ActivationCodes ac
left join OrdersLineItems od on ac.activationCodePKID = od.activationCodePKID
where od.activationCodePKID is null and ac.internalorderPKID is not null
and ac.campaignID is not null
and ac.enteredBy like 'ProcessRequest|Entitlement%'
RAISERROR ('step completed', 0, 1) WITH NOWAIT
open GetAllCodes
while (1=1)
begin
RAISERROR ('entering cursor', 0, 1) WITH NOWAIT
fetch next from GetAllCodes into #activationCodePKID
if (##fetch_status <> 0)
begin
DEALLOCATE GetAllCodes
break
end
exec fixOrder #activationCodePKID, 6, 7
end
It seems to just get stuck in a loop...I've commented out the exec statement, and just put in a print statment, but it just keeps going. The 'step completed' print statement gets printed, and so does the 'entering cursor' statement. And then nothing.... just hangs. The query itself returns 192 rows, so to just loop over, it should loop over 192 times and then break out and end. Ideas?
EDIT:
I added this:
declare #var varchar(10)
set #var = 'here: ' + cast(##fetch_status as varchar(10))
RAISERROR (#var, 0, 1) WITH NOWAIT
...right after the fetch next from GetAllCodes into #activationCodePKID statement - still nothing. The 'entering cursor' still gets printed, but then it just hangs...
EDIT 2:
I stripped out a lot of stuff, and added this right after the 'declare cursor' statement, to see if I can output ANYTHING...
RAISERROR ('query done', 0, 1) WITH NOWAIT
open GetAllCodes
fetch next from GetAllCodes into #activationCodePKID
close GetAllCodes
deallocate GetAllCodes
Still hangs... so then I took out the 'fetch statement', and it seemed to not hang anymore. It didn't do anything, obviously, because I don't have it do anything, but it completed execution.
Which makes me think as to why the fetch statement is hanging. Is there some server setting that could impact this? Some memory issues? Hardware issues?
Is there a reason the cursor is structured like that?
Usually cursors are built like so:
declare #dbName varchar(max);
declare myCursor cursor for select [name] from sys.databases;
open myCursor;
fetch next from myCursor into #dbName;
while (##fetch_status = 0)
begin
print #dbName;
fetch next from myCursor into #dbName;
end;
close myCursor;
deallocate myCursor;
Whenever they get stuck in a look will be because the fetch status isn't been set to zero (usual cause for me, is missing the "fetch next").
--
EDIT:
Can you check in the Activity monitor and see if that piece of SQL is running? Is there a deadlock?
My guess is, ##fetch_status is returning something other than 0, e.g. -1 or -2
MSDN Link
Maybe check to see what exactly is in there; that should give you a clue about what is wrong.

How do I go through a cursor to perform logic on multiple tables? (The table names are in the cursor)

I get the feeling this is pretty basic database work, but it isn't for me. I'm trying to get a list of all of my tombstone tables from system tables and store the results in a cursor. I'm then trying to perform some logic on each of those tables I'm having trouble doing so.
Any help would be greatly appreciated.
Here is the error I get:
Must declare the table variable "#tablename"
Here is the code:
declare tombstonetables cursor for
(select name from sys.objects
where
name like'%tombstone%'
and type = 'U'--for user_table
)
Print 'Begin purging tombstone tables'
declare #tablename varchar(250)
open tombstonetables
fetch next from tombstonetables into #tablename
WHILE ##FETCH_STATUS = 0
begin
select * from #tablename--real logic goes here later
fetch next from tombstonetables into #tablename
end
close tombstonetables
deallocate tombstonetables
Looks like you need to use Dynamic SQL
Here is a reference to a simple walk through http://www.mssqltips.com/tip.asp?tip=1160
You will probably need to make use of sp_executesql
Here is a simple example of using Dynamic SQL with your example
DECLARE #DynamicSQL nvarchar(100)
WHILE ##FETCH_STATUS = 0
begin
SET #DynamicSQL = 'select * from ' + #tablename --real logic goes here later
EXEC #DynamicSQL
fetch next from tombstonetables into #tablename
end

MSSQL: How do you script Stored Procedure creation with code?

I am trying to query for a list of stored procedure definitions using information_schema.routines that exist in one database but not in another.
SELECT
t1.Routine_Definition
FROM
[server1].MyDatabase.INFORMATION_SCHEMA.Routines t1
LEFT JOIN
[server2].MyDatabase.INFORMATION_SCHEMA.Routines t2 ON t1.Routine_Name = t2.Routine_Name
WHERE
t2.Routine_Name is null
This gives me the query definitions in a single line so when I have a comment like this
--Some comment
SELECT Column
FROM Somewhere
The SQL gets commented out and I cannot use the definition to create the SP.
How to I parse this back with the proper line breaks?
or
Is there a better way to get these scripts (using code)?
The stored procedure is only displayed on one line in Management Studio. If you run the query with results to text, or use the following, you will get the correct line breaks:
declare #sql varchar(8000) -- varchar(max) in SQL 2005+
SELECT
#sql = t1.Routine_Definition
FROM
INFORMATION_SCHEMA.Routines t1
print #sql
DECLARE MY_CURSOR Cursor
FOR
SELECT
t1.Routine_Definition
FROM
[server1].MyDatabase.INFORMATION_SCHEMA.Routines t1
LEFT JOIN
[server2].MyDatabase.INFORMATION_SCHEMA.Routines t2 ON t1.Routine_Name = t2.Routine_Name
WHERE
t2.Routine_Name is null AND
LEN(t1.Routine_Definition) < 4000
Open My_Cursor
DECLARE #sql VARCHAR(MAX)
FETCH NEXT FROM MY_Cursor INTO #sql
While (##FETCH_STATUS <> -1)
BEGIN
IF (##FETCH_STATUS <> -2)
Print #sql
FETCH NEXT FROM MY_CURSOR INTO #sql
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR
GO
Here is how I implemented ck's solution...
the INFORMATION_SCHEMA view only returns the first 4000 characters in the definition. (Ideally you wont have SP that are that long)You will want to script those manually or some other way.
I think the easiest way of getting your stored procedures is to use the Import/Export utility that is built into SQL Server Management Studio. From there you can export your Stored Procedure Objects into the code window or to a file that you can immediately run.