Looping through each table in a database dropping and creating a trigger - sql-server

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

Related

Create cursor inside cursor

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

Replacing cursor with set based query

I want to find count of set of tables. The table names are values in another table.
--like
select count(*) from tablename
--tablename is obtained from
select tablename from table1
--table1 has around 171 tablenames
I was using cursor to get each table name and select count for each, but it is taking time. Can you please help how to replace cursor code with set based solution?
Below is my cursor code
SET NOCOUNT ON;
if( OBJECT_ID('tempdb..#temptablenew') IS NOT NULL )
BEGIN
DROP table #temptablenew
END
select * into #temptablenew from table1
declare #srccount int
declare #tablename nvarchar(max);
declare #q2 nvarchar(max);
declare #id int;
declare my_cursor cursor
local static read_only forward_only
for
select id,tablename from #temptablenew
open my_cursor
fetch next from my_cursor
into #id,#tablename
while ##fetch_status = 0
begin
set #q2 =N'select #srccount= count(*) from '+#tablename+' with (nolock)';
execute sp_executesql #q2,#PARAMS = N'#srccount INT OUTPUT',
#srccount = #srccount OUTPUT
select #srccount,#id,#tablename
fetch next from my_cursor
into #id,#tablename
end
close my_cursor;
deallocate my_cursor;
Thanks in advance
Try this,
SET NOCOUNT ON;
declare #q2 nvarchar(max) = '';
SELECT #q2 = #q2 + 'SELECT COUNT(*) AS cnt, ''' + tablename + ''' AS TableName FROM ' + tablename + ' (NOLOCK); '
FROM table1
--Print #q2
execute sp_executesql #q2

How to get return value from EXEC sp_executesql

How do I get the return value from EXEC sp_executesql #OpenQry so I can check if the value exists in IF EXISTS?
DECLARE #TableName VARCHAR(25)
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC sp_executesql #OpenQry
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName)
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
you just need put the variable that you want receive de return value before sp_executesql
try tis code and let me know if works
DECLARE #TableName VARCHAR(MAX)
DECLARE #TableNameToDrop VARCHAR(MAX) --NEW
DECLARE #TD_QUERY NVARCHAR(MAX)
DECLARE #OpenQry NVARCHAR(MAX) -- Dont forget to declare this variable
declare #sqldrop nvarchar(max)
DECLARE CUR_QRY CURSOR FOR
SELECT TABLENAME FROM dbo.tbl_table
OPEN CUR_QRY
FETCH NEXT FROM CUR_QRY INTO #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
SET #OpenQry = 'SELECT * FROM OPENQUERY(linkedserver,''SELECT TABLENAME FROM DBC.TABLES WHERE TABLEKIND=''''T'''' AND DATABASENAME=''''dbname'''' AND TABLENAME=''''' + #TableName + ''''''')'
EXEC TableNameToDrop = sp_executesql #OpenQry
/*
IF EXISTS (SELECT #OpenQry)
AND EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableName )
*/
--maybe you dont need check if the table name existis, jut try this
IF EXISTS (SELECT TableName FROM dbo.table WHERE TableName=#TableNameToDrop )
BEGIN
set #sqldrop = 'drop table '+ #TableNameToDrop
EXEC sp_executesql #sqldrop
END
FETCH NEXT FROM CUR_QRY INTO #TableName
END
CLOSE CUR_QRY
DEALLOCATE CUR_QRY
Regards

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);

Error in Declaration of cursor

set #SQL=N' select #minTableId = MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
Declare Cursor For
EXEC SP_EXECUTESQL #SQL
if i have declared all the variables in above query but Declaration of cursor in above
query shows ERROR.
What is Solution?
In order to execute a cursor over dynamic SQL you must put the output of your dynamic sql into a temporary table and then cursor over the temporary table like this:
DECLARE #TableName NVARCHAR(100)
DECLARE #SQL NVARCHAR(1000)
CREATE TABLE #TempTABLE(email NVARCHAR(200))
SET #TableName='Users'
SELECT #SQL='INSERT INTO #TempTable SELECT email FROM ' + #TableName
EXEC (#SQL)
DECLARE MyCursor CURSOR FOR SELECT * FROM #TempTable
OPEN MyCursor
DECLARE #Email NVARCHAR(200)
FETCH NEXT FROM MyCursor INTO #Email
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Email = ' + #Email
FETCH NEXT FROM MyCursor INTO #Email
END
CLOSE MyCursor
DEALLOCATE MyCursor
DROP TABLE #TempTABLE
I dont think you need a cursor for this
try
DECLARE #AcDB VARCHAR(10),
#Sql NVARCHAR(MAX)
set #SQL=N' select MIN(id) from ' + #AcDB + '.dbo.vTblOfRollNo '
DECLARE #Temp TABLE(
MinID INT
)
INSERT INTO #Temp EXEC SP_EXECUTESQL #SQL
DECLARE #minTableId INT
SELECT TOP 1 #minTableId = MinID FROM #Temp
SELECT #minTableId
EDIT: Also here is the actual CURSOR documentation
DECLARE CURSOR (Transact-SQL)

Resources