Drop databases with no tables - sql

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

Related

Can I use a drop statement to delete specific principals from database after completing a restore from Production in SQL Server?

I have task to automate the process of taking a copy of a specific production environment and using the .bak file to restore a different environment (TEST or DEV) so that they don't end up out of sync. I understand this isn't exactly best practice, but it's the best solution right now for the problem we're addressing.
I obviously don't want to keep the principals/roles/permissions that my Prod environment has in my Test environment, and so I am looking for a way to delete the principals and then put the original test ones back.
I found Kenneth Fisher's example here. However, I'm wondering if that solution is a little over engineered for my purposes.
Is there a reason I shouldn't just use a script like below to delete exactly which principals I need to from the restored test file and then use Mr. Fisher's script to put the old principals back?
DECLARE #sql AS NVARCHAR(500)
DECLARE #name sysname
DECLARE cursor_name CURSOR FAST_FORWARD READ_ONLY FOR
SELECT name
FROM sys.database_principals
WHERE [type] in (N'U',N'S',N'G')
AND name NOT IN ('dbo', 'guest', 'INFORMATION_SCHEMA', 'sys', 'public', 'codaprod_user', 'NT SERVICE\HealthService')
OPEN cursor_name
FETCH NEXT FROM cursor_name INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = N'IF EXISTS (SELECT TOP 1 1 FROM sys.schemas WHERE [name] = ''' + #name + ''')
BEGIN
DROP SCHEMA [' + #name + N']
END
BEGIN TRY
DROP USER [' + #name + N']
END TRY
BEGIN CATCH END CATCH;'
--PRINT 'USE ' + DB_NAME() + ';' + #sql
EXEC sys.sp_executesql #sql
FETCH NEXT FROM cursor_name INTO #name
END
CLOSE cursor_name
DEALLOCATE cursor_name;
GO

get sql log file name from within a script

We have a few test databases that we are throwing test indexes into and the log file gets bloated real quickly due to us dropping the contents of the table and repopulating it.
I have found, thanks to Stack Overflow, a few scripts and put them together to do what I need.
Here is the script:
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DBCC SHRINKFILE(N'SSSIndexes_Log', 1) <-- my issue is here
GO
The problem is the log file name. Is there a way to get the log file name without having to look it up manually and include it in the script to where this part is automated?
Btw, we never intend on restore this database. These are temporary indexes.
Thanks!
USE SSSIndexes
GO
ALTER DATABASE SSSIndexes SET RECOVERY SIMPLE WITH NO_WAIT
GO
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
You can use this to generate script for log file truncating of all databases on a specific server. To stick to specific databases use a filter.
SELECT
' USE [' + name + ']; GO
-- Truncate the log by changing the database recovery model to SIMPLE.
ALTER DATABASE [' + name + '] SET RECOVERY SIMPLE;
GO
-- Shrink the truncated log file to 1 MB.
DECLARE #logname varchar(128);
SELECT TOP 1 #logname=[name] FROM [' + name + '].[sys].[database_files] WHERE [type] = 1;
DBCC SHRINKFILE (#logname, 1);
GO
-- Reset the database recovery model.
ALTER DATABASE [' + name + '] SET RECOVERY FULL;
GO
' AS qry FROM master.dbo.sysdatabases
WHERE dbid > 6
WARNING!!!: YOU SHOULD ONLY DO THIS ON TEST DB SERVERS. Production DBs typically WANT to have FULL recovery mode. Test DBs you usually don't care.
Expanding on Abdul's answer (I had trouble getting it to show in new lines) and Dis' answers this finds the first db that has recovery FULL, sets it to simple and shrinks it... just keep clicking execute until it returns null
declare #dbname nvarchar(50)
declare #logfilename nvarchar(100)
select top(1) #dbname = name from sys.databases where recovery_model_desc <> 'SIMPLE' AND name <> 'tempdb'
declare #shrinkdb nvarchar(500)
set #shrinkdb = 'USE master
ALTER DATABASE [' + #dbname + '] SET RECOVERY SIMPLE
'
select #shrinkdb
execute sp_executesql #shrinkdb
set #shrinkdb = 'USE [' + #dbname + ']
DECLARE #Name NVARCHAR(50)
DECLARE cur CURSOR FOR
SELECT [name]
FROM [sys].[database_files]
where [type] = 1
OPEN cur
FETCH NEXT FROM cur INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
DBCC SHRINKFILE(#Name, 1)
FETCH NEXT FROM cur INTO #Name
END
CLOSE cur
DEALLOCATE cur
'
select #shrinkdb
execute sp_executesql #shrinkdb

calling stored procedure in a cursor

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

Delete all tables with a certain prefix name in a SQL 2008 database programmatically

I have 3000+ tables in my SQL 2008 database with names like listed below, that all starts with tempBinary_, that I need to delete programmatically, how do I do that?
I don't know if I prefer the solution in a SQL-script or with use of LINQtoSQL, i guess both are fine.
tempBinary_002c90322f4e492795a0b8a14e2f7c99
tempBinary_0039f7db05a9456f96eb3cd6a788225a
tempBinary_0057da9ef0d84017b3d0bbcbfb934fb2
I've used Like before on columns, but I don't know if it good for table names too.
Maybe something like this, where LIKE is used, can do it? I don't know.
Use [dbo].[database_name]
DROP TABLE table_name
WHERE table_name LIKE 'tempBinary_%'
Any ideas?
declare #stmt varchar(max) = ''
declare #tbl_name varchar(255)
DECLARE tbl_cursor CURSOR FORWARD_ONLY READ_ONLY
FOR select name
from sysobjects
where xtype='u' and name like 'tempBinary%'
OPEN tbl_cursor
FETCH NEXT FROM tbl_cursor
INTO #tbl_name;
WHILE ##FETCH_STATUS = 0
BEGIN
set #stmt = #stmt + 'drop table ' + #tbl_name + ';' + CHAR(13)
FETCH NEXT FROM tbl_cursor
INTO #tbl_name
end
CLOSE tbl_cursor;
DEALLOCATE tbl_cursor;
execute sp_sqlexec #stmt

Programmatically replacing linked server references with local database references in SQL Server stored procs?

As a part of a scripted procedure I'm trying to programmatically update references to linked servers in stored procs. We have several references like this:-
SELECT foo, bar
FROM [Server].[Database].dbo.[Table]
Which I would like to translate to:-
SELECT foo, bar
FROM [Database].dbo.[Table]
I would like to do this entirely programmatically in a 'fire and forget' script across several databases.
The idea I have right now is to use metadata to find references to linked tables, read the text of each sp from metadata again, adjust each sp's text, then shove each block of updated text into an exec statement to rebuild 'em one-by-one.
I do wonder whether this will be a humongous pain however, so does anybody have any better ideas? I am open to using powershell if that could provide a better solution.
Thanks in advance!
Hopefully I am understanding the questions, but rather than removing or replacing [Server], I suggest one of two approaches:
Option 1: Don't change any of the
SPs. Instead, update the linked
server configuration to point a
different database, even the local
box.
Option 2: Don't change any of the
SPs. Instead, start using SQL Server
Aliases. SQL Server Aliases are
managed via the CliConfig utility and
are ultimately stored in the
registry. Thus, they can be applied
manually or via .reg script.
Basically, the SQL Server Alias
deciphers the server (along with
port) which is being referenced. If
you update the link server
configuration to reference the SQL
Server Alias rather than a specific
server, you can point your procedures
to different server (even the local server) whenever you
would like.
I hope it helps.
Your approach is the easiest, frankly. I had a similar issue earlier this year
Read sys.sql_modules
REPLACE the linked server text and CREATE -> ALTER
EXEC (#Result)
Here's a script to find all procs/functions/views that reference linked servers on a SQL 2005 instance - might be useful too:
USE master
GO
SET NOCOUNT ON;
--------------------------------------------------------------------
-- Test linked server connections
--------------------------------------------------------------------
BEGIN TRY DROP TABLE #Svrs; END TRY BEGIN CATCH END CATCH;
CREATE TABLE #Svrs
(
[Server] nvarchar(max),
[CanConnectAsDefault] bit
);
DECLARE #ServerName nvarchar(max), #RetVal int;
DECLARE Svrs CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT ServerName = S.name
FROM sys.servers S;
OPEN Svrs;
FETCH NEXT FROM Svrs INTO #ServerName;
WHILE ##FETCH_STATUS = 0
BEGIN
BEGIN TRY
EXEC #RetVal = sys.sp_testlinkedserver #ServerName;
END TRY
BEGIN CATCH
SET #RetVal = sign(##error);
END CATCH;
INSERT INTO #Svrs
SELECT #ServerName
, CASE WHEN #RetVal = 0 THEN 1 ELSE 0 END;
FETCH NEXT FROM Svrs INTO #ServerName;
END;
CLOSE Svrs;
DEALLOCATE Svrs;
SELECT * FROM #Svrs
DROP TABLE #Svrs;
GO
--------------------------------------------------------------------
-- Report linked server references
--------------------------------------------------------------------
BEGIN TRY DROP TABLE #Refs; END TRY BEGIN CATCH END CATCH;
CREATE TABLE #Refs
(
[Server] nvarchar(max),
[Database] nvarchar(max),
[Schema] nvarchar(max),
[Object] nvarchar(max),
[Type] nvarchar(max)
);
DECLARE #DatabaseName nvarchar(max), #ServerName nvarchar(max), #SQL nvarchar(max);
DECLARE Refs CURSOR FAST_FORWARD READ_ONLY
FOR
SELECT DatabaseName = D.name
, ServerName = S.name
-- , ServerProvider = S.provider
-- , ServerSource = S.data_source
FROM sys.databases D
CROSS JOIN sys.servers S
WHERE D.name NOT IN ('master', 'tempdb', 'model', 'msdb', 'ReportServer', 'ReportServerTempDB');
OPEN Refs;
FETCH NEXT FROM Refs INTO #DatabaseName, #ServerName;
WHILE ##FETCH_STATUS = 0
BEGIN
SET #SQL = 'USE [' + #DatabaseName + '];
INSERT INTO #Refs
SELECT DISTINCT ''' + #ServerName + ''', ''' + #DatabaseName + ''', S.[name], O.[name], O.type_desc
FROM syscomments C
INNER JOIN sys.objects O ON C.id = O.[object_id]
LEFT JOIN sys.schemas S ON S.[schema_id] = O.[schema_id]
WHERE C.[TEXT] LIKE ''%[ ,~[( '''']' + #ServerName + '[ .,~])'''' ]%'' ESCAPE ''~'';'
PRINT 'Looking for ' + #ServerName + ' refs in ' + #DatabaseName -- + ': ' + #SQL;
EXEC sp_executesql #SQL;
FETCH NEXT FROM Refs INTO #DatabaseName, #ServerName;
END
CLOSE Refs;
DEALLOCATE Refs;
SELECT * FROM #Refs
DROP TABLE #Refs;
GO
--------------------------------------------------------------------
SET NOCOUNT OFF;
GO
This is not going to be a good idea for a production environment, but if you need a loopback linked server for dev purposes this worked for me:
EXEC sp_addlinkedserver #server = N'name_for_linked_server',
#srvproduct = N' ',
#provider = N'SQLNCLI',
#datasrc = N'name_of_my_sqlserver_instance',
#catalog = N'name_of_database'