I have many databases that on my SQL Server box that start with a prefix zzz.
Is there a way to do a DROP DATABASE (or some other method) that will remove and delete the data files? If a connection is opened, I want it closed.
Basically I just want them gone.
Generate a drop script, copy/paste & run:
exec master.sys.sp_msforeachdb 'if ''?'' like ''ZZZ%'' print ''drop database [?]'''
Or drop directly in the SQL string if your brave.
use master;
go
-- this will drop all dbs that start with t5....
declare #strsql varchar(500)
declare #curname sysname
select #curname = name from sys.databases
where name like 't5%'
while( ##rowcount> 0)
begin
set #strsql ='ALTER DATABASE ' +#curname +' SET OFFLINE WITH ROLLBACK IMMEDIATE'
exec (#strsql)
set #strsql ='drop database '+#curname
exec (#strsql)
select #curname = name from sys.databases
where name like 't5%'
end
You could write a dynamic SQL to do this:
use master
go
declare #dbnames nvarchar(max)
declare #statement nvarchar(max)
declare #closeconnection nvarchar(max)
set #dbnames = ''
set #statement = ''
select #dbnames = #dbnames + ',[' + name + ']' from sys.databases where name like 'zzz%'
if len(#dbnames) = 0
begin
print 'no databases to drop'
end
else
BEGIN
SET #closeconnection = 'alter database ' + substring(#dbnames, 2, len(#dbnames))
+ ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
set #statement = 'drop database ' + substring(#dbnames, 2, len(#dbnames))
print #statement
EXEC sp_executesql #closeconnection;
exec sp_executesql #statement;
end
Normally, the syntax to close all active connections to a database is:
--set it to single user to disable any other connections
ALTER DATABASE YourDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
--do your stuff here
--set it back to multiple users
ALTER DATABASE YourDatabase SET MULTI_USER
Alternatively, you could also generate a dynamic select list that populates your drop database statement along with close connection statements like this:
USE master;
Go
SELECT 'DROP DATABASE '+ name,
'alter database ' + name + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
FROM sys.databases WHERE name like 'zzz%';
GO
Courtesy: #SeriousM and OFH
I modified benjamin's script a bit so you only have to declare the prefix once.
use master;
declare #dbPrefix varchar(10)
set #dbPrefix = 'zzz_%';
declare #strsql varchar(500)
declare #curname sysname
select #curname = name from sys.databases
where name like #dbPrefix
while( ##rowcount> 0)
begin
set #strsql ='ALTER DATABASE ' +#curname +' SET SINGLE_USER WITH ROLLBACK IMMEDIATE'
exec (#strsql)
set #strsql ='drop database '+#curname
exec (#strsql)
select #curname = name from sys.databases
where name like #dbPrefix
end
Related
I'm creating a procedure to add users logins to a specific database. I call this procedure inside a trigger when a user is inserted in my database (I want to have different logins for each user in my site and in the access to the database).
Here's my trigger:
ALTER TRIGGER [dbo].[sys_users_insert]
ON [dbo].[sys_users]
AFTER INSERT
AS
BEGIN
DECLARE #USERNAME varchar(MAX)
SELECT #USERNAME = INSERTED.username
FROM INSERTED
EXEC CreateUser #USERNAME
END
The CreateUser is my procedure to create the logins in the database. The procedure is working. When I call it from a query editor:
EXEC CreateUser 'patricia.santos'
The user is created with success. But when I insert a user in the table and trigger runs the user isn't created nor is inserted in the table. And I found out that if I insert a user without dots in the username everything goes ok. So I believe is something with the dot but I would like to know if there's some workaround to accept usernames with dots.
UPDATE
Here's my procedure for creating logins in my database:
USE mydatabase
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[CreateUser]
#USERNAME AS VARCHAR(MAX) = ''
AS
BEGIN
declare #sql nvarchar(MAX)
If NOT EXISTS(select loginname from master.dbo.syslogins where name = #USERNAME)
BEGIN
set #sql = 'CREATE LOGIN ' + #USERNAME +
' WITH
PASSWORD = ''something'',
CHECK_POLICY = OFF'
exec(#sql)
set #sql = 'ALTER SERVER ROLE sysadmin ADD MEMBER ' + #username
exec(#sql)
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = #USERNAME)
BEGIN
-- Creates a database user for the login created above.
set #sql = 'CREATE USER [' + #USERNAME + '] FOR LOGIN [' + #USERNAME + ']'
exec(#sql)
EXEC sp_addrolemember 'db_owner', #USERNAME
END
END
END
Thank you :)
I think the issue may be with your CreateUser procedure. Ensure that the “CREATE USER” clause and ALTER SERVER ROLE clause wraps the user name in square brackets. Please see updated procedure below:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[CreateUser]
#USERNAME AS VARCHAR(MAX) = ''
AS
BEGIN
declare #sql nvarchar(MAX)
If NOT EXISTS(select loginname from master.dbo.syslogins where name = #USERNAME)
BEGIN
set #sql = 'CREATE LOGIN [' + #USERNAME + '] ' +
' WITH
PASSWORD = ''something'',
CHECK_POLICY = OFF'
exec(#sql)
set #sql = 'ALTER SERVER ROLE sysadmin ADD MEMBER [' + #username + ']'
exec(#sql)
IF NOT EXISTS (SELECT * FROM sys.database_principals WHERE name = #USERNAME)
BEGIN
-- Creates a database user for the login created above.
set #sql = 'CREATE USER [' + #USERNAME + '] FOR LOGIN [' + #USERNAME + ']'
exec(#sql)
EXEC sp_addrolemember 'db_owner', #USERNAME
END
END
END
Let me know if this helps at all.
I am using below code to switch db context to master and create procedure and setup start up script.
BEGIN TRY
DECLARE #dbName NVARCHAR(100)
SET #dbName = DB_NAME()
USE MASTER
IF NOT EXISTS (
SELECT name
FROM sys.objects
WHERE object_id = OBJECT_ID('spSetTrustWorthyOn')
)
EXEC (
'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE [' + #dbName + '] SET TRUSTWORTHY ON
END'
)
EXECUTE sp_procoption 'spSetTrustWorthyOn'
,'startup'
,'ON'
END TRY
BEGIN CATCH
END CATCH
GO
Now Issue is when I want to switch back to existing database.I could not find any way to go back to my original database.
I also can not hard code the database as this is dynamic query and we have multiple databases.
Any help will be much appreciated.
Thanks
Instead of a USE statement for the master database, qualify the catalog views and use EXEC sp_executesql statement with the master database qualified. This will avoid changing the database context in the outer script.
DECLARE
#dbName sysname = DB_NAME()
,#sql nvarchar(MAX);
BEGIN TRY
IF NOT EXISTS (
SELECT *
FROM master.sys.objects
WHERE object_id = OBJECT_ID(N'spSetTrustWorthyOn')
)
BEGIN
SET #sql = N'CREATE PROCEDURE spSetTrustWorthyOn
AS
BEGIN
ALTER DATABASE ' + QUOTENAME(#dbName) + ' SET TRUSTWORTHY ON;
END;';
EXECUTE master..sp_executesql #sql;
EXECUTE sp_procoption
'spSetTrustWorthyOn'
,'startup'
,'ON';
END;
END TRY
BEGIN CATCH
THROW;
END CATCH;
GO
I ran the below scripts
DECLARE #name VARCHAR(50)
DECLARE #Statment VARCHAR(500)
DECLARE DB_cursor CURSOR
FOR
SELECT name
FROM sys.databases
WHERE name NOT IN ( 'master', 'tempdb', 'model', 'msdb',
'ReportServer$SQLEXPRESS',
'ReportServer$SQLEXPRESSTempDB' )
OPEN DB_cursor
FETCH NEXT FROM DB_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #name
ALTER DATABASE [#name] SET SINGLE_USER WITH ROLLBACK IMMEDIATE
SET #Statment = 'Backup database ' + #name + ' To disk =N' + ''''
+ 'C:\Mas\' + #name + '.bak' + ''''
PRINT #Statment
EXEC(#Statment)
-- ALTER DATABASE [#name] SET MULTI_USER WITH ROLLBACK IMMEDIATE
-- EXEC sp_dboption #name, N'offline', N'false'
FETCH NEXT FROM DB_cursor INTO #name
END
CLOSE DB_cursor
DEALLOCATE DB_cursor
I got the error below
Msg 5011, Level 14, State 5, Line 11
User does not have permission to alter database '#name', the database does not exist, or the database is not in a state that allows access checks.
Msg 5069, Level 16, State 1, Line 11
ALTER DATABASE statement failed.
Please let me know where I am stuck on how to resolve this issue.
Note:- I ran this script in sa login.
Error message clearly states the reason. It's because Alter database command treats [#name] as a database and not a placeholder. You can include both the statements in dynamic query and then execute. Following should work:
DECLARE #name VARCHAR(50)
DECLARE #Statment VARCHAR(500)
DECLARE #Statment1 VARCHAR(500)
DECLARE #Statment2 VARCHAR(500)
select name from sys.databases where name NOT IN('master','tempdb','model','msdb','ReportServer$SQLEXPRESS','ReportServer$SQLEXPRESSTempDB')
OPEN DB_cursor
FETCH NEXT FROM DB_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
Print #name
SET #Statment = 'ALTER DATABASE ' + #name + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE; '
EXEC(#Statment)
SET #Statment1 = 'BACKUP DATABASE ' + #name + ' To disk =N' +''''+ 'C:\Mas\'+ #name +'.bak'+''''
EXEC(#Statment1)
SET #Statment2 = 'ALTER DATABASE ' + #name + ' SET MULTI_USER WITH ROLLBACK IMMEDIATE; '
EXEC(#Statment2)
EXEC(#Statment)
-- ALTER DATABASE [#name] SET MULTI_USER WITH ROLLBACK IMMEDIATE
-- EXEC sp_dboption #name, N'offline', N'false'
FETCH NEXT FROM DB_cursor INTO #name
END
CLOSE DB_cursor
DEALLOCATE DB_cursor
I change your T-SQL and this worked.
you most change destination of back up file in the anther path like this sample .
DECLARE #name VARCHAR(50)
DECLARE #Statment VARCHAR(500)
DECLARE DB_cursor CURSOR
FOR
SELECT name
FROM sys.databases
WHERE name NOT IN ( 'master', 'tempdb', 'model', 'msdb',
'ReportServer$SQLEXPRESS',
'ReportServer$SQLEXPRESSTempDB' )
OPEN DB_cursor
FETCH NEXT FROM DB_cursor INTO #name
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #name
-- DECLARE #A NVARCHAR(800) = ' ALTER DATABASE [' + #name + '] SET Multi_USER WITH ROLLBACK IMMEDIATE '
DECLARE #A NVARCHAR(800) = ' ALTER DATABASE [' + #name + '] SET SINGLE_USER WITH ROLLBACK IMMEDIATE '
EXECUTE Sp_ExecuteSQL #A
SET #Statment = 'Backup database ' + #name + ' To disk =N' + ''''
+ 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER2012\MSSQL\Backup\'
+ #name + '.bak' + ''''
PRINT #Statment
EXEC(#Statment)
-- ALTER DATABASE [#name] SET MULTI_USER WITH ROLLBACK IMMEDIATE
-- EXEC sp_dboption #name, N'offline', N'false'
FETCH NEXT FROM DB_cursor INTO #name
END
CLOSE DB_cursor
DEALLOCATE DB_cursor
Like down picture
you most grate access to SQL Server user in your path of Back up
The SQL command ALTER DATABASE does not allow a parameter hence it is treating #name as being the actual name and not as a parameter.
ALTER DATABASE
NAME
Specifies a logical name for the file.
logical_file_name
Is the name used in Microsoft SQL Server when referencing the file. The name must be unique within the database and conform to the rules for identifiers. The name can be a character or Unicode constant, a regular identifier, or a delimited identifier. For more information, see Using Identifiers.
You most use SP_ExecuteSQL for change state of Database
DECLARE #A NVARCHAR(800) = ' ALTER DATABASE [' +
#name +
'] SET SINGLE_USER WITH ROLLBACK IMMEDIATE '
EXECUTE Sp_ExecuteSQL #A
And then you can set database in single user mode.
I'm trying to switch the current database with a SQL statement.
I have tried the following, but all attempts failed:
USE #DatabaseName
EXEC sp_sqlexec #Sql -- where #Sql = 'USE [' + #DatabaseName + ']'
To add a little more detail.
EDIT: I would like to perform several things on two separate database, where both are configured with a variable. Something like this:
USE Database1
SELECT * FROM Table1
USE Database2
SELECT * FROM Table2
The problem with the former is that what you're doing is USE 'myDB' rather than USE myDB.
you're passing a string; but USE is looking for an explicit reference.
The latter example works for me.
declare #sql varchar(20)
select #sql = 'USE myDb'
EXEC sp_sqlexec #Sql
-- also works
select #sql = 'USE [myDb]'
EXEC sp_sqlexec #Sql
exec sp_execsql #Sql
The DB change only lasts for the time to complete #sql
http://blog.sqlauthority.com/2007/07/02/sql-server-2005-comparison-sp_executesql-vs-executeexec/
I have the same problem, I overcame it with an ugly -- but useful -- set of GOTOs.
The reason I call the "script runner" before everything is that I want to hide the complexity and ugly approach from any developer that just wants to work with the actual script. At the same time, I can make sure that the script is run in the two (extensible to three and more) databases in the exact same way.
GOTO ScriptRunner
ScriptExecutes:
--------------------ACTUAL SCRIPT--------------------
-------- Will be executed in DB1 and in DB2 ---------
--TODO: Your script right here
------------------ACTUAL SCRIPT ENDS-----------------
GOTO ScriptReturns
ScriptRunner:
USE DB1
GOTO ScriptExecutes
ScriptReturns:
IF (db_name() = 'DB1')
BEGIN
USE DB2
GOTO ScriptExecutes
END
With this approach you get to keep your variables and SQL Server does not freak out if you happen to go over a DECLARE statement twice.
Just wanted to thank KM for his valuable solution.
I implemented it myself to reduce the amount of lines in a shrinkdatabase request on SQLServer.
Here is my SQL request if it can help anyone :
-- Declare the variable to be used
DECLARE #Query varchar (1000)
DECLARE #MyDBN varchar(11);
-- Initializing the #MyDBN variable (possible values : db1, db2, db3, ...)
SET #MyDBN = 'db1';
-- Creating the request to execute
SET #Query='use '+ #MyDBN +'; ALTER DATABASE '+ #MyDBN +' SET RECOVERY SIMPLE WITH NO_WAIT; DBCC SHRINKDATABASE ('+ #MyDBN +', 1, TRUNCATEONLY); ALTER DATABASE '+ #MyDBN +' SET RECOVERY FULL WITH NO_WAIT'
--
EXEC (#Query)
try this:
DECLARE #Query varchar(1000)
DECLARE #DatabaseName varchar(500)
SET #DatabaseName='xyz'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table1'
EXEC (#Query)
SET #DatabaseName='abc'
SET #Query='SELECT * FROM Server.'+#DatabaseName+'.Owner.Table2'
EXEC (#Query)
I case that someone need a solution for this, this is one:
if you use a dynamic USE statement all your query need to be dynamic, because it need to be everything in the same context.
You can try with SYNONYM, is basically an ALIAS to a specific Table, this SYNONYM is inserted into the sys.synonyms table so you have access to it from any context
Look this static statement:
CREATE SYNONYM MASTER_SCHEMACOLUMNS FOR Master.INFORMATION_SCHEMA.COLUMNS
SELECT * FROM MASTER_SCHEMACOLUMNS
Now dynamic:
DECLARE #SQL VARCHAR(200)
DECLARE #CATALOG VARCHAR(200) = 'Master'
IF EXISTS(SELECT * FROM sys.synonyms s WHERE s.name = 'CURRENT_SCHEMACOLUMNS')
BEGIN
DROP SYNONYM CURRENT_SCHEMACOLUMNS
END
SELECT #SQL = 'CREATE SYNONYM CURRENT_SCHEMACOLUMNS FOR '+ #CATALOG +'.INFORMATION_SCHEMA.COLUMNS';
EXEC sp_sqlexec #SQL
--Your not dynamic Code
SELECT * FROM CURRENT_SCHEMACOLUMNS
Now just change the value of #CATALOG and you will be able to list the same table but from different catalog.
If SQLCMD is an option, it supports scripting variables above and beyond what straight T-SQL can do. For example: http://msdn.microsoft.com/en-us/library/ms188714.aspx
You can do this:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
select #SQL = 'USE ' + #dbName +'; {can put command(s) here}';
EXEC (#SQL);
{but not here!}
This means you can do a recursive select like the following:
Declare #dbName nvarchar(max);
SET #dbName = 'TESTDB';
Declare #SQL nvarchar(max);
SELECT #SQL = 'USE ' + #dbName + '; ' +(Select ... {query here}
For XML Path(''),Type)
.value('text()[1]','nvarchar(max)');
Exec (#SQL)
Use exec sp_execsql #Sql
Example
DECLARE #sql as nvarchar(100)
DECLARE #paraDOB datetime
SET #paraDOB = '1/1/1981'
SET #sql=N'SELECT * FROM EmpMast WHERE DOB >= #paraDOB'
exec sp_executesql #sql,N'#paraDOB datetime',#paraDOB
-- If you are using a variable for the database name.
-- Try something like this.
DECLARE #DBName varchar(50)
Set #DBName = 'Database1'; /* could be passed in by a parameter. */
IF( #DBName = 'Database1')
Begin
USE [Database1];
SELECT FROM Table1;
End
IF( #DBName = 'Database2')
Begin
USE [Database2];
SELECT FROM Table2;
End
IF( #DBName is null)
Begin
USE [Database1];
End
Any idea if it's possible to create a procedure in another database using T-SQL alone, where the name of the database is not known up front and has to be read from a table? Kind of like this example:
Use [MasterDatabase]
Declare #FirstDatabase nvarchar(100)
Select Top 1 #FirstDatabase=[ChildDatabase] From [ChildDatabases]
Declare #SQL nvarchar(4000)
Declare #CRLF nvarchar(10) Set #CRLF=nchar(13)+nchar(10)
Set #SQL =
'Use [+'#Firstdatabase+']'+#CRLF+
'Go'+#CRLF+
'Create Proc [Test] As Select 123'
Exec (#SQL)
See what I'm trying to do? This example fails because Go is actually not a T-SQL command but it something recognised by the query analyser/SQL management studio and produces an error. Remove the Go and it also fails because Create Proc must be the first line of the script. Arrgg!!
The syntax of T-SQL doesn't allow you do things like this:
Create [OtherDatabase].[dbo].[Test]
Which is a shame as it would work a treat! You can do that with Select statements, shame it's inconsistent:
Select * From [OtherDatabase]..[TheTable]
Cheers, Rob.
It's a pain, but this is what I do. I took this from an example I found on sqlteam, I think - you might have some quoting issues with the way I did the indiscriminate REPLACE:
DECLARE #sql AS varchar(MAX)
DECLARE #metasql as varchar(MAX)
DECLARE #PrintQuery AS bit
DECLARE #ExecQuery AS bit
SET #PrintQuery = 1
SET #ExecQuery = 0
SET #sql =
'
CREATE PROCEDURE etc.
AS
BEGIN
END
'
SET #metasql = '
USE OtherDatabase
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
'
IF #PrintQuery = 1
PRINT #metasql
IF #ExecQuery = 1
EXEC (#metasql)
DECLARE #UseAndExecStatment nvarchar(4000),
#SQLString nvarchar(4000)
SET #UseAndExecStatment = 'use ' + #DBName +' exec sp_executesql #SQLString'
SET #SQLString = N'CREATE Procedure [Test] As Select 123'
EXEC sp_executesql #UseAndExecStatment,
N'#SQLString nvarchar(4000)', #SQLString=#SQLString
This is how i have done with Alter Procedure:
DECLARE #metasql as varchar(MAX)
DECLARE #sql AS varchar(MAX)
SET #sql =
'ALTER PROCEDURE [dbo].[GetVersion]
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP(1)[Version] from VersionTable
END'
SET #metasql = '
USE MyProdDb
IF (OBJECT_ID(''GetVersion'') IS NOT NULL OR OBJECT_ID(''GetVersion'', ''P'') IS NOT NULL)
BEGIN
EXEC (''' + REPLACE(#sql, '''', '''''') + ''')
END
'
--PRINT #metasql
EXEC (#metasql)
You could shell out to osql using xp_cmdshell, I suppose.
I dont think this can be done with TSQL.
You could use an SSIS package that looped the names and connected to the servers dynamically which creates the schema (procs ) you need.
This is probably what I would do as it means it is all contained within the package.
Configuration can be kept separate by either using a table or external xml file that contained the list of server/databases to deploy the schema to.
It's not necessary to use EXEC within EXEC.
You can simply use OtherDatabase.sys.sp_executesql
DECLARE #sql AS varchar(MAX) = N'
CREATE PROCEDURE etc.
AS
BEGIN
-- whatever
END
';
PRINT #sql;
EXEC OtherDatabase.sys.sp_executesql #sql;