Create cursor inside cursor - sql-server

I am pretty new to the cursor. I am trying to run cursor inside a cursor. Help is appreciated. Context: I am trying to run index_optimize stored procedure through all databases when SQL Server starts.
USE [master]
GO
CREATE PROCEDURE [dbo].[Run_through_database]
AS
BEGIN
DECLARE #DatabaseName AS varchar(500)
-- Provide the name of SP that you want to run
DECLARE #SPName AS varchar(128) = 'dbo.index_optimize'
DECLARE DBCursor CURSOR FOR
--Filter the list of the database in which stored procedure exists
SELECT NAME
FROM sys.databases
WHERE database_id > 4
OPEN DBCursor
FETCH NEXT FROM DBCursor INTO #DatabaseName
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #DBName AS nvarchar(500);
SET #DBName = QUOTENAME(N'' + #DatabaseName + '');
-- Use Dynamic SQL to change the database name and
-- execute stored procedure from that database
EXEC (N'USE ' + #DBName + N'; EXEC(''' + #SPName + ' '');'
);
FETCH NEXT FROM DBCursor INTO #DatabaseName
END
CLOSE DBCursor
DEALLOCATE DBCursor
END
USE [master]
GO
ALTER PROCEDURE [dbo].[Index_optimize]
AS
BEGIN
DECLARE #Objectid INT, #Indexid INT,#schemaname VARCHAR(100),#tablename VARCHAR(300),#ixname VARCHAR(500),#avg_fragment float,#command VARCHAR(4000)
DECLARE AWS_Cusrsor CURSOR FOR
SELECT A.object_id,A.index_id,QUOTENAME(SS.NAME) AS schemaname,QUOTENAME(OBJECT_NAME(B.object_id,B.database_id))as tablename ,QUOTENAME(A.name) AS ixname,B.avg_fragmentation_in_percent AS avg_fragment FROM sys.indexes A inner join sys.dm_db_index_physical_stats(DB_ID(),NULL,NULL,NULL,'LIMITED') AS B
ON A.object_id=B.object_id and A.index_id=B.index_id
INNER JOIN SYS.OBJECTS OS ON A.object_id=OS.object_id
INNER JOIN sys.schemas SS ON OS.schema_id=SS.schema_id
WHERE B.avg_fragmentation_in_percent>30 AND A.index_id>0 AND A.IS_DISABLED<>1
ORDER BY tablename,ixname
OPEN AWS_Cusrsor
FETCH NEXT FROM AWS_Cusrsor INTO #Objectid,#Indexid,#schemaname,#tablename,#ixname,#avg_fragment
WHILE ##FETCH_STATUS=0
BEGIN
IF #avg_fragment>=30.0
BEGIN
SET #command=N'ALTER INDEX '+#ixname+N' ON '+#schemaname+N'.'+ #tablename+N' REBUILD '+N' WITH (ONLINE = ON)';
--Can add following line for index reorganization. Else remove following line.
SET #command=N'ALTER INDEX '+#ixname+N' ON '+#schemaname+N'.'+ #tablename+N' REORGANIZE';
EXEC(#command)
PRINT #command
END
FETCH NEXT FROM AWS_Cusrsor INTO #Objectid,#Indexid,#schemaname,#tablename,#ixname,#avg_fragment
END
CLOSE AWS_Cusrsor
DEALLOCATE AWS_Cusrsor
End
GO

Related

Looping through each table in a database dropping and creating a trigger

I have following two statements executing triggers:
IF EXISTS (SELECT * FROM sys.triggers
WHERE Object_ID = Object_ID("Insert_Serverdate_Into_CreatedAt"))
DROP TRIGGER Insert_Serverdate_Into_CreatedAt
GO
CREATE TRIGGER Insert_Serverdate_Into_CreatedAt ON *myTable*
AFTER INSERT
AS
BEGIN
INSERT INTO *myTable* (CreatedAt) VALUES(GETDATE())
END
GO
IF EXISTS (SELECT * FROM sys.triggers
WHERE Object_ID = Object_ID("Insert_Serverdate_Into_UpdatedAt"))
DROP TRIGGER Insert_Serverdate_Into_UpdatedAt
GO
CREATE TRIGGER Insert_Serverdate_Into_UpdatedAt ON *myTable*
AFTER UPDATE
AS
BEGIN
INSERT INTO *myTable* (UpdatedAt) VALUES(GETDATE())
END
GO
Now I have found two possibilities to loop through each table in my database
The first one is:
EXECUTE sp_MSforeachtable #command1 = myCommand
The second one is:
DECLARE #NAME VARCHAR(100)
DECLARE #SQL NVARCHAR(300)
DECLARE CUR CURSOR FOR
SELECT NAME
FROM SYS.TABLES
WHERE TYPE = 'U'
AND SCHEMA_ID = 1
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = myCommand
PRINT #SQL
EXEC Sp_executesql
#SQL
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR
The problem here is that these possibilities do not know the keyword GO which I need for executing my commands.
Is there a possibility to loop through all tables and execute my statement?
Thank you!
Give each of the four parts its own query like this:
DECLARE #NAME VARCHAR(100)
DECLARE #SQL1 NVARCHAR(300)
DECLARE #SQL2 NVARCHAR(300)
DECLARE #SQL3 NVARCHAR(300)
DECLARE #SQL4 NVARCHAR(300)
DECLARE CUR CURSOR FOR
SELECT NAME
FROM SYS.TABLES
WHERE TYPE = 'U'
AND SCHEMA_ID = 1
OPEN CUR
FETCH NEXT FROM CUR INTO #NAME
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL1 = 'IF EXISTS (SELECT *
FROM sys.triggers
WHERE Object_ID = Object_ID(''['+#Name+'_Insert_Serverdate_Into_CreatedAt]''))
DROP TRIGGER ['+#name+'_Insert_Serverdate_Into_CreatedAt]'
SET #SQL2 = 'CREATE TRIGGER ['+ #Name + '_Insert_Serverdate_Into_CreatedAt] ON ['+#Name+']
AFTER INSERT
AS
BEGIN
INSERT INTO ['+#Name+'] (CreatedAt) VALUES(GETDATE())
END'
SET #SQL3 = 'IF EXISTS (SELECT *
FROM sys.triggers
WHERE Object_ID = Object_ID(''['+#Name+'_Insert_Serverdate_Into_UpdatedAt]''))
DROP TRIGGER ['+#name+'_Insert_Serverdate_Into_UpdatedAt]'
SET #SQL4 = 'CREATE TRIGGER ['+#Name+'_Insert_Serverdate_Into_UpdatedAt] ON ['+#Name+']
AFTER UPDATE
AS
BEGIN
INSERT INTO ['+ #Name+'] (UpdatedAt) VALUES(GETDATE())
END'
PRINT #SQL1
EXEC Sp_executesql #SQL1
PRINT #SQL2
EXEC Sp_executesql #SQL2
PRINT #SQL3
EXEC Sp_executesql #SQL3
PRINT #SQL4
EXEC Sp_executesql #SQL4
FETCH NEXT FROM CUR INTO #NAME
END
CLOSE CUR
DEALLOCATE CUR

sql cursor insert result into a table

I have created a cursor which iterates through all the databases and displays the 1 record per database.
I would like the records to be inserted into 1 table where I can view it. The query may change which is why I don't want to create the table structure for a specific query and insert it. I wanted to use the "select into" clause but that will fail on the second time the cursor runs
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR SELECT name FROM #DBNAME
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = 'use [' + #DB_Name + '] Select '''+ #DB_Name + ''' ,'+
--Enter query below
'* from authentication where username like ''%clair#indicater%'' and password = ''Rohan2410'''
-- print #Command
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
You should better use INSERT INTO ... instead of SELECT INTO, something like this:
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR SELECT name FROM #DBNAME
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = 'use [' + #DB_Name + ']
IF OBJECT_ID(''tempdb..##output'') IS NULL
BEGIN
SELECT NULL AS DB_Name,*
INTO ##output
FROM authentication WHERE 1=0
END
INSERT INTO ##output
Select '''+ #DB_Name + ''' ,'+
--Enter query below
'* from authentication where username like ''%clair#indicater%'' and password = ''Rohan2410'''
-- print #Command
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
SELECT * FROM ##output
DROP TABLE ##output
Basically, on the first cursor iteration we will create an empty temp table with the correct structure. Then we just insert into that temp table.

Drop and re-create procedure if it exists in T-SQL not working

IF EXISTS ( SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'LOCATION') AND type IN (N'P', N'PC'))
DROP PROCEDURE [dbo].[LOCATION]
GO
CREATE PROCEDURE [dbo].[LOCATION]
#IP NVARCHAR(100)
AS
BEGIN
DECLARE #IPNumber BIGINT
SELECT #IPNumber = dbo.ConvertIp2Num(#IP)
SELECT [country_code],[country_name]
FROM [myDatabase].[dbo].[CountryIP]
WHERE #IPNumber BETWEEN ip_from AND ip_to
END
I have the above code to check if stored procedure LOCATION exists in the current database. I expect it to drop and re-create the procedure if it exists.
However, if the procedure exists the code is still executing and as a result i get the following error 'There is already an object named 'LOCATION' in the database.'
Why is that code failing to drop the procedure if it exists?
The same code works properly for a another procedure in the same database.
Try this (preferred method using a view):
IF EXISTS(SELECT 1
FROM INFORMATION_SCHEMA.ROUTINES
WHERE ROUTINE_NAME = 'PRC_NAME'
AND SPECIFIC_SCHEMA = 'schema_name')
BEGIN
DROP PROCEDURE PRC_NAME
END
or this (not recommended using direct access to a system table):
IF EXISTS (SELECT 1
FROM SYS.PROCEDURES
WHERE NAME = 'PRC_NAME'
AND SCHEMA_NAME(SCHEMA_ID) = 'SCHEMA_NAME'
AND [TYPE] IN (N'P',N'PC'))
BEGIN
DROP PROCEDURE PRC_NAME
END
Why the first method is preferred you can find out for example in this question: SQL Server: should I use information_schema tables over sys tables?
This is kind of late, but others that end up here might want to check out the MSDN documentation that say you could use:
DROP PROCEDURE IF EXISTS dbo.uspMyProc;
GO
This is however available from SQL Server 2016 Community Technology Preview 3.3 (CTP 3.3).
You could use:
IF OBJECT_ID('MSL_GET_IP_LOCATION', 'P') IS NOT NULL
DROP PROCEDURE MSL_GET_IP_LOCATION
GO
Further thought on this is you will need to make sure you have unique names across all objects.
SQL Server -
Drop List of Stored Procedures if existed on Customer DB +
Copy List of Stored Procedures from master to another DB (recreate dynamically). P.S.: #TargetDBName=your DB.
I hope it will help someone
--Drop SPs from Customer DB if existed---
DECLARE #TargetDBName NVARCHAR(255)
SET #TargetDBName = DB_NAME()
DECLARE #SQL NVARCHAR(max)
SET #SQL = ''
DECLARE v CURSOR
FOR
SELECT [NAME]
FROM [Master].[sys].[procedures] p WITH(NOLOCK)
INNER JOIN [Master].sys.sql_modules m WITH(NOLOCK) ON p.object_id = m.object_id
WHERE p.[NAME] LIKE 'mySPs_list_%'
AND [type] = 'P'
OPEN v
FETCH NEXT
FROM v
INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = REPLACE(#sql, '''', '''''')
SET #sql = 'USE [' + #TargetDBName + ']; IF OBJECT_ID('''+#sql+''', ''P'') IS NOT NULL DROP PROCEDURE '+ #sql+';'
EXEC SP_EXECUTESQL #sql
FETCH NEXT
FROM v
INTO #sql
END
CLOSE v
DEALLOCATE v;
--COPY SPs from master to Another DB-------------
DECLARE c CURSOR
FOR
SELECT [Definition]
FROM [Master].[sys].[procedures] p
INNER JOIN [Master].sys.sql_modules m ON p.object_id = m.object_id
WHERE p.[NAME] LIKE 'mySPs_list_%'
AND [type] = 'P'
OPEN c
FETCH NEXT
FROM c
INTO #sql
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = REPLACE(#sql, '''', '''''')
SET #sql = 'USE [' + #TargetDBName + ']; EXEC(''' + #sql + ''')'
EXEC SP_EXECUTESQL #sql
FETCH NEXT
FROM c
INTO #sql
END
CLOSE c
DEALLOCATE c;

Execute procedure executing same statement twice

I have written this procedure,
DECLARE CURS_TABLE CURSOR FOR
SELECT NAME FROM SYS.TABLES WHERE NAME LIKE 'AK_LIB_ADDRESS'
DECLARE #TABLE_NAME VARCHAR(100);
DECLARE #SQL VARCHAR(300);
OPEN CURS_TABLE
FETCH NEXT FROM CURS_TABLE INTO #TABLE_NAME;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'ALTER TABLE '+#TABLE_NAME+' ADD PRIMARY KEY(ID);';
EXEC (#SQL)
FETCH NEXT FROM CURS_TABLE INTO #TABLE_NAME
END
CLOSE CURS_TABLE;
DEALLOCATE CURS_TABLE;
The problem is the exec(#sql) executing the same statement twice, i checked by placing print statement,it's working fine with print statement if i comment exec line...
So please can u give me any idea where i'm doing wrong..?
You should do like this, used a temporary table instead of selecting directly from the sys.tables
SELECT NAME into #temp FROM SYS.TABLES WHERE NAME LIKE 'AK_LIB_ADDRESS%'
DECLARE CURS_TABLE CURSOR FOR
SELECT NAME from #temp
DECLARE #TABLE_NAME VARCHAR(100);
DECLARE #SQL VARCHAR(300);
OPEN CURS_TABLE
FETCH NEXT FROM CURS_TABLE INTO #TABLE_NAME;
WHILE ##FETCH_STATUS = 0
BEGIN
select #TABLE_NAME
SET #SQL = 'ALTER TABLE '+#TABLE_NAME+' ADD PRIMARY KEY(ID);';
EXEC (#SQL)
FETCH NEXT FROM CURS_TABLE INTO #TABLE_NAME
END
CLOSE CURS_TABLE;
DEALLOCATE CURS_TABLE;
Or add a Insensitive keyword to the cursor. The reason is that you are changing a heap to the B-tree inside the trigger, that's the reason you are getting inconsistent behaviour in the cursor.
DECLARE CURS_TABLE CURSOR INSENSITIVE FOR
SELECT NAME FROM SYS.TABLES WHERE NAME LIKE 'AK_LIB_ADDRESS%'
DECLARE #TABLE_NAME VARCHAR(100);
DECLARE #SQL VARCHAR(300);

Run operations on all the tables in all the databases

I'm trying to create a SQL Server script that applies some operations to all the tables in all the databases. I need to rename some tables if some conditions are respected, truncate the tables otherwise.
This is my script
EXEC sp_MSforeachdb
#command1 = '
IF not exists(select 1 where ''?'' in (''master'',''model'',''msdb'',''tempdb''))
EXEC [?].dbo.sp_MSforeachtable
#command1 = ''
IF(substring(&, 1, 3)=pv_ and right(&, 5) != _data and right(&, 4) != _BCK)
exec sp_RENAME & , &_BCK''
ELSE IF (right(&, 4) != _BCK)
TRUNCATE TABLE &
#replacechar = ''&'''
I got some errors but I'm new to SQL Server and I have not idea how to fix this script.
Any suggestions?
Many thanks
Here is a solution for start. It won't be quick, but it loops all tables of all databases on the server. Inside in the second cursor you can deceide what to do with the table.
(The query is not optimalized, just a quick solution)
DECLARE #DBName NVARCHAR(50)
DECLARE #TableName NVARCHAR(100)
DECLARE #DynamicSQL NVARCHAR(300)
DECLARE #DBCursor CURSOR
SET #DBCursor = CURSOR FOR
SELECT NAME FROM SYS.DATABASES
WHERE NAME NOT IN ('master','tempdb','model','msdb')
OPEN #DBCursor
FETCH NEXT FROM #DBCursor INTO #DBName
WHILE ##FETCH_STATUS = 0
BEGIN
CREATE TABLE #TempTableDatas
(
name varchar(100),
objectID int
)
SET #DynamicSQL = 'INSERT INTO #TempTableDatas
SELECT name, object_id FROM [' + #DBName + ']' + '.sys.Tables '
EXEC SP_EXECUTESQL #DynamicSQL
DECLARE #TableCursor CURSOR
SET #TableCursor = CURSOR FOR
SELECT name FROM #TempTableDatas
OPEN #TableCursor
FETCH NEXT FROM #TableCursor INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #TableName, #DBName
FETCH NEXT FROM #TableCursor INTO #TableName
END
CLOSE #TableCursor
DEALLOCATE #TableCursor
DROP TABLE #TempTableDatas
FETCH NEXT FROM #DBCursor INTO #DBName
END
CLOSE #DBCursor
DEALLOCATE #DBCursor

Resources