I have created a SQL Server 2005 stored procedure which finds dependent objects on a particular table.
I want to run this stored procedure for different database and for different tables. I have created cursor for this.
When I write USE #dbname, it tries to find the stored procedure in a #dbname and not the current database.
Can anybody please help me with how do I write this command in a cursor?
DECLARE name_cur CURSOR FOR
SELECT db_name, obj_name from Stats_Usage
WHERE last_user_update > '2011-06-01' ORDER BY db_name
DECLARE #tableName NVARCHAR (800)
DECLARE #dbName NVARCHAR(800)
DECLARE #sql NVARCHAR(900)
OPEN name_cur
FETCH name_cur INTO #dbName, #tableName
WHILE ##Fetch_Status = 0
BEGIN
SET #sql = 'USE '+#dbName +' EXEC proc_depend ' + #tableName
EXEC (#sql)
FETCH name_cur INTO #dbName, #tableName
END
CLOSE name_cur
DEALLOCATE name_cur
GO
You can fully qualify your Stored Procedure name.
Assuming the database your SP resides in is called procs (for example), you could amend your query to use the following:
SET #sql = 'USE '+#dbName +' EXEC procs.dbo.proc_depend ' + #tableName
EXEC (#sql)
Refactor your stored proc to check for dependant objects cross database. You'll want to send it a command like this:
exec proc_depend 'MyDatabase.dbo.MyTable';
Try this instead:
SET #sql = ' EXEC proc_depend ''' #dbName + '.dbo.'+ #tableName + ''';
You'll need to dig into & modify proc_depend to ensure that it can take a fully qualified object name like database.schema.table
Related
I have a SQL set of instructions that I want to execute across multiple databases. I currently have the following SQL code:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
WHILE ##FETCH_STATUS = 0
BEGIN
EXECUTE('USE ' + #dbName + N';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM '+ #mySourceTable + N';
EXECUTE dbo.someProcedure #content;');
FETCH NEXT FROM dbCursor INTO #dbName;
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
Basically I want to do the following: I have several databases that all have the same [someUserDefinedType] table type (with the same structure) and a procedure named dbo.someProcedure that receives as a parameter a table of said type (the dbo.someProcedure is not the same across databases, it is specific to each). I want to go through the list of provided databases (#dbList) and execute each stored procedure with data from #mySourceTable. I am not sure if the code above is the best approach, it does not work and gives the error:
Must declare the scalar variable "#mySourceTable".
This variable is already declared at the beginning of the script. What am I doing wrong? Is it possible to pass the data from #mySourceTable using a variable and not create another table for it (I really want to avoid that)?
Try something like this:
USE Database1
DECLARE #mySourceTable AS [someUserDefinedType];
/*Execute set of operations on Database 1 and #mySourceTable*/
DECLARE #dbList TABLE (DBName nvarchar(50));
INSERT INTO #dbList (DBName)
VALUES('Database2'),('Database3');
DECLARE #dbName nvarchar(50);
DECLARE dbCursor CURSOR FOR SELECT DBName FROM #dbList;
OPEN dbCursor;
FETCH NEXT FROM dbCursor INTO #dbName;
DECLARE #DynamicTSQLStatement NVARCHAR(MAX);
WHILE ##FETCH_STATUS = 0
BEGIN
SET #DynamicTSQLStatement = N'
USE ' + #dbName + ';
DECLARE #content AS [someUserDefinedType];
INSERT INTO #content (ID)
SELECT ID FROM #mySourceTable;
EXECUTE dbo.someProcedure #content;';
FETCH NEXT FROM dbCursor INTO #dbName;
EXEC sp_executesql #DynamicTSQLStatement, N'#mySourceTable someUserDefinedType readonly', #mySourceTable = #mySourceTable
END;
CLOSE dbCursor;
DEALLOCATE dbCursor;
When you are executing T-SQL statement with sp_executesql you can pass parameters.
I'm testing a drop database query for all databases that begin with a specific prefix. However, because that can easily lead to horrific things I'm taking my drop database query
declare #dbname nvarchar (200);
declare #query nvarchar (max);
DECLARE db_cursor CURSOR FOR
select name from sys.databases
where name like 'PREFIX%'
Open db_cursor
fetch next from db_cursor into #dbname
while ##FETCH_STATUS = 0
BEGIN
set #query = 'Drop Database ['+ #dbname + ']'
Exec(#query)
FETCH NEXT FROM db_cursor INTO #dbname
END
Close db_cursor
deallocate db_cursor
and want to change the Drop Database part to something that is less scary.
So MAIN QUESTION is there a simple SQL query that I could put in there that would always apply to any SQL Server database? So I know that this query will only affect the databases I want it to before switching it back to Drop Database?
EDIT: Better yet, a query that will return the names of the databases.
Like select name from sys.databases but one that will work with ['+ #dbname + '] to return only the names of databases with that prefix to ensure that this query affects the appropriate databases.
Instead of Exec(#query), just call PRINT #query. That will show you the SQL that you intend to run.
PRINT Documentation
First of all just execute the following query and it will tell you what databases will it bring forward in the rest of the code
select name from sys.databases
where name like 'PREFIX%'
Finally add a PRINT statement to see the final DROP DATABASE statements dynamically build inside the cursor.
Some minor improvement in your code:
declare #dbname SYSNAME;
declare #query nvarchar (max);
DECLARE db_cursor CURSOR LOCAL FORWARD_ONLY FOR
select name from sys.databases
where name like 'PREFIX%'
Open db_cursor
fetch next from db_cursor into #dbname
while (##FETCH_STATUS = 0)
BEGIN
set #query = 'Drop Database '+ QUOTENAME(#dbname)
--Exec sp_executesql #query --<-- For execution
-- PRINT #query --<-- For debugging
FETCH NEXT FROM db_cursor INTO #dbname
END
Close db_cursor
deallocate db_cursor
Is there a query which drops / deletes databases with no tables in them (deletes empty databases)?
Server is Microsoft SQL Server 2005
This should do it.
Tested on a lab machine and it dropped all databases with 0 user tables.
Note, however, that tables aren't the only things in a database, necessarily. There could be stored procedures, functions, etc that someone might still need.
NOTE THAT THIS IS A VERY DANGEROUS OPERATION, AS IT DROPS DATABASES. USE AT YOUR OWN RISK. I AM NOT RESPONSIBLE FOR DAMAGE YOU CAUSE.
USE [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery = 'USE [' + #name + ']; IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
DROP DATABASE [' + #name + ']
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
Note also that, if a database is in use, SQL Server will refuse to drop it. So, if there are other connections to a particular database that this tries to drop, the command will abort.
To avoid that problem, you can set the database in question to single-user mode.
The following script is the same as the above, except it also sets the target databases to single-user mode to kill active connections.
BE EVEN MORE CAREFUL WITH THIS, AS IT'S ESSENTIALLY THE NUCLEAR OPTION:
use [master];
DECLARE #name varchar(50);
DECLARE #innerQuery varchar(max);
DECLARE tableCursor CURSOR FOR SELECT name FROM sys.databases where owner_sid != 0x01;
OPEN tableCursor;
FETCH NEXT FROM tableCursor
INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #innerQuery =
'USE [' + #name + '];
IF (SELECT COUNT(*) FROM sys.objects WHERE type = ''U'') = 0
BEGIN
USE [master];
ALTER DATABASE [' + #name + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
DROP DATABASE [' + #name + '];
END'
EXEC(#innerQuery)
FETCH NEXT FROM tableCursor INTO #name
END
CLOSE tableCursor;
DEALLOCATE tableCursor;
I think it is not possible (at least not with a TSQL command, might be a stored procedure somewhere). You would have to query sysobjects and abort if any found. (And you have to decide if you want to ignore some of the system objects like the design tables).
The script below will generate the needed DROP DATABASE script. You could tweak this to execute the statement too (in the master database context) but I suggest you review it first just in case.
EXEC sp_MSforeachdb N'
USE [?];
IF N''?'' NOT IN(N''master'', N''model'', N''msdb'', N''tempdb'')
BEGIN
IF NOT EXISTS(
SELECT *
FROM sys.tables
WHERE
OBJECTPROPERTYEX(object_id, ''IsMSShipped'') = 0
)
BEGIN
PRINT ''DROP DATABASE [?];'';
END;
END;';
Could someone please explain to me why I have error message show up when I exec the stored procedure. "A cursor with the name 'tName_cursor' already exists"
DECLARE #tName VARCHAR(100)
DECLARE #lsql VARCHAR(8000)
DECLARE tName_cursor CURSOR FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
OPEN tName_cursor;
FETCH NEXT FROM tName_cursor INTO #tName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lsql = N'
UPDATE [' +#tName+ ']
SET LATESTOFALL_FLG = ''N''
FROM [' +#tName+ '] T
JOIN D_CUSTOM_METRICS_RULE S
ON T.METRIC_ID = S.CUSTOM_METRIC_RULE_ID
AND T.LATESTOFALL_FLG = ''Y'''
EXECUTE sp_executesql #lsql
FETCH NEXT FROM tName_cursor INTO #tName;
END
CLOSE tName_cursor;
DEALLOCATE tName_cursor;
DECLARE tName_cursor_REDO CURSOR FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
OPEN tName_cursor_REDO;
FETCH NEXT FROM tName_cursor_REDO INTO #tName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #lsql = N'
UPDATE [' +#tName+ ']
SET LATESTOFDAY_FLG = ''N''
FROM [' +#tName+ '] T
JOIN D_CUSTOM_METRICS_RULE S
ON T.METRIC_ID = S.CUSTOM_METRIC_RULE_ID
AND T.CALC_METRIC_DATE_ID = CONVERT(INT,convert(VARCHAR, getdate(), 112))
AND T.LATESTOFDAY_FLG = ''Y'''
EXECUTE sp_executesql #lsql
FETCH NEXT FROM tName_cursor_REDO INTO #tName;
END
CLOSE tName_cursor_REDO;
DEALLOCATE tName_cursor_REDO;
Looks like some exception or 'return' breaks execution before DEALLOCATE command
"A cursor with the name 'tName_cursor' already exists"
message can happen when your 'declare' block is executed successfully, but after some code fails before 'DEALLOCATE tName_cursor' statement. Then, the second time you execute stored proc, it tries to declare a cursor again and the error message pops out. I recommend you to add 'begin try .. end try' block to your code and 'print ERROR_MESSAGE()' in exception block to see what comes out.
It sounds like you may be using GLOBAL cursors (?).
If you don't need them, I suggest using LOCAL cursors.
e.g.
DECLARE tName_cursor_REDO CURSOR LOCAL FOR
SELECT NAME FROM SYS.tables WHERE TYPE = 'U' AND NAME LIKE 'PPM_METRIC%'
That may help to make things more robust.
Let's say I have some update script:
update sometable set somecolumn = 'somevalue' where xyz = 0
Now let's say I have multiple databases, like DB1, DB2, DB3 and so on. How could I run this script on all of them without doing it manually?
Thanks :)
You can do this using cursor
get list of all server in your lan or in network
create cursor for that
Than make use of sp_executesql to run you update script with forpart query
set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [sp_cross_db_query]
#SQLQuery varchar(400)
AS
DECLARE #DB_Name varchar(100)
DECLARE database_cursor CURSOR FOR
SELECT DatabaseName
FROM Management.dbo.Customers
OPEN database_cursor
FETCH NEXT FROM database_cursor INTO #DB_Name
WHILE ##FETCH_STATUS = 0
BEGIN
exec(
'USE [' + #DB_Name + '];' +
#SQLQuery
)
FETCH NEXT FROM database_cursor INTO #DB_Name
END
CLOSE database_cursor
DEALLOCATE database_cursor
to run the query
exec sp_cross_db_query 'SELECT count(*) FROM Products'
If you wanted all databases, you can use sp_MSforeachdb:
http://www.databasejournal.com/features/mssql/article.php/3441031/SQL-Server-Undocumented-Stored-Procedures-spMSforeachtable-and-spMSforeachdb.htm
EXEC sp_MSforeachdb #command1="UPDATE ?..sometable SET somecolumn='somevalue' WHERE xyz=0"
Or for specific databases, you could try some of the logic as seen here:
http://www.sqlservercurry.com/2009/04/6-common-uses-of-undocumented-stored.html
Hope that helps.