Related
I have had to set my database to single_user mode to allow for a dbcc checkdb repair and now I am unable to get my database back to multi_user. I have tried the following command in a query window against the master database but it hasn't worked as suggested by another Stack overflow post:
USE [master];
GO
ALTER DATABASE mydb SET MULTI_USER WITH ROLLBACK IMMEDIATE;
GO
I get the following error:
Msg 5064, Level 16, State 1, Line 2 Changes to the state or options of
database 'mydb' cannot be made at this time. The database is in
single-user mode, and a user is currently connected to it.
Msg 5069,
Level 16, State 1, Line 2 ALTER DATABASE statement failed.
If I right click on the database and try selecting properties then it errors saying that it is already in use.
Any help would be greatly appreciated.
Try killing the existing connection and setting MULTI_USER in the same batch:
USE master;
GO
DECLARE #sql nvarchar(MAX);
SELECT #sql = STRING_AGG(N'KILL ' + CAST(session_id as nvarchar(5)), ';')
FROM sys.dm_exec_sessions
WHERE database_id = DB_ID(N'mydb');
SET #sql = #sql + N';ALTER DATABASE mydb SET MULTI_USER;';
--PRINT #sql;
EXEC sp_executesql #sql;
GO
To get the session that was tied to the database I ran:
SELECT request_session_id
FROM sys.dm_tran_locks
WHERE resource_database_id = DB_ID('mydb')
This yielded a processid of 55
I then ran:
kill 55
go
And I was the able to use the alter multi_user line that was previously not working
The most likely reason why you're getting this error is that you still have the original session open that you used to set the database to single user. If you execute the above SQL in the same session that you set the database to single user in, it will work.
First try selecting the master database and run the alter command.
If it does not work, There exists some open connections to database and you can check and kill the processes
use master
GO
select
d.name,
d.dbid,
spid,
login_time,
nt_domain,
nt_username,
loginame
from sysprocesses p
inner join sysdatabases d
on p.dbid = d.dbid
where d.name = 'dbname'
GO
kill 52 -- kill the number in spid field
GO
exec sp_dboption 'dbname', 'single user', 'FALSE'
GO
During my integration tests, I try to drop database using:
USE master
ALTER DATABASE TestXyz SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE TestXyz
However, quite often (given the number of tests) one of the application background processes manages to get between SET SINGLE_USER and DROP DATABASE, which makes it single user of the database and breaks the DROP.
I can not use RESTRICTED_USER, as the application currently has db_owner permission (due to a large amount of legacy code, some of which requires it, so it will not be changed just for the tests).
I can not use OFFLINE as it does not delete database files from the disk.
How would you solve this problem?
OK plan b... iterate a drop of connections and rename the DB to get it away from the applications domain. Then drop it. To handle iterating through connections a try catch on the rename will hopefully allow it to run until it is able to drop the connection. Example code below creates a DB TestDB; renames it to testdb2 in the while loop before dropping it after the loop has succeeded.
-- Setup a scratch Db for testing
create database testdb
go
use testdb
while exists (select name from sys.databases where name = 'testdb')
Begin
DECLARE #DbName nvarchar(50) SET #DbName = N'testdb'
DECLARE #EXECSQL varchar(max) SET #EXECSQL = ''
SELECT #EXECSQL = #EXECSQL + 'Kill ' + Convert(varchar, SPId) + ';'
FROM MASTER..SysProcesses
WHERE DBId = DB_ID(#DbName) AND SPId <> ##SPId
EXEC(#EXECSQL)
Begin try
EXEC sp_renamedb 'testdb', 'testdb2'
end try
Begin Catch
print 'failed to rename'
End Catch
end
drop database testdb2
Try this once:
Stop application services and run your query.
Stop application services and restart SQL Server Services and then run your query.
I have finally solved it using the following approach:
ALTER LOGIN MyAppUser DISABLE
ALTER DATABASE TestXyz SET SINGLE_USER WITH ROLLBACK IMMEDIATE
DROP DATABASE TestXyz
ALTER LOGIN MyAppUser ENABLE
Since I can use different login for test database management process, this allows me to block application from accessing the DB. (The reason for SINGLE_USER here is just to kick already connected users. I haven't checked if ALTER LOGIN already does that, but I assume it does not).
Alternative option is to delete MyAppUser from the database before dropping it, however I thought about it only now and do not have code for it.
I have a database in SQL Server 2008 R2 and I created this stored procedure for restoring databases:
CREATE PROCEDURE [dbo].[usp_DBRestore]
#DBName nvarchar(60)
,#BackName nvarchar(120)
,#OutMessage nvarchar(4000) output
--,
--#DataName varchar(60),
--#DataFileName varchar(120),
--#LogName varchar(60),
--#LogFileName varchar(120)
AS
BEGIN TRY
USE [master]
ALTER DATABASE #DBName SET SINGLE_USER WITH ROLLBACK IMMEDIATE
RESTORE DATABASE #DBName FROM
DISK = #BackName WITH
FILE = 1, NOUNLOAD,
REPLACE,
PASSWORD = 'TEST'
SET #OutMessage = 'OK';
ALTER DATABASE #DBName SET MULTI_USER WITH ROLLBACK IMMEDIATE
END TRY
BEGIN CATCH
ALTER DATABASE #DBName SET MULTI_USER WITH ROLLBACK IMMEDIATE
INSERT [dbo].[ErrorLog]
(
[UserName],
[ErrorNumber],
[ErrorSeverity],
[ErrorState],
[ErrorProcedure],
[ErrorLine],
[ErrorMessage]
)
VALUES(
CONVERT(sysname, CURRENT_USER),
ERROR_NUMBER(),
ERROR_SEVERITY(),
ERROR_STATE(),
ERROR_PROCEDURE(),
ERROR_LINE(),
ERROR_MESSAGE()
)
END CATCH
When I execute code I see this error :
a USE database statement is not allowed in a procedure, function or
trigger.
How can I solve this error?
You cannot do this in that way - you basically have two options:
stick to a stored procedure, but in that case, you have to use dynamic SQL. Your stored procedure creates a string of SQL statements, which allows it to use USE master and it allows it to dynamically set the database name etc., and then it executes that SQL statement using sp_executesql #sqlRestoreStatement. If you want to check this out, you MUST be all means read (and understand) Erland Sommarskog's seminal article The Curse and Blessings of Dynamic SQL
you can use a regular SQL script, possibly with SQLCMD placeholders (if you have SQLCMD mode enabled in your SQL Server Management Studio) and execute the restore from a regular script (which you can put into your own template folder, for instance). In that case, you'd have something like:
:setvar dbname YourDatabaseNameHere
DECLARE #FileName NVARCHAR(255)
SET #FileName = N'D:\YourBackupDirectory\SomeDatabase.bak'
RESTORE DATABASE [$(dbname)]
FROM DISK = #FileName
WITH FILE = 1,
MOVE N'YourDatabase_Data' TO N'D:\MSSQL\Data\$(dbname).mdf',
MOVE N'YourDatbase_Log' TO N'D:\MSSQL\Data\$(dbname)_Log.ldf',
NOUNLOAD, REPLACE,
STATS = 2
GO
With this setup, you can easily use the SQL script as a template and restore any kind of database using it.
You don't need the USE statement. Best is to remove Use statement and create / Alter this sp on master database itself.
If you want to take a backup execute this SP from master DB. I can not see any other way out.
You can create a linked server and have that referenced in your stored procedure.
For example. LinkedServer.database.[dbo].StoredProcedure
Check out this
How to create the linked server for SQL Server 2008 where we have the database from 2000 and 2005
How long should it take to run
ALTER DATABASE [MySite] SET READ_COMMITTED_SNAPSHOT ON
I just ran it and it's taken 10 minutes.
How can I check if it is applied?
You can check the status of the READ_COMMITTED_SNAPSHOT setting using the sys.databases view. Check the value of the is_read_committed_snapshot_on column. Already asked and answered.
As for the duration, Books Online states that there can't be any other connections to the database when this takes place, but it doesn't require single-user mode. So you may be blocked by other active connections. Run sp_who (or sp_who2) to see what else is connected to that database.
Try this:
ALTER DATABASE generic SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE
OK (I am the original questioner) so it turns out this whole time I didn't even have the darn thing enabled.
Here's the ultimate code to run to enable snapshot mode and make sure it is enabled.
SELECT is_read_committed_snapshot_on, snapshot_isolation_state_desc,snapshot_isolation_state FROM sys.databases WHERE name='shipperdb'
ALTER DATABASE shipperdb SET allow_snapshot_isolation ON
ALTER DATABASE shipperdb SET SINGLE_USER WITH ROLLBACK IMMEDIATE
ALTER DATABASE shipperdb SET read_committed_snapshot ON
ALTER DATABASE shipperdb SET MULTI_USER
SELECT is_read_committed_snapshot_on, snapshot_isolation_state_desc,snapshot_isolation_state FROM sys.databases WHERE name='shipperdb'
This works even with connections active (presumably you're fine with them getting kicked out).
You can see the before and after state and this should run almost immediately.
IMPORTANT:
The option READ_COMMITTED_SNAPSHOT above corresponds to IsolationLevel.ReadCommitted in .NET
The option ALLOW_SNAPSHOT_ISOLATION above corresponds to IsolationLevel.Snapshot in .NET
Great article about different versioning
.NET Tips:
Looks like Isolationlevel.ReadCommitted is allowed in code even if not enabled by the database. No warning is thrown. So do yourself a favor and be sure it is turned on before you assume it is for 3 years like I did!!!
If you're using C# you probably want the ReadCommitted IsolationLevel and not Snapshot - unless you are doing writes in this transaction.
READ COMMITTED SNAPSHOT does optimistic reads and pessimistic writes. In contrast, SNAPSHOT does optimistic reads and optimistic writes. (from here)
bool snapshotEnabled = true;
using (var t = new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadCommitted
}))
{
using (var shipDB = new ShipperDBDataContext())
{
}
}
In additional you may get an error about being 'unable to promote' a transaction. Search for 'promotion' in Introducing System.Transactions in the .NET Framework 2.0.
Unless you're doing something special like connecting to an external database (or second database) then something as simple as creating a new DataContext can cause this. I had a cache that 'spun up' its own datacontext at initialization and this was trying to escalate the transaction to a full distributed one.
The solution was simple :
using (var tran = new TransactionScope(TransactionScopeOption.Suppress))
{
using (var shipDB = new ShipperDBDataContext())
{
// initialize cache
}
}
See also Deadlocked article by #CodingHorror
Try this code:
if(charindex('Microsoft SQL Server 2005',##version) > 0)
begin
declare #sql varchar(8000)
select #sql = '
ALTER DATABASE ' + DB_NAME() + ' SET SINGLE_USER WITH ROLLBACK IMMEDIATE ;
ALTER DATABASE ' + DB_NAME() + ' SET READ_COMMITTED_SNAPSHOT ON;
ALTER DATABASE ' + DB_NAME() + ' SET MULTI_USER;'
Exec(#sql)
end
I tried the command:
ALTER DATABASE MyDB SET READ_COMMITTED_SNAPSHOT ON
GO
against a dev box but the it took 10+ minutes and so I killed it.
I then found this:
https://willwarren.com/2015/10/12/sql-server-read-committed-snapshot/
and used his code block (which took about 1:26 to run):
USE master
GO
/**
* Cut off live connections
* This will roll back any open transactions after 30 seconds and
* restricts access to the DB to logins with sysadmin, dbcreator or
* db_owner roles
*/
ALTER DATABASE MyDB SET RESTRICTED_USER WITH ROLLBACK AFTER 30 SECONDS
GO
-- Enable RCSI for MyDB
ALTER DATABASE MyDB SET READ_COMMITTED_SNAPSHOT ON
GO
-- Allow connections to be established once again
ALTER DATABASE MyDB SET MULTI_USER
GO
-- Check the status afterwards to make sure it worked
SELECT is_read_committed_snapshot_on
FROM sys.databases
WHERE [name] = 'MyDB '
Try use master database before altering current database.
USE Master
GO
ALTER DATABASE [YourDatabase] SET READ_COMMITTED_SNAPSHOT ON
GO
I didn't take a second for me when i changed my DB to single user
All you need to do is this:
ALTER DATABASE xyz SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE;
No need to put the database into single user mode.
You will rollback uncommitted transactions though.
Try Shut off the other SQL services so that only the SQL server service is running.
Mine ran for 5 minutes then I cancelled it because it was obvious nothing was happening. Its a brand new server so there are no other users connected. I shut off the SQL Reporting Services and then ran it again.. took less than a second to complete.
With "ROLLBACK IMMEDIATE" it took about 20-30 seconds on my db which is 300GB.
ALTER DATABASE DBNAME SET READ_COMMITTED_SNAPSHOT ON WITH ROLLBACK IMMEDIATE
I want to rename a database, but keep getting the error that 'couldn't get exclusive lock' on the database, which implies there is some connection(s) still active.
How can I kill all the connections to the database so that I can rename it?
The reason that the approach that Adam suggested won't work is that during the time that you are looping over the active connections new one can be established, and you'll miss those. You could instead use the following approach which does not have this drawback:
-- set your current connection to use master otherwise you might get an error
use master
ALTER DATABASE YourDatabase SET SINGLE_USER WITH ROLLBACK IMMEDIATE
--do you stuff here
ALTER DATABASE YourDatabase SET MULTI_USER
Script to accomplish this, replace 'DB_NAME' with the database to kill all connections to:
USE master
GO
SET NOCOUNT ON
DECLARE #DBName varchar(50)
DECLARE #spidstr varchar(8000)
DECLARE #ConnKilled smallint
SET #ConnKilled=0
SET #spidstr = ''
Set #DBName = 'DB_NAME'
IF db_id(#DBName) < 4
BEGIN
PRINT 'Connections to system databases cannot be killed'
RETURN
END
SELECT #spidstr=coalesce(#spidstr,',' )+'kill '+convert(varchar, spid)+ '; '
FROM master..sysprocesses WHERE dbid=db_id(#DBName)
IF LEN(#spidstr) > 0
BEGIN
EXEC(#spidstr)
SELECT #ConnKilled = COUNT(1)
FROM master..sysprocesses WHERE dbid=db_id(#DBName)
END
Kill it, and kill it with fire:
USE master
go
DECLARE #dbname sysname
SET #dbname = 'yourdbname'
DECLARE #spid int
SELECT #spid = min(spid) from master.dbo.sysprocesses where dbid = db_id(#dbname)
WHILE #spid IS NOT NULL
BEGIN
EXECUTE ('KILL ' + #spid)
SELECT #spid = min(spid) from master.dbo.sysprocesses where dbid = db_id(#dbname) AND spid > #spid
END
Using SQL Management Studio Express:
In the Object Explorer tree drill down under Management to "Activity Monitor" (if you cannot find it there then right click on the database server and select "Activity Monitor"). Opening the Activity Monitor, you can view all process info. You should be able to find the locks for the database you're interested in and kill those locks, which will also kill the connection.
You should be able to rename after that.
I've always used:
ALTER DATABASE DB_NAME SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
SP_RENAMEDB 'DB_NAME','DB_NAME_NEW'
Go
ALTER DATABASE DB_NAME_NEW SET MULTI_USER -- set back to multi user
GO
ALTER DATABASE [Test]
SET OFFLINE WITH ROLLBACK IMMEDIATE
ALTER DATABASE [Test]
SET ONLINE
Take offline takes a while and sometimes I experience some problems with that..
Most solid way in my opinion:
Detach
Right click DB -> Tasks -> Detach...
check "Drop Connections"
Ok
Reattach
Right click Databases -> Attach..
Add... -> select your database, and change the Attach As column to your desired database name.
Ok
Select 'Kill '+ CAST(p.spid AS VARCHAR)KillCommand into #temp
from master.dbo.sysprocesses p (nolock)
join master..sysdatabases d (nolock) on p.dbid = d.dbid
Where d.[name] = 'your db name'
Declare #query nvarchar(max)
--Select * from #temp
Select #query =STUFF((
select ' ' + KillCommand from #temp
FOR XML PATH('')),1,1,'')
Execute sp_executesql #query
Drop table #temp
use the 'master' database and run this query, it will kill all the active connections from your database.
I usually run into that error when I am trying to restore a database I usually just go to the top of the tree in Management Studio and right click and restart the database server (because it's on a development machine, this might not be ideal in production). This is close all database connections.
In MS SQL Server Management Studio on the object explorer, right click on the database. In the context menu that follows select 'Tasks -> Take Offline'
Another "kill it with fire" approach is to just restart the MSSQLSERVER service.
I like to do stuff from the commandline. Pasting this exactly into CMD will do it:
NET STOP MSSQLSERVER & NET START MSSQLSERVER
Or open "services.msc" and find "SQL Server (MSSQLSERVER)" and right-click, select "restart".
This will "for sure, for sure" kill ALL connections to ALL databases running on that instance.
(I like this better than many approaches that change and change back the configuration on the server/database)
Here's how to reliably this sort of thing in MS SQL Server Management Studio 2008 (may work for other versions too):
In the Object Explorer Tree, right click the root database server (with the green arrow), then click activity monitor.
Open the processes tab in the activity monitor, select the 'databases' drop down menu, and filter by the database you want.
Right click the DB in Object Explorer and start a 'Tasks -> Take Offline' task. Leave this running in the background while you...
Safely shut down whatever you can.
Kill all remaining processes from the process tab.
Bring the DB back online.
Rename the DB.
Bring your service back online and point it to the new DB.
The option working for me in this scenario is as follows:
Start the "Detach" operation on the database in question. This wil open a window (in SQL 2005) displaying the active connections that prevents actions on the DB.
Kill the active connections, cancel the detach-operation.
The database should now be available for restoring.
Try this:
ALTER DATABASE [DATABASE_NAME]
SET SINGLE_USER
WITH ROLLBACK IMMEDIATE
Right click on the database name, click on Property to get property window, Open the Options tab and change the "Restrict Access" property from Multi User to Single User. When you hit on OK button, it will prompt you to closes all open connection, select "Yes" and you are set to rename the database....
These didn't work for me (SQL2008 Enterprise), I also couldn't see any running processes or users connected to the DB. Restarting the server (Right click on Sql Server in Management Studio and pick Restart) allowed me to restore the DB.
I'm using SQL Server 2008 R2, my DB was already set for single user and there was a connection that restricted any action on the database. Thus the recommended SQLMenace's solution responded with error. Here is one that worked in my case.
I use sp_who to get list of all process in database. This is better because you may want to review which process to kill.
declare #proc table(
SPID bigint,
Status nvarchar(255),
Login nvarchar(255),
HostName nvarchar(255),
BlkBy nvarchar(255),
DBName nvarchar(255),
Command nvarchar(MAX),
CPUTime bigint,
DiskIO bigint,
LastBatch nvarchar(255),
ProgramName nvarchar(255),
SPID2 bigint,
REQUESTID bigint
)
insert into #proc
exec sp_who2
select *, KillCommand = concat('kill ', SPID, ';')
from #proc
Result
You can use command in KillCommand column to kill the process you want to.
SPID KillCommand
26 kill 26;
27 kill 27;
28 kill 28;
You can Use SP_Who command and kill all process that use your database and then rename your database.