sql-servert-sqlsql-server-2008-r2single-userinvoke-sqlcmd

SQL Server - Single User Mode - Persist Which User


Question

Is there a way, when putting a database into single user mode, to specify that user (in my case it is the current user), and to ensure that even after that user disconnects, the single session kept reserved for that defined user; i.e. so it is genuinely single-user rather than single-concurrent-user?

Background

To give some context...

We have some scripts to refresh data in our test environments. Recently one failed with an error stating that the user account under which the scripts are run did not have access to the database. On investigation, the user was a sysadmin on the database; so this is clearly not true. However, I found that the script puts the database into Single User mode. When looking at the active sessions on the DB I can see that the single user is different to the expected account (rather it's another service account belonging to a system which polls this database intermittently). My assumption is the following has happened:

There are various possible solutions.

Update

The code uses PowerShell's Invoke-SqlCmd, which creates and drops connections for every command run; so as soon as the database is put in single-user mode the connection's dropped. I looked at this commands parameters to see if there were options for pooling/persisting connections, but the closest I could find was DedicatedAdministratorConnection, which it seems I should steer well clear of (http://www.brentozar.com/archive/2011/08/dedicated-admin-connection-why-want-when-need-how-tell-whos-using/) despite my initial hopes.


Solution

  • Sorry, no magic. There's no native solution for what you need. SQL Server makes sure the session that set single-user-mode is the session that survives. Once that session terminates (user disconnect, network broken, etc...), the "slot" is now available to any other connection including certain background worker threads or the SSMS object browser. DAC is not a good idea for anything other than rescue operations.

    You could use sqlcmd to run the scripts or call a script file from PS (invoke-sqlcmd -inputfile "c:\mysqlfile.sql" -serverinstance "servername\serverinstance" -database "mydatabase") so it's executed in a single session rather than individual sessions from PS.

    There are many other ways to prevent users from connecting to the DB (triggers, security, etc...) but those are typically risky in that you need to be very careful implementing the blocks and making sure the blocks are safely and correctly removed when done. This is non-trivial as you need to consider loss of connection or loss of instance and how to deal with the state after resume or recovery. Gets real messy, real quick so tread carefully.