There are two kinds of people in this world:
those who do everything using a command-line interface and those who
love using a graphical user interface (GUI) (some of you may fall
somewhere in between, and thus violate our hypothesis). SQL Server has
traditionally shipped with a few command-line tools. Perhaps the most
popular scripting application is SQLCMD
, a replacement to the aging osql.exe
command-line tool. Another scripting tool that is more application
agnostic is PowerShell. PowerShell is an extensible scripting engine
that is part of the Windows operating system. Some of the other products
in the box, like SQL Server Integration Services, have command-line
interfaces as well. DTEXEC.exe
is an example of an SSIS package execution utility.
SQLCMD
SQLCMD
, pronounced “SQL command,” is a command-line tool used to connect to SQL Server and submit T-SQL queries and commands. With SQLCMD
, you can do the following:
- Execute SQL scripts against any SQL Server instance.
- Define and pass variables from the command line as well as within scripts.
- Use predefined system variables.
- Include multiple SQL scripts inline.
- Dynamically change connections within the same script.
- Connect to SQL Server via the dedicated administrator connection (DAC).
Connecting to SQL Server
SQLCMD
allows users to make
multiple connections to different servers within the same script. For
example, suppose you had a few simple backup database scripts that each
backed up a database on a specific server. On SERVERONE
, the administrator could run the following backup script to back up the ReportServer database:
File: backup_ReportServer.sql
BACKUP DATABASE [ReportServer] TO DISK='C:\backups\ReportServer.bak'
On SERVERTWO
, the administrator could run the following script to back up the Products database:
File: backup_Products.sql
BACKUP DATABASE [Products] TO DISK='D:\SQLServer\Backups\Products.bak'
In the real world, you know that administrators
tend to have lots of scripts, and each perform its own functions on a
specific server. With SQLCMD
, you can now consolidate these into a single script using the :CONNECT
command. Let’s see this same scenario of backing up multiple databases using a single script:
File: backup_databases.sql
--Make a connection to SERVERONE using Windows Authentication
:CONNECT SERVERONE –E
--Issue a backup database command for ReportServer
BACKUP DATABASE [ReportServer] TO DISK='C:\backups\ReportServer.bak'
GO
--Make a connection to SERVERTWO using Windows Authentication
:CONNECT SERVERTWO –E
--Issue a backup database command for Products database
BACKUP DATABASE [Products] TO DISK='D:\SQLServer\Backups\Products.bak'
GO
Issuing the SQLCMD
command sqlcmd -E -i backup_databases.sql
yields the following result:
Sqlcmd: Successfully connected to server 'SERVERONE'.
Processed 280 pages for database 'ReportServer', file 'ReportServer' on file 4.
Processed 1 pages for database 'ReportServer', file 'ReportServer_log' on file 4.
BACKUP DATABASE successfully processed 281 pages in 0.369 seconds (6.238 MB/sec).
Sqlcmd: Successfully connected to server 'SERVERTWO'.
Processed 144 pages for database 'Products', file 'Products' on file 6.
Processed 1 pages for database 'Products', file 'Products_log' on file 6.
BACKUP DATABASE successfully processed 145 pages in 0.237 seconds (5.011 MB/sec)
Passing Variables
SQLCMD
also provides the ability to
pass variables from the command line and within the script itself. For
example, assume you have a generic “backup database” script called backup_database_generic.sql
that can be reused:
File: backup_database_generic.sql
:CONNECT $(myConnection)
BACKUP DATABASE $(myDatabase) TO DISK='C:\backups\$(myDatabase).bak'
At this point, you could call this script from the command line using the new -v
parameter. This parameter tells SQLCMD
that the following text is a variable, an example of which is shown here:
C:\>SQLCMD –E –i backup_database_generic.sql
–v myConnection="." myDatabase="ReportServer"
When the backup_database_generic.sql
script is run, it will have two variables defined: myConnection
, which is equal to "."
, and myDatabase
, which is equal to "ReportServer"
. Alternatively, if you wanted to use variables, you also could have set the parameters within another script, as shown here:
File: backup_database_main.sql
:SETVAR myConnection .
:SETVAR myDatabase ReportServer
:R "backup_database_generic.sql"
GO
When this script is executed, SQLCMD
will set the myConnection
variable to "."
(the period is an alias for the local server—you could have used "localhost"
or the actual name of the server as well) and the myDatabase
variable to "ReportServer"
; then, it will insert the contents of the backup_database_generic.sql
script inline.
PowerShell Provider for SQL Server
PowerShell is a command-line shell and scripting
language designed with significant improvements in power and
functionality when compared with VBScript and the Windows command
prompt. PowerShell is available in Windows Server 2008 and is also
available as a download (www.microsoft.com/powershell
)
for other versions of Windows. SQL Server 2012 provides a PowerShell
provider that enables you to easily access SQL Server instances, SMO
objects, and evaluate policies within the PowerShell environment.
Note You can launch PowerShell with the SQL Server provider using sqlps.exe
.
Not only can you easily write a script that
interacts with a SQL Server instance, but in that same script, you can
easily access other PowerShell providers like the one for Exchange
Server. You now have a consistent, seamless experience for scripting
across Microsoft products.
Consider the following example:
#Use WMI to obtain the AT Scheduler job list
$colItems = get-wmiobject -class "Win32_ScheduledJob"
-namespace "root\CIMV2" -computername "."
foreach ($objItem in $colItems)
{
$JobId = $objItem.JobID
$JobStatus = $objItem.JobStatus
$JobName = $objItem.Command
#Use the SQL Provider Invoke-SqlCmd cmdlet to insert our
## result into the JobReports table
Invoke-SqlCmd -Query "INSERT INTO master..JobReports
(job_engine, job_engine_id, job_name, job_last_outcome)
VALUES('NT','$JobId','$JobName','$JobStatus')"
}
#Now let's obtain the job listing from the JobServer object
#REPLACE the <SERVERNAME> with your own server name!
Set-Location "SQL:\<SERVERNAME>\default\JobServer"
$jobItems = get-childitem "Jobs"
foreach ($objItem in $jobItems)
{
$JobId = $objItem.JobID
$JobStatus = $objItem.LastRunOutcome
$JobName = $objItem.Name
Invoke-SqlCmd -Query "INSERT INTO master..JobReports
(job_engine, job_engine_id, job_name, job_last_outcome)
VALUES('AGENT','$JobId','$JobName','$JobStatus')"
}
Note
You will have to have the SQL Server Agent service running in order for
this script to work. You can start the SQL Server Agent by selecting
Start from the SQL Server Agent context menu in SSMS.
This example assumes you have a table in the master database that is defined as follows:
CREATE TABLE JobReports
(job_engine CHAR(6),
job_engine_id VARCHAR(50),
job_name VARCHAR(255),
job_last_outcome VARCHAR(50),
report_time datetime DEFAULT GETDATE())
After running your code, the JobReports table
would be filled with entries of both the Windows Task scheduled jobs and
SQL Agent jobs. An example result set follows:
job_engine job_engine_id job_name last_outcome report_time
---------- ------------- ------------ ------------ ------------------
NT 1 ntbackup.exe Success 2008-01-22 15:32:29.270
NT 4 CustomApp.exe Success 2008-01-22 15:32:29.280
AGENT 3226bb84-4e… BackupTestDB Succeeded 2008-01-22 15:32:29.290
AGENT 642f4e27-66… BackupDevDB Unknown 2008-01-22 15:32:29.300
AGENT ddc03a7b-45… ReIndxTestDB Unknown 2008-01-22 15:32:29.300
This script enumerates the jobs defined within
the Windows Task Scheduler as well as the SQL Server Agent jobs and
inserts these lists in a table. This is just one example of the seamless
scripting experience that can be obtained by using PowerShell.