SQL Server management can be done from PowerShell or any other .NET language using the Server Management Objects library, and the data can be accessed using ADO.NET.. Creating databases
Trang 1$errmsg = $errmsg + " Error Message: " + $error[0].Exception.Message
$errmsg = $errmsg + " Error Message: " +
$error[0].FullyQualifiedErrorId
$log = New-Object System.Diagnostics.EventLog(’Application’)
$log.set_source("MyPowerShellScript")
$log.WriteEntry($errmsg) }
Trap {
# Handle the error Error_Handler
# End the program
break;
}
# Your code here
Then, the event log can be queried using the following cmdlet:
get-Eventlog application | where-Object {$_.Message -like
"Error*"} | format-Table
This enables consistent notification of errors in PowerShell scripts
Communicating with SQL Server
The two main reasons for communicating with SQL Server are to manage the server and to use the data
contained on the server in some way Not only are administrators expected to manage the server
effi-ciently, they’re also frequently asked to extract some corporate data to send to another application, to
make quick updates to correct a problem, or other such requests SQL Server management can be done
from PowerShell or any other NET language using the Server Management Objects library, and the data
can be accessed using ADO.NET
SQL Server Management Objects
SQL Server Management Objects (SMO) and its related sisters (RMO for Replication Management
Objects and AMO for Analysis Services Management Objects) are object libraries that provide a
programmatic way to manage Microsoft SQL SMO can be used to manage SQL Server 2000, 2005,
and 2008 It was introduced with SQL Server 2005, but supports the management of SQL Server 2000
instances as well SMO was built using the NET Framework, so the objects it exposes are available in
PowerShell
Trang 2SMO has been designed to be easier to use and more efficient than its predecessor DMO (Distributed
Management Objects) For example, when returning the collection of databases for a server, SMO
will only return the name and the schema for the databases on the server DMO would return the
fully instantiated collections of every database on that server, taking time to both populate that entire
collection and increase the network bandwidth required to pass it over the wire
Before using SMO within PowerShell, the SMO assembly must be loaded into the environment If
PowerShell is started from within SQL Server Management Studio or from SQL Server Agent, the
sqlps.exeprogram is run, not basic PowerShell.Sqlps.exeis discussed later in this chapter
When running the native PowerShell environment interactively, the following commands are
required:
[System.Reflection.Assembly]::LoadWithPartialName(’Microsoft
.SqlServer.SMO’) | out-null
[System.Reflection.Assembly]::LoadWithPartialName(’Microsoft
.SqlServer.SMOExtended’) | out-null
If SMO objects are going to be loaded on a regular basis, these commands can be loaded into
a profile file User profiles are generally loaded into%UserProfile%\My Documents\
WindowsPowerShell\Microsoft.PowerShell_profile.ps1, where%UserProfile%is
usually inc:\Users\username
The results of the command are piped to theout-nulldevice because the version number of the
library is generally not needed
After loading the SMO libraries, it’s easy to connect to a server (using Windows Authentication) by
issu-ing the followissu-ing command:
$sqlsvr = new-object (’Microsoft.SqlServer.Management
.Smo.Server’) ‘MyServer’
The$sqlsvrvariable now contains aServerobject for theMyServerinstance of SQL Server The
properties and methods of theServerobject can be seen by piping the variable into theGet-Member
cmdlet:
$sqlsvr | Get-Member
The SMO object library is best represented in a chart for ease in understanding The basic object is the
Serverobject, and managing the server starts with connecting to that object For example, the SMO
objects used in managing user databases is shown in Figure 7-1
Creating databases and database objects using SMO may seem counterintuitive, as usually these objects
are created using Transact-SQL scripts, but automating the processes that create the objects can provide
consistency in an area that is usually quite inconsistent
Trang 3FIGURE 7-1
Database objects
Database FileGroups FileGroup
Files DataFile
LogFile LogFiles
IsDefault
Name FileName GrowthType Growth MaxSize Alter
Create
Name FileName GrowthType Growth MaxSize
Server
Settings DefaultFile DefaultLog Databases
Trang 4SQL Server requires that a database have a PRIMARY filegroup and that the system tables (the database
meta-data) reside in that filegroup It is recommended that you keep your application data out of the
PRIMARY filegroup, both from a manageability perspective and a performance perspective When
creat-ing a database uscreat-ing SQL Server Management Studio (SSMS), it can be tedious to create a database with
the desired size and file location and with a separate, default filegroup to hold the application data This
is a relatively simple process using SMO
Best Practice
Don’t place application data in the primary filegroup, because the size and location of application file
groups are easier to control and manage, especially as the size of the database grows The database
metadata must exist in the primary filegroup, and once the primary filegroup is online, the database is
considered available As each additional filegroup becomes available, the data within it is usable by
applications, but smaller, discrete filegroups can improve the overall uptime for the application data
Listing 7-3 shows the script to create a database The example database is a database calledMyAppDB,
which will have a 5MB file in the primary filegroup to hold the database metadata This file will be
allowed to grow by 25% when required, but it shouldn’t be required The percentage or fixed growth
size chosen depends on the actual usage history for the application, but a growth size too small can
cause excessive fragmentation, and a growth size too large can take too much time when the autogrow
event occurs The logical nameMyAppDB_SysDatawill be used for this file and it will be placed in the
default data path for the server
The application data will be located in a second filegroup calledAppFG, which will then be set to
be the default filegroup for the database The filegroup will contain one file with a logical name of
MyAppDB_AppDataand will also be housed in the default data path for the server The initial size will
be set to 25MB and it will be allowed to grow by 25% each time it is required, but set a maximum
size of 100MB
Log files in SQL Server do not use filegroups, so a log file namedMyAppDB_Logwill be added to the
LogFilescollection of the database, and it will be housed in the default log file path for the server Its
initial size will be set to 10MB and it will be allowed to grow by 25% each time it is required, but you
won’t set a maximum size for the log file
Once the structural objects have been created for the database, theCreate()method is executed,
but SQL Server automatically sets the default filegroup to primary when a database is created Once it
has been created, the script sets the default filegroup toAppFGusing theAlter()method at both
the FileGroup and Database levels Because of a bug in SMO, theDefaultFileandDefaultLog
properties in the Server object’sSettingscollection don’t properly initialize Therefore, the
script places the files in the same location as the master database data and log files, as defined in
theMasterDBPathandMasterDBLogPathproperties in theServerobject’sInformation
collection
Trang 5LISTING 7-3
CreateDB.ps1
#createdb.ps1
#Creates a new database using our specifications
[System.Reflection.Assembly]::LoadWithPartialName(’Microsoft.SqlServer.SMO’)
| out-null
# Instantiate a new SMO Server object and connect to server SQLTBWS\INST01
$s =
new-object (’Microsoft.SqlServer.Management.Smo.Server’) ‘SQLTBWS\INST01’
$dbname = ‘SMO_DB’
# Set the database logical and physical names
$syslogname = $dbname + ‘_SysData’
$applogname = $dbname + ‘_AppData’
$loglogname = $dbname + ‘_Log’
# An SMO bug in SQL 2005 and SQL 2008 cause the default locations to possibly
be null
$fileloc = $s.Settings.DefaultFile
$logloc = $s.Settings.DefaultLog
if ($fileloc.Length = 0) {
$fileloc = $s.Information.MasterDBPath
}
if ($logloc.Length = 0) {
$logloc = $s.Information.MasterDBLogPath
}
# Place the files in the same location as the master database
$dbsysfile = $fileloc + ‘\’ + $syslogname + ‘.mdf’
$dbappfile = $fileloc + ‘\’ + $applogname + ‘.ndf’
$dblogfile = $logloc + ‘\’ + $loglogname + ‘.ldf’
# Instantiate the database object and add the filegroups
$db =
new-object (’Microsoft.SqlServer.Management.Smo.Database’) ($s, $dbname)
$sysfg =
new-object (’Microsoft.SqlServer.Management.Smo.FileGroup’) ($db, ‘PRIMARY’)
$db.FileGroups.Add($sysfg)
$appfg = new-object (’Microsoft.SqlServer.Management.Smo.FileGroup’) ($db,
‘AppFG’)
$db.FileGroups.Add($appfg)
# Create the file for the system tables
$dbdsysfile = new-object (’Microsoft.SqlServer.Management.Smo.DataFile’)
($sysfg, $syslogname)
Trang 6$dbdsysfile.GrowthType = ‘None’
$dbdsysfile.IsPrimaryFile = ‘True’
# Create the file for the Application tables
$dbdappfile = new-object (’Microsoft.SqlServer.Management.Smo.DataFile’)
($appfg, $applogname)
$appfg.Files.Add($dbdappfile)
$dbdappfile.FileName = $dbappfile
$dbdappfile.Size = [double](25.0 * 1024.0)
$dbdappfile.GrowthType = ‘Percent’
$dbdappfile.Growth = 25.0
$dbdappfile.MaxSize = [double](100.0 * 1024.0)
# Create the file for the log
$dblfile = new-object (’Microsoft.SqlServer.Management.Smo.LogFile’) ($db,
$loglogname)
$db.LogFiles.Add($dblfile)
$dblfile.FileName = $dblogfile
$dblfile.Size = [double](10.0 * 1024.0)
$dblfile.GrowthType = ‘Percent’
$dblfile.Growth = 25.0
# Create the database
$db.Create()
# Set the default filegroup to AppFG
$appfg = $db.FileGroups[’AppFG’]
$appfg.IsDefault = $true
$appfg.Alter()
$db.Alter()
ADO.NET
ADO.NET consists of a set of object libraries that enable communication between client programs and
the source of the data, in this case SQL Server Two groups of objects are defined within ADO.NET: a
set of connected objects, which enable the client to communicate with the server using an active
connec-tion, and a set of disconnected objects, which act as an offline data cache, enabling the client application
to work with the data independently of the server These two groups of objects are listed in Table 7-4
and Table 7-5, respectively
The first thing needed for a session using ADO.NET is a connection to the database The
SqlConnectionobject is initialized using the following commands:
$connstring = "Data Source=myServerAddress;Initial
Catalog=myDataBase;Integrated Security=SSPI;"
# or its equivalent
$connstring = "Server=myServerAddress;
Database=myDataBase;Trusted_Connection=True;"
$cn = new-object system.data.SqlClient.SqlConnection($connstring)
Trang 7TABLE 7-4
Connected Objects
Connection object A connection to the data source
Command object Can represent a query against a database, a call to a stored procedure, or a
direct request to return the contents of a specific table DataReader object Designed to return query results as quickly as possible
Transaction object Groups a number of changes to a database and treats them as a single unit of
work The Connection object has a BeginTransaction method that can be used to create Transaction objects
Parameter object Allows the specification of parameters for stored procedures or parameterized
queries DataAdapter object Acts as a bridge between the database and the disconnected objects in the
ADO.NET object model
TABLE 7-5
Disconnected Objects
DataTable object Allows the examination of data through collections of rows and columns
DataColumn object Corresponds to a column in a table
Constraint object Defines and enforces column constraints
DataRow object Provides access to the DataTable’s Rows collection
DataSet object The container for a number of DataTable objects
DataRelation object Defines the relations between DataTables in the DataSet object
DataView object Allows the examination of DataTable data in different ways
Many options are available for configuring connection strings, most of which are available at
www.connectionstrings.com/sql-server-2008
Once the connection object is initialized, the connection can be used to send queries to SQL Server
Listing 7-4 shows an example using theAdventureWorkssample database, returning query results to
Trang 8LISTING 7-4
EmployeeExtract.ps1
#employeeextract.ps1
#This script pulls info from the Person.Contact table in AdventureWorks
and presents it to the user
$cn = new-object system.data.SqlClient.SqlConnection("Data Source=SQLTBWS\INST01;
Integrated Security=SSPI;Initial Catalog=AdventureWorks");
$ds = new-object "System.Data.DataSet" "dsPersonData"
$q = "SELECT TOP 25 [ContactID]"
$q = $q + " ,[FirstName]"
$q = $q + " ,[LastName]"
$q = $q + " ,[EmailAddress]"
$q = $q + " ,[Phone]"
$q = $q + " FROM [AdventureWorks].[Person].[Contact]"
$da = new-object "System.Data.SqlClient.SqlDataAdapter" ($q, $cn)
$da.Fill($ds)
$dtPerson = new-object "System.Data.DataTable" "dtPersonData"
$dtPerson = $ds.Tables[0]
$dtPerson | FOREACH-OBJECT { [string]$_.ContactID + ": " + $_.FirstName + ", " +
$_.LastName + ", " + $_.EmailAddress + ", " + $_.Phone }
The script first connects with the database server, and then builds a string containing the query to be
run That query and the connection object are then supplied as parameters to aSqlDataAdapter
object Using theDataAdapter’sFillmethod, aDataSetis populated with the query results
One table results from the query, so theDataTableobject is populated using the first table in the
DataSet ThisDataTableis then iterated through to return results to the user Running this script
produces these results:
25
1: Gustavo, Achong, gustavo0@adventure-works.com, 398-555-0132
2: Catherine, Abel, catherine0@adventure-works.com, 747-555-0171
3: Kim, Abercrombie, kim2@adventure-works.com, 334-555-0137
4: Humberto, Acevedo, humberto0@adventure-works.com, 599-555-0127
5: Pilar, Ackerman, pilar1@adventure-works.com, 1 (11) 500 555-0132
6: Frances, Adams, frances0@adventure-works.com, 991-555-0183
7: Margaret, Smith, margaret0@adventure-works.com, 959-555-0151
8: Carla, Adams, carla0@adventure-works.com, 107-555-0138
9: Jay, Adams, jay1@adventure-works.com, 158-555-0142
10: Ronald, Adina, ronald0@adventure-works.com, 453-555-0165
11: Samuel, Agcaoili, samuel0@adventure-works.com, 554-555-0110
12: James, Aguilar, james2@adventure-works.com, 1 (11) 500 555-0198
13: Robert, Ahlering, robert1@adventure-works.com, 678-555-0175
14: Fran¸cois, Ferrier, fran¸cois1@adventure-works.com, 571-555-0128
15: Kim, Akers, kim3@adventure-works.com, 440-555-0166
16: Lili, Alameda, lili0@adventure-works.com, 1 (11) 500 555-0150
Trang 917: Amy, Alberts, amy1@adventure-works.com, 727-555-0115 18: Anna, Albright, anna0@adventure-works.com, 197-555-0143 19: Milton, Albury, milton0@adventure-works.com, 492-555-0189 20: Paul, Alcorn, paul2@adventure-works.com, 331-555-0162 21: Gregory, Alderson, gregory0@adventure-works.com, 968-555-0153 22: J Phillip, Alexander, jphillip0@adventure-works.com, 845-555-0187 23: Michelle, Alexander, michelle0@adventure-works.com, 115-555-0175 24: Sean, Jacobson, sean2@adventure-works.com, 555-555-0162
25: Phyllis, Allen, phyllis0@adventure-works.com, 695-555-0111
Scripting SQL Server Tasks
While using PowerShell interactively to perform maintenance tasks may be fun and interesting, it doesn’t
save much time Scripting enables administrators to perform the same function the same way every time,
saving the time it might take to remember how to solve a problem and enabling the administrator to
focus on new problems as they occur Typically, administrators create scripts for two basic categories of
tasks: administrative tasks, those that perform normal administrative functions, and data-based tasks.
Administrative tasks
A script showing how to create a database was shown in Listing 7-3, but nearly every administrative
activity required of a SQL Server DBA can be scripted using PowerShell and SMO
The most important task a DBA must perform is backing up databases First, the backup directory is
needed so SQL Server knows where to put the backup files Getting this information using Transact-SQL
isn’t possible (without using extended stored procedures to poll the system registry values, that is), but
it’s quite easy by using the objects shown in Figure 7-2 to get theBackupDirectoryproperty from
the server’sSettingscollection
FIGURE 7-2
The SMO objects used to access the location of the backup directory
Settings
BackupDirectory Server
Once the backup directory is known, it’s easy to set the properties necessary to perform the backup
Figure 7-3 shows the SMO objects needed to back up the database
Trang 10Databaseproperty tells SQL Server which database to back up, and theMediaDescriptiondefines
whether the backup is sent to disk or tape (disk is much more efficient, in general) Each file or tape
device used as a destination for the backup is defined and added to the backup’sDevicescollection
Once these properties have been set, theSqlBackupmethod is called to perform the backup
FIGURE 7-3
The SMO backup properties and methods
Backup
Action BackupSetDescription
BackupSetName Database MediaDescription Devices BackupDevice AddDevice SqlBackup
The example script in Listing 7-5 loops through the databases for which theIsSystemObject
prop-erty is set toFalseand theIsMirroringEnabledproperty is also set toFalse It uses theGet
-Datecmdlet to get the current system date and time and puts that into the filename for the backup
file If the database that has been backed up is not inSIMPLErecovery mode, the script also performs
a transaction log backup
LISTING 7-5
Backup.ps1
#backup.ps1
#Performs a Full backup followed by a transaction log backup on all user
databases
[System.Reflection.Assembly]::LoadWithPartialName(’Microsoft.SqlServer.SMO’)
| out-null
[System.Reflection.Assembly]::LoadWithPartialName(’Microsoft.SqlServer
.SmoExtended’) | out-null