create function in another database dynamically - sql

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

Related

Like all databases on the server for a query

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

Only fetching first value in column

CREATE PROC [dbo].[usp_InsertGenerator]
AS
BEGIN
DECLARE #tablename varchar(max)
DECLARE cursCol1 CURSOR FOR
SELECT name FROM sys.tables
OPEN cursCol1
FETCH NEXT FROM cursCol1 INTO #tablename
DECLARE cursCol CURSOR FOR
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = #tableName
OPEN cursCol
DECLARE #string nvarchar(3000)
DECLARE #stringData nvarchar(3000)
DECLARE #dataType nvarchar(1000)
SET #string='INSERT '+#tableName+'('
SET #stringData=''
DECLARE #colName nvarchar(50)
FETCH NEXT FROM cursCol INTO #colName,#dataType
IF ##fetch_status<>0
BEGIN
PRINT 'Table '+#tableName+' not found, processing skipped.'
--FETCH NEXT FROM cursCol1 INTO #tablename
CLOSE curscol
DEALLOCATE curscol
RETURN
END
WHILE ##FETCH_STATUS=0
--FETCH NEXT FROM cursCol1 INTO #tablename
BEGIN
IF #dataType in ('varchar','char','nchar','nvarchar')
BEGIN
SET #stringData=#stringData+'''''''''+
isnull('+#colName+','''')+'''''',''+'
END
ELSE
BEGIN
SET #stringData=#stringData+'''''''''+
isnull(cast('+#colName+' as varchar(200)),''0'')+'''''',''+'
END
SET #string=#string+#colName+','
FETCH NEXT FROM cursCol INTO #colName ,#dataType
END
BEGIN
DECLARE #Query nvarchar(4000)
SET #query ='SELECT '''+substring(#string,0,len(#string)) + ')
VALUES(''+ ' + substring(#stringData,0,len(#stringData)-2)+'''+'')''
FROM '+#tableName
PRINT (#query)
CLOSE cursCol1
DEALLOCATE cursCol1
CLOSE cursCol
DEALLOCATE cursCol
END
END
GO
When executing the procedure I am only getting first value(Row) from name column in sys.tables.
I want it for all the rows in the sys.tables.
Query should take each table name one by one then execute the query for every table so that I can get insert statement for all the tables.
Thanks in advance .
Please try using cursor to get name, cursCol1 Should be closed at last after cursCol. FETCH NEXT is also missing for cursCol1
CREATE PROC [dbo].[usp_InsertGenerator]
AS
BEGIN
DECLARE #tablename varchar(max)
DECLARE #tbl table (insertVal varchar(max))
DECLARE cursCol1 CURSOR FOR
SELECT name FROM sys.tables
OPEN cursCol1
FETCH NEXT FROM cursCol1 INTO #tablename
WHILE ##FETCH_STATUS=0
begin
DECLARE cursCol CURSOR FOR
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = #tableName
OPEN cursCol
DECLARE #string nvarchar(3000)
DECLARE #stringData nvarchar(3000)
DECLARE #dataType nvarchar(1000)
SET #string='INSERT '+#tableName+'('
SET #stringData=''
DECLARE #colName nvarchar(50)
FETCH NEXT FROM cursCol INTO #colName,#dataType
IF ##fetch_status<>0
BEGIN
PRINT 'Table '+#tableName+' not found, processing skipped.'
FETCH NEXT FROM cursCol1 INTO #tablename
CLOSE curscol
DEALLOCATE curscol
RETURN
END
WHILE ##FETCH_STATUS=0
--FETCH NEXT FROM cursCol1 INTO #tablename
BEGIN
IF #dataType in ('varchar','char','nchar','nvarchar')
BEGIN
SET #stringData=#stringData+'''''''''+
isnull('+#colName+','''')+'''''',''+'
END
ELSE
BEGIN
SET #stringData=#stringData+'''''''''+
isnull(cast('+#colName+' as varchar(200)),''0'')+'''''',''+'
END
SET #string=#string+#colName+','
FETCH NEXT FROM cursCol INTO #colName ,#dataType
END
BEGIN
DECLARE #Query nvarchar(4000)
SET #query ='SELECT '''+substring(#string,0,len(#string)) + ')
VALUES(''+ ' + substring(#stringData,0,len(#stringData)-2)+'''+'')''
FROM '+#tableName
INSERT INTO #tbl (insertVal) EXEC (#query)
-- PRINT (#query)
END
CLOSE cursCol
DEALLOCATE cursCol
FETCH NEXT FROM cursCol1 INTO #tablename
END
CLOSE cursCol1
DEALLOCATE cursCol1
SELECT * FROM #tbl
END

Need to declare a cursor with a variable in the declaration

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

Fill Factor--SQL Server, Is there a way to set Fill Factor across a database, server, or schema?

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

How to do an Alter in SQL Cursor

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