Find SQL Server services running on a remote server using Powershell

You’re given a list of Windows servers and told they host multiple SQL Server instances.  How do you find which services are running?  Use Powershell:

 

get-service mssql* -computername myServer

 

It will give a nice list of services that have MSSQL at the beginning of their names:

Status Name DisplayName
Running MSSQL$INSTANCE2 SQL Server(INSTANCE2)
Running MSSQLSERVER SQL Server(MSSQLSERVER)
Stopped MSSQLServerADHe… SQL Server Active Directory Helper
Stopped MSSQLServerADHe… SQL Active Directory Helper Service

A shorter version:

gsv mssql* -cn myServer

Running .sql files from within the Query Editor using SQLCMD mode

As a DBA, I have a plethora of queries that I run on a consistent basis, commands that give me information about who’s connected to the instance, how mirroring is performing, what types of permissions a user has, etc.  Early in my career I began cataloging these queries.  My first file was called ‘useful_sql.txt’ (I still have it to this day) and it grew longer with each learned action.  I’d open up SSMS, click the folder icon to open the file and cut/paste any commands I needed to a new editor window.  What a pain.  As I grew in the profession, I began creating individual .sql files with a meaningful title for each specific query, but I still had to click the folder, find the file, wait for it to open in SSMS then click to execute the query.  A little less painful, but still not ideal.

Powershell offered some options but meant I had to leave the query editor.

Here was my solution: SQLCMD mode.

Enabling SQLCMD mode in the query editor allows you to run commands just as you would from the SQLCMD prompt.  The command I find most useful is:

:r <filename>

The file is read and the statements executed.  Now, instead of the hassle of opening a file and executing the commands thru a multi-click process, a query can be executed without leaving the query editor and keyboard, e.g.

:r I:/scripts/showSQL.sql

This executes the following query stored in the ‘showSQL.sql’ file that shows me who’s running which statement:

SELECT
   CONVERT(VARCHAR(8000),(SELECT SUBSTRING(sqltext.text,req.statement_start_offset/2,
   CASE
   WHEN req.statement_end_offset = -1 THEN 8000
   ELSE (req.statement_end_offset-req.statement_start_offset)/2
   END))) as 'SQL statement',
   req.blocking_session_id,
   req.session_id,
   req.status,
   req.command,
   DB_NAME(req.database_id) as "db_name",
   sess.login_name as "user name",
   req.start_time,
   req.cpu_time,
   req.total_elapsed_time
FROM
   sys.dm_exec_requests req
CROSS APPLY
   sys.dm_exec_sql_text(sql_handle) AS sqltext
JOIN
   sys.dm_exec_sessions sess
   on req.session_id = sess.session_id
where
   req.session_id > 50
order by
   req.status

 

For the above example, I have a file share that I’ve mapped to the I: drive.  I place all my scripts on that drive in the /scripts directory.  Files will be read relative to the directory that SQLCMD is invoked from; from the query editor this usually means c:\windows\systems32.  While you could place all your .sql scripts there, it’s not a good idea.  I suggest a fully qualified path to a place of your choosing.

SQLCMD mode has to be enabled in the SSMS query editor. It can be done per query editor session or set to open the editor in SQLCMD mode by default:

  • To enable SQLCMD mode for the current query editor session, go to the Query menu and select SQLCMD mode.
  • To enable SQLCMD mode by default, go to Tools > Options. In the left hand pane single click Query Execution then click the textbox By default, open new queries in SQLCMD mode

 

There a few other uses for SQLCMD mode as well as some limitations, here’s the link to BOL if you’d like more information:

http://msdn.microsoft.com/en-us/library/ms174187(v=SQL.105).aspx

Happy scripting!

<Disclaimer: the sample script was not created by me but I do not remember from where I copied it.  If it’s your work, please post a comment and I’ll be happy to give you credit.>

Simple restore using T-SQL

Restoring your database is simple using T-SQL.

I begin by using ‘RESTORE FILELISTONLY’ to get information about the files contained in the backup set. It returns a result set with information like ‘LogicalName’, ‘PhysicalName’, ‘FileGroupName’ and ‘Size’ (in 8K pages), among other things. You’ll use this information if you’re renaming or moving the .MDF or .LDF files contained in the backup set:

RESTORE FILELISTONLY
FROM DISK = 'D:\MSSQL10_50.SANDBOX_2K8R2\MSSQL\BACKUP\TestDB\TestDB.bak'

The output will be something like this:

To restore a backup over the existing database, use:

RESTORE DATABASE [TestDB]
-- location and name of the backup set file
FROM DISK = 'D:\MSSQL10_50.SANDBOX_2K8R2\MSSQL\BACKUP\TestDB\TestDB.bak' 
WITH RECOVERY,
REPLACE

The REPLACE parameter instructs the engine to overwrite the existing data and log files. Use this cautiously. If the REPLACE command is not used and the database has changes later than what is stored in the backup set, you’ll receive an error:

“Msg 3159, Level 16, State 1, Line 1
The tail of the log for the database “TestDB” has not been backed up. Use BACKUP LOG WITH NORECOVERY to backup the log if it contains work you do not want to lose. Use the WITH REPLACE or WITH STOPAT clause of the RESTORE statement to just overwrite the contents of the log.”

If the data and log directory are different from the backup set, use:

RESTORE DATABASE [TestDB]
-- location and name of the backup set file
FROM DISK = 'D:\MSSQL.1\MSSQL\BACKUP\TestDB\TestDB.bak' 
WITH RECOVERY,
-- location and name of the data file
MOVE 'TestDB' to 'D:\MSSQL.1\MSSQL\TestDB.mdf', 
-- location and name of the log file
MOVE 'TestDB_log' to 'D:\MSSQL.1\MSSQL\TestDB_log.ldf'

For the above example, I’ve copied a .bak file from my named instance server SANDBOX_2K8R2 to a default instance server. The location of the SQL Server data directory is different on the two servers, so we need to use the MOVE parameter with the RESTORE command.

Be careful with the naming of the data and log files. Although the extensions have no meaning to the SQL Server engine, the convention is to use .mdf for the master data file (and .ndf for any others) and .ldf for the log files. Any other convention could get confusing.

Create a linked server to an Oracle database (and not lock up the instance)

I’ve created numerous linked servers to Oracle from SQL Server and what should be a straightforward process using SSMS has caused me some headaches.  For whatever reason, when using the GUI, I’ve actually locked up the entire SQL Server instance and had to restart the service when trying to create a linked server to Oracle.  This is not good when working in production. The following will outline the do’s and don’ts for this process.

To begin: download, install and configure the Oracle client to communicate with your Oracle database.  This link can assist you with that process.

Once the Oracle client is installed, here’s the steps that can cause potential problems (skip to code section for the correct solution):

Using SQL Server 2005 Management Studio, I expanded ‘Server Objects‘, right clicked ‘Linked Servers‘ and chose ‘New Linked Server‘.  I entered a name in the ‘Linked Server‘ text box then went to ‘Provider‘ (after choosing the ‘Other Data Source‘ radio button).  The drop down showed me a list of the drivers installed; to connect to Oracle we have two choices: the Microsoft driver, called ‘Microsoft OLE DB Provider for Oracle‘ and the Oracle driver, ‘Oracle Provider for OLE DB‘.  Here’s where I ran into problems: if I chose the Oracle driver, about half the time the drop down would freeze and eventually the instance would become unresponsive.  I’d have to restart the SQL Server service.

Here’s the code section, showing how to create a linked server using T-SQL:

-- Create the linked server
EXEC master.dbo.sp_addlinkedserver
@server = N'LinkToOracle', -- name of the Linked Server
@srvproduct=N'Oracle',
@provider=N'MSDAORA', -- Microsoft driver
@datasrc=N'myOracleDatabase' -- TNS name of the Oracle database
go
 
-- Add the user to connect to the Oracle database
EXEC master.dbo.sp_addlinkedsrvlogin
@rmtsrvname=N'LinkToOracle', -- name of the Linked Server
@useself=N'False',
@locallogin=NULL,
@rmtuser=N'myOracleUser', -- Oracle database user
@rmtpassword='########' -- Enter the correct password here
go

The above code will create a linked server called ‘LinkToOracle’ using the Microsoft OLE DB driver, then using an Oracle database user to authenticate.  To use the Oracle OLE DB driver, replace the ‘@provider’ line in the top call to the sp_addlinkedserver stored procedure with:

@provider=N'OraOLEDB.Oracle'

To test connectivity, open a query window and do a select from a table that the Oracle user has access to.  The following assumes an ‘Employee’ table in the ‘HumanResources’ schema exists in the Oracle database:

SELECT count(*) FROM LinkToOracle..HumanResources.Employee

Notice the double dots between ‘LinkToOracle‘ and ‘HumanResources‘.  This denotes use of the default catalog of the linked server.

To send the query to the Oracle database for processing, which would allow you to write PL/SQL, use the OPENQUERY command:

SELECT * 
FROM OPENQUERY(LinkToOracle, 'select count(*) from HumanResources.Employees')

Get information about your databases

Let’s begin with the catalog view ‘sys.databases’. It has lots of useful information:

-- find the name, owner, create date and recovery mode of all user databases
SELECT 
   name,
   SUSER_SNAME(owner_sid),  -- SUSER_SNAME() returns the login when passed a 'sid'
   create_date, 
   recovery_model_desc
FROM 
   sys.databases
WHERE 
   database_id > 4;

You can also find the compatibility level, collation and state of the database, among other things. Database IDs 1 – 4 are the system databases:

1 = master
2 = tempdb
3 = model
4 = msdb

There’s a system stored procedure that gives similar information. Pass a database name as a parameter to find information about that database. If no argument is supplied, information is returned about all databases:

EXEC sp_helpdb N'AdventureWorks';
EXEC sp_helpdb;

Find the name, location and size of your database files using ‘sys.database_files’:

-- type_desc will let you know if it's the data or log file
SELECT 
   name, 
   type_desc, 
   physical_name, 
   size
FROM 
   sys.database_files

Working with SQL Server logins

SQL Server ‘logins’ are what’s known as a server level ‘principal’. From BOL, “Principals are entities that can request SQL Server resources.” There are two types of logins, one where the credentials are stored in SQL Server and one where the credentials come from Windows, either locally or through Active Directory. So let’s begin with a list of our logins. Not surprising, the catalog view ‘sys.server_principals’ works nicely:

-- List logins on the instance
SELECT 
   name, 
   sid, 
   type_desc, 
   create_date, 
   default_database_name
FROM 
   sys.server_principals
WHERE 
   type = 'S' 
OR 
   type = 'U'
ORDER BY type_desc
For the above example, the various ‘types’ are:
S = SQL Server login
U = Windows authenticated login

 

The catalog view ‘sys.sql_logins’ is also useful, but only shows information for logins that store credentials in SQL Server:

SELECT 
   name, 
   sid, 
   create_date, 
   default_database_name,
   is_policy_checked, 
   is_expiration_checked
FROM 
   sys.sys_logins

 

We can create a new login with the following code:

-- Create a SQL Server login called 'testuser', adhering 
-- to password expiration and policy standards
CREATE LOGIN [testuser] WITH PASSWORD = 'StrongPasswordHere!',
CHECK_EXPIRATION=ON, CHECK_POLICY=ON
-- Create a Windows authenticated login
-- called 'testuser' in the domain 'united'
CREATE LOGIN [united\testuser] FROM WINDOWS,
CHECK_EXPIRATION=ON, CHECK_POLICY=ON

 

Conversely, we can delete a login:

-- Delete the 'testuser' SQL Server login
DROP LOGIN [testuser]
 
-- Delete the 'united\testuser' Windows authenticated login
DROP LOGIN [united\testuser]

 

Here’s more useful commands when working with logins:

-- Change 'testuser' password from 'StrongPasswordHere!' 
-- to 'AnotherStrongPassword!'
ALTER LOGIN [testuser] WITH PASSWORD = 'AnotherStrongPassword!'
 
-- Disable and enable the 'testuser' login
ALTER LOGIN [testuser] DISABLE
ALTER LOGIN [testuser] ENABLE