I have a statement but it queries a database that I insert in the variable "USE", I need it to read all the databases hosted in sys.databases:
USE [BD001]
SELECT DB_NAME() AS DatabaseName,
'guest' AS Database_User,
[permission_name],
[state_desc]
FROM sys.database_permissions
WHERE [grantee_principal_id] = DATABASE_PRINCIPAL_ID('guest')
AND [state_desc] LIKE 'GRANT%'
AND [permission_name] = 'CONNECT'
AND DB_NAME() NOT IN ('master', 'tempdb', 'msdb');
I thought I could create a variable but I can't...and I can't figure out how to do it.
Is there any way to make this query generic so that I can insert other test queries?
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(1000)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
WHERE name NOT IN ('master', 'tempdb', 'msdb' )
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = '
SELECT ''' + #DB_Name + ''' AS DatabaseName,
''guest'' AS Database_User,
[permission_name],
[state_desc]
FROM ' +#DB_Name+ '.sys.database_permissions
WHERE [grantee_principal_id] = DATABASE_PRINCIPAL_ID(''guest'')
AND [state_desc] LIKE ''GRANT%''
AND [permission_name] = ''CONNECT'''
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
I think the following cursor may help you;
DECLARE #DB_Name varchar(100)
DECLARE #Command nvarchar(1000)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
WHERE name NOT IN ('master', 'tempdb', 'msdb' )
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = '
SELECT ''' + #DB_Name + ''' AS DatabaseName,
''guest'' AS Database_User,
[permission_name],
[state_desc]
FROM ' +#DB_Name+ '.sys.database_permissions
WHERE [grantee_principal_id] = DATABASE_PRINCIPAL_ID(''guest'')
AND [state_desc] LIKE ''GRANT%''
AND [permission_name] = ''CONNECT'''
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
Related
I had a problem with a maintenance procedure and need to create a second one where I declare a cursor with a list of database ID and pass them into another cursor to get a list of tables for each database.
Current problem is that in the inner cursor even though it runs use [database_name], when i declare it and specify my query it selects the tables from the master database. it doesn't change the database context before going into the inner cursor.
DECLARE #db varchar(128)
declare #cmd varchar(1024)
declare #table varchar(255)
declare #cmd2 varchar(1024)
DECLARE crDB CURSOR global FORWARD_only FOR
SELECT [name] FROM sys.databases WHERE database_id > 4
and database_id in (33) ORDER BY [name]
OPEN crDB
FETCH crDB INTO #db
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'USE [' + #db +']'
EXEC (#cmd)
DECLARE crTB CURSOR LOCAL FAST_FORWARD FOR
select [name] from sys.objects where type = 'u' ORDER BY [name]
OPEN crTB
FETCH crTB INTO #table
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd2 = 'Update Statistics ' + #table + CHAR(13)
PRINT #cmd2
EXEC (#cmd2)
end
CLOSE crTB
DEALLOCATE crTB
FETCH crDB INTO #db
END
CLOSE crDB
DEALLOCATE crDB
GO
The issue with your inner cursor, is scope. You can to do 2 things here. You have to move your inner cursor to right after the USE [' + #db like:
DECLARE #db VARCHAR(128);
DECLARE #cmd VARCHAR(1024);
DECLARE #table VARCHAR(255);
DECLARE #cmd2 VARCHAR(1024);
DECLARE crDB CURSOR GLOBAL READ_ONLY FORWARD_ONLY FOR
SELECT name
FROM sys.databases
WHERE database_id > 4
AND database_id IN (33)
ORDER BY name;
OPEN crDB;
FETCH crDB
INTO #db;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'USE [' + #db + ']
GO;
DECLARE crTB CURSOR LOCAL FAST_FORWARD FOR
SELECT name
FROM sys.objects
WHERE type = ''u'';
ORDER BY name;
OPEN crTB;
FETCH NEXT FROM crTB
INTO #table;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd2 = ''Update Statistics '' + #table + CHAR(13);
PRINT #cmd2;
EXEC (#cmd2);
END;
CLOSE crTB;
DEALLOCATE crTB;
';
EXEC (#cmd);
FETCH NEXT FROM crDB
INTO #db;
END;
CLOSE crDB;
DEALLOCATE crDB;
Or you can get rid of the inner cursor altogether and use sys.sp_MSforeachtable:
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'USE [' + #db + ']
GO;
EXEC sys.sp_MSforeachtable #command1 = ''UPDATE STATISTICS ?;''';
EXEC (#cmd);
I understand that you can set a default fill factor but I want to change existing fill factors back to default across an entire server.
DECLARE #Database VARCHAR(255)
DECLARE #Table VARCHAR(255)
DECLARE #cmd NVARCHAR(500)
DECLARE #fillfactor INT
SET #fillfactor = 90
DECLARE DatabaseCursor CURSOR FOR
SELECT name FROM master.dbo.sysdatabases
WHERE name NOT IN ('master','msdb','tempdb','model','distribution')
ORDER BY 1
OPEN DatabaseCursor
FETCH NEXT FROM DatabaseCursor INTO #Database
WHILE ##FETCH_STATUS = 0
BEGIN
SET #cmd = 'DECLARE TableCursor CURSOR FOR SELECT ''['' + table_catalog + ''].['' + table_schema + ''].['' +
table_name + '']'' as tableName FROM [' + #Database + '].INFORMATION_SCHEMA.TABLES
WHERE table_type = ''BASE TABLE'''
-- create table cursor
EXEC (#cmd)
OPEN TableCursor
FETCH NEXT FROM TableCursor INTO #Table
WHILE ##FETCH_STATUS = 0
BEGIN
IF (##MICROSOFTVERSION / POWER(2, 24) >= 9)
BEGIN
-- SQL 2005 or higher command
SET #cmd = 'ALTER INDEX ALL ON ' + #Table + ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),#fillfactor) + ')'
EXEC (#cmd)
END
ELSE
BEGIN
-- SQL 2000 command
DBCC DBREINDEX(#Table,' ',#fillfactor)
END
FETCH NEXT FROM TableCursor INTO #Table
END
CLOSE TableCursor
DEALLOCATE TableCursor
FETCH NEXT FROM DatabaseCursor INTO #Database
END
CLOSE DatabaseCursor
DEALLOCATE DatabaseCursor
BEGIN TRAN
DECLARE #table_name VARCHAR(MAX)
DECLARE table_cursor CURSOR LOCAL
FOR
SELECT
TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
OPEN table_cursor
FETCH NEXT FROM table_cursor INTO
#table_name
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'ALTER INDEX ALL ON '+#table_name+'
REBUILD WITH (FILLFACTOR = 100)'
END
CLOSE table_cursor;
DEALLOCATE table_cursor;
ROLLBACK TRAN
I have the below script and I want to be able to run this against a dynamic list of databases except the system databases. That's straight forward enough. The tricky bit is each database could have a different list of users to run the fix command against. Would this be a 3rd cursor? My attempt below which is not properly populating the users for each database. Any help would be greatly appreciated.
SET nocount ON
GO
SET QUOTED_IDENTIFIER OFF
GO
--
-- Declare and define variables
--
DECLARE #databasename VARCHAR(50) -- database name
DECLARE #sqlcommand nvarchar(256) -- SQL Command generated
-- Include the in-scope database names into #name
DECLARE db_cursor CURSOR FOR
SELECT NAME
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' ) -- don't include the databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Fixing Logins for '
+ Cast(#databasename AS VARCHAR)
DECLARE curSQL CURSOR FOR
SELECT "USE " + ( #databasename ) + ";" + " exec sp_change_users_login 'AUTO_FIX','" + NAME + "'"
SELECT NAME
FROM sys.sysusers
WHERE issqluser = 1
AND NAME NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
OPEN curSQL
FETCH curSQL INTO #sqlcommand
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sqlcommand
EXEC (#sqlcommand)
FETCH curSQL INTO #sqlcommand
END
CLOSE curSQL
DEALLOCATE curSQL
FETCH NEXT FROM db_cursor INTO #databasename
END
CLOSE db_cursor
DEALLOCATE db_cursor
Or you can do it cursorless.
DECLARE #Output NVARCHAR(MAX)
SET #Output = ''
SELECT #Output = #Output +
'--Fixing Logins For ' + name + CHAR(10) +
'USE ' + name + CHAR(10) +
'EXEC sp_change_users_login ''AUTO_FIX'','''+
(
SELECT b.name
FROM sys.sysusers as b
WHERE issqluser = 1
AND b.name NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
)
+''''+CHAR(10)+CHAR(10)
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' )
PRINT #Output
The main thing with your procedure is that, unlike PHP, TSQL cannot use single and double quotes interchangeably. So I fixed up your dynamic SQL string.
The other minor thing appears to have been a disjointed query from the cursor declaration - in yours, you follow the second declare with a select clause, without an accompanying from clause, which would have thrown on "Name" not being able to be found for that cursor. So I fixed that up, too.
DECLARE #databasename SYSNAME, #sqlcommand nvarchar(max)
DECLARE db_cursor CURSOR FOR
SELECT NAME
FROM master.dbo.sysdatabases
WHERE NAME NOT IN ( 'master', 'model', 'msdb', 'tempdb', 'DBATools' ) -- don't include the databases
OPEN db_cursor
FETCH NEXT FROM db_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT 'Fixing Logins for '
+ Cast(#databasename AS VARCHAR)
DECLARE curSQL CURSOR FOR
SELECT 'USE ' + #databasename + ';' + ' exec sp_change_users_login ''AUTO_FIX'',''' + NAME + ''';'
FROM sys.database_principals
WHERE Type='S'
AND NAME NOT IN ( 'dbo', 'guest', 'INFORMATION_SCHEMA', 'sys' )
OPEN curSQL
FETCH curSQL INTO #sqlcommand
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #sqlcommand
--EXEC (#sqlcommand)
FETCH curSQL INTO #sqlcommand
END
CLOSE curSQL
DEALLOCATE curSQL
FETCH NEXT FROM db_cursor INTO #databasename
END
CLOSE db_cursor
DEALLOCATE db_cursor
EDIT: Switched table to database_principals rather than sys.sysusers per comment.
I'm trying to create function on every database while I'm on master,
I'm using cursors
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master','model','msdb','tempdb','ReportServer$SQLEXPRESS','ReportServer$SQLEXPRESSTempDB')
OPEN c_db_names
FETCH next from c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
set #sql = 'Create function...'
set #UseAndExecStatment = 'use ' + #db_name + ' exec sp_executesql '+#sql
exec Sp_executeSql #UseAndExecStatment
FETCH next from c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
the problem is that its not working, it says:
"CREATE FUNCTION' must be the first statement in a query batch"
Any ideas?
Thanks for your help.
You can use db..sp_executesql instead:
declare #sql nvarchar(max), #db_name nvarchar(max), #sql2 nvarchar(max)
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master','model','msdb','tempdb','ReportServer$SQLEXPRESS','ReportServer$SQLEXPRESSTempDB')
and name ='reports'
OPEN c_db_names
FETCH next from c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
set #sql2 = 'create procedure ...'
set #sql = 'exec '+#db_name+'..sp_executesql N''' + #sql2+''''
print #sql
--exec(#sql)
FETCH next from c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
define the database in "create" query
try
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master','model','msdb','tempdb','ReportServer$SQLEXPRESS','ReportServer$SQLEXPRESSTempDB')
OPEN c_db_names
FETCH next from c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
set #sql = 'Create function '+#db_name+'dbo.functionName ......'
exec Sp_executeSql #sql
FETCH next from c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
see:
set #sql = 'Create function '+#db_name+'dbo.functionName ......'
done :)
declare #sql nvarchar(max), #db_name nvarchar(max), #sql2 nvarchar(max)
declare #sp_executesql nvarchar (max)
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master','model','msdb','tempdb','ReportServer$SQLEXPRESS','ReportServer$SQLEXPRESSTempDB')
--and name ='reports'
OPEN c_db_names
FETCH next from c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
set #sql2 = N'
CREATE FUNCTION [String_Turn_Month] (#d char(6))
RETURNS char(6)
AS
BEGIN
DECLARE ##DSTR char(6)
IF #D=''''000000''''
set ##dstr=''''000000''''
IF substring(#d,3,4)<1800
RETURN(#D)
IF substring(#d,3,4)>1800
set ##dstr=cast(substring(#d,3,4)+substring(#d,1,2) as char(6))
RETURN(##DSTR)
END'
-- set #sql = 'exec '+#db_name+'..sp_executesql N''' + #sql2+''''
set #sp_executesql = 'exec ' + #db_name + '..sp_executeSQL N''' + #sql2 + ''''
PRINT #sp_executesql
EXEC sp_executeSQL #sp_executesql
print '------------'
print '------------'
print '------------'
-- exec(#sql)
FETCH next from c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
I have 100's of databases for which I need to do an Alter to a procedure, they all have the same procedure. How can I add a cursor that will allow me to do this alter?.
DECLARE #databasename varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = 'ALTER PROCEDURE ''' + #databasename + '''.[dbo].[DeleteAccountUpgrade]
#Id INT
AS
DELETE FROM AccountUpgrades WHERE Id = #Id'
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #databasename
END
CLOSE database_cursor
DEALLOCATE database_cursor
It says that i need to declare #Id but i dont understand why, I just want to run that alter on each database, i am not sending any data to execute the procedure, i just want to modify the existing procedure on all databases.
Use nest sp_executesql calls. This allow you to execute DDL against other databases.
DECLARE #databasename varchar(100)
DECLARE #Command nvarchar(200)
DECLARE database_cursor CURSOR FOR
SELECT name
FROM MASTER.sys.sysdatabases
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #databasename
WHILE ##FETCH_STATUS = 0
BEGIN
SELECT #Command = N'USE ' + QUOTENAME(#databasename) +
' EXEC sp_executesql N''ALTER PROCEDURE [dbo].[DeleteAccountUpgrade] ' +
'#Id INT ' +
'AS ' +
'DELETE FROM AccountUpgrades WHERE Id = #Id'''
--PRINT #Command
EXEC sp_executesql #Command
FETCH NEXT FROM database_cursor INTO #databasename
END
CLOSE database_cursor
DEALLOCATE database_cursor
You need to use EXEC by itself, and not with sp_executesql.
EXEC sp_executesql #Command
should be changed to:
EXEC(#Command)
The sp_executesql procedure does parameterization with variables. This is where the error is coming from. See also https://stackoverflow.com/a/16308447/1822514