<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>SQL Server</title><link>http://www.pluralsight.com/blogs/dan/category/81.aspx</link><description>SQL topics, espcially SQL Server 2005</description><managingEditor>Dan Sullivan</managingEditor><dc:language>en-US</dc:language><generator>.Text Version 0.95.2004.102</generator><item><dc:creator>Dan Sullivan</dc:creator><title>PowerSMO At Work Part II</title><link>http://pluralsight.com/blogs/dan/archive/2007/03/12/46428.aspx</link><pubDate>Mon, 12 Mar 2007 10:36:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2007/03/12/46428.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/46428.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2007/03/12/46428.aspx#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/46428.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/46428.aspx</trackback:ping><description>&lt;P&gt;The next article in my series&amp;nbsp;on PowerShell and SMO, &lt;A href="http://www.simple-talk.com/sql/database-administration/powersmo-at-work-part-2/"&gt;PowerSMO At&amp;nbsp;Work Part II&lt;/A&gt;&amp;nbsp;is up on &lt;A href="http://www.simple-talk.com"&gt;Simple-Talk.com&lt;/A&gt;&amp;nbsp;now.&lt;/P&gt;
&lt;P&gt;Dan&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/46428.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerSMO articles</title><link>http://pluralsight.com/blogs/dan/archive/2007/02/19/46147.aspx</link><pubDate>Mon, 19 Feb 2007 06:22:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2007/02/19/46147.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/46147.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2007/02/19/46147.aspx#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/46147.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/46147.aspx</trackback:ping><description>&lt;P&gt;I'm working on a series of articles on PowerSMO, my combination of PowerShell and SMO, for&amp;nbsp;&lt;A href="http://www.simple-talk.com"&gt;http://www.simple-talk.com&lt;/A&gt;. The first few are on the site now.&lt;/P&gt;
&lt;P&gt;Some of the topics in these articles are covered in the &lt;A href="www.pluralsight.com/courses/AppliedSql2005.aspx"&gt;Applied SQL Server 2005&lt;/A&gt; course.&lt;/P&gt;
&lt;P&gt;Dan&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/46147.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Applied PowerSMO! I</title><link>http://pluralsight.com/blogs/dan/archive/2006/11/08/41964.aspx</link><pubDate>Wed, 08 Nov 2006 20:33:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/11/08/41964.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41964.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/11/08/41964.aspx#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41964.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41964.aspx</trackback:ping><description>&lt;P&gt;Now that we have PowerSMO! we can start making use of it. The first example will be building a test database. Whenever I work on an new database application or write labs for a course that involves databases I need to make test database with some data in them. T-SQL is just fine for defining tables and such, but as soon as I want to fill those tables with some sample data it gets a bit tedious&amp;#8230; I just want to directly inject some C# into my T-SQL to manipulate strings, generate random data and so on. Now with PowerSMO! I can, in effect, do just that.&lt;/P&gt;
&lt;P&gt;This blog article assumes you have some familiarity with PowerSMO!, SQL Server, and PowerShell. The purpose of this blog article is to show how to make use of PowerSMO! to do some typical database operations.&lt;/P&gt;
&lt;P&gt;We start by making a database.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $server = SMO_Server
PS C:\demos&amp;gt; $testdb = SMO_Database $server "TestDB_1"
PS C:\demos&amp;gt; $testdb.DatabaseOptions.RecoveryModel="Simple"
PS C:\demos&amp;gt; $testdb.Create()
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;To start with we need a reference to an instance of SQL Server. We are using the get-SMO_Server function from PowerSMO! to get this reference. Note that short form of the function is used, dropping the &amp;#8220;get-&amp;#8221; prefix. This is a handy feature of PowerShell.&lt;/P&gt;
&lt;P&gt;&lt;CODE&gt;$testdb&lt;/CODE&gt; is the test database we are making. SMO_Database requires a reference to a server and a name. I usually have a common prefix for test database names, we will how that is useful shortly. Also I set the RecoveryMode to &amp;#8220;Simple&amp;#8221; so I don&amp;#8217;t end up with a big log on database that is really a throwaway anyhow. Lastly the $testdb doesn&amp;#8217;t really exist until Create() is called on it, that&amp;#8217;s what makes SMO issue the appropriate T-SQL commands to the server to create the database.&lt;/P&gt;
&lt;P&gt;Since I, and probably you too, have a standard way to create a test database we should we should capture our &lt;CODE&gt;ad hoc&lt;/CODE&gt; script in a function so we can reuse it. Actually we are going to make two functions here, one to create a test database name, and another to make an actual test database. We will see shortly that this will make it a lot easier to maintain things over time.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function global:get-TestDatabaseName
([string]$suffix)
{
"TestDB_{0}" -f $suffix;
}
function global:new-TestDatabase
(
[Microsoft.SqlServer.Management.Smo.Server]$server,
[string]$name_suffix)
{
$name = get-TestDatabaseName $name_suffix;
$testdb = SMO_Database $server $name;
$testdb.DatabaseOptions.RecoveryModel="Simple";
$testdb.Create();
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;get-TestDatabaseName is used to create a name for a test database. All test databases have the prefix &amp;#8220;TestDB_&amp;#8221;. The -f operator is the PowerShell formatting operator. It replaces &lt;CODE&gt;{0}&lt;/CODE&gt; with the first parameter that follows it and &lt;CODE&gt;{1}&lt;/CODE&gt; with the second and so on.&lt;/P&gt;
&lt;P&gt;The new-TestDatabase requires a Server and a string as input. The body of the function duplicates our &lt;CODE&gt;ad hoc&lt;/CODE&gt; script but uses the get-TestDatabaseName to generate the name of the database we want to add. Let&amp;#8217;s try it out&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; new-TestDatabase $server 3
PS C:\demos&amp;gt; new-TestDatabase $server 4
PS C:\demos&amp;gt; new-TestDatabase $server 5
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It&amp;#8217;s pretty easy to add a lot of databases. In fact you will probably find that often you end up with a lot of trash databases you want to get rid of and sometimes you just want to clean out your test databases and start over. Because we have a standard prefix for out test database that fairly easy to do. To make things even easier let&amp;#8217;s add a function that finds all of our test databases.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function global:get-TestDatabases
([Microsoft.SqlServer.Management.Smo.Server]$server)
{
$pat = get-TestDatabaseName "*";
$server.Databases | ?{$_.Name -like $pat}
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;CODE&gt;get-TestDatabases&lt;/CODE&gt; function makes a pattern for test database names using the get-TestDatabaseName function. It then passes the databases it finds in &lt;CODE&gt;$server&lt;/CODE&gt; through a &lt;CODE&gt;-like&lt;/CODE&gt; filter that uses this pattern to eliminate the database that are not test databases.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; get-TestDataBases $server | %{$_.name}
TestDB_1
TestDB_2
TestDB_3
TestDB_4
TestDB_5
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we can see we have made a good sized population of test databases. However it is pretty easy to get rid of all of them.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; TestDatabases $server | %{$_.Drop()}
PS C:\demos&amp;gt; TestDatabases $server | %{$_.name}
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We use the get-TestDatabases function to pipe each of the databases into a script that calls the &lt;CODE&gt;Drop()&lt;/CODE&gt; method on each one. A quick check shows we were successful. Ok, let&amp;#8217;s put our test database back into the server for the rest of the things we want to do.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; new-TestDatabase $server 1
PS C:\demos&amp;gt; $testdb = $server.Databases[(TestDatabaseName 1)]
PS C:\demos&amp;gt; $testdb.Name
TestDB_1
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;My test systems have a few Windows users, Dawn, Don, and SqlAdmin, that I use for testing. They are all ordinary Windows users with no special priveleges and the all have logins on the Sql Server instance I&amp;#8217;m going to do testing with. We can check to see if they are there easily.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $server.logins |  %{$_.name}
Ambler
AuditLogin
BUILTIN\Administrators
Frank
Joe
MyAsmLogin
NT AUTHORITY\SYSTEM
PARSEC5\Administrator
PARSEC5\Dawn
PARSEC5\Don
PARSEC5\SqlAdmin
PARSEC5\SQLServer2005MSFTEUser$PARSEC5$MSSQLSERVER
PARSEC5\SQLServer2005MSSQLUser$PARSEC5$MSSQLSERVER
PARSEC5\SQLServer2005SQLAgentUser$PARSEC5$MSSQLSERVER
sa
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If you look about half-way down the list you will see Dawn, Don and SqlAdmin. Of course we can refine this a bit more to list only the logins we are interested in.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $server.logins | 
    ?{$_.name -like "*\Dawn" -or $_.name -like "*\Don"
    -or $_.name -like "*\SqlAdmin"} | %{$_.name}
PARSEC5\Dawn
PARSEC5\Don
PARSEC5\SqlAdmin
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we use the logical -or operator and the -like pattern matching operator to filter out the logins to just the standard ones we use. In fact we should make add this to a library of functions we use when we build test databases.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function global:Get-StandardTestLogins
([Microsoft.SqlServer.Management.Smo.Server]$server)
{
$server.logins | 
    ?{$_.name -like "*\Dawn" -or 
    $_.name -like "*\Don" -or
    $_.name -like "*\SqlAdmin"}
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The Get-StandardTestLogin function is a bit different from the &lt;I&gt;ad hoc&lt;/I&gt; script we put together; It outputs a login object instead of just a name. Note that it uses a typed parameter for input, it requries a Server as input parameter. We can still use it to get the list of names though, even though it outputs login objects.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; StandardTestLogins $server | %{$_.name}
PARSEC5\Dawn
PARSEC5\Don
PARSEC5\SqlAdmin
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If it turns out our test logins are not on the system we could use PowerShell to add them. PowerShell integrates support for WMI, Window Management Instrumentation, and is a conventional way to script new users into a Windows system or enterprise. We are not going to cover those features in this blog article though.&lt;/P&gt;
&lt;P&gt;There is one last thing we have to check for out test logins. As the name implies SqlAdmin is supposed to be in the sysadmin role for SqlServer. That&amp;#8217;s straight forward to check.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $server.logins["PARSEC5\SqlAdmin"].IsMember("sysadmin")
True
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Looks like we are good to go for our test logins. &lt;/P&gt;
&lt;P&gt;Now that we know that our standard test logins are there, lets add the corresponding users to our test database.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; foreach ($login in StandardTestLogins $server)
&amp;gt;&amp;gt; {
&amp;gt;&amp;gt; $user = SMO_User $testdb $login.name
&amp;gt;&amp;gt; $user.login = $login.name
&amp;gt;&amp;gt; $user.Create()
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $testdb.users | %{$_.name}
dbo
guest
INFORMATION_SCHEMA
PARSEC5\Dawn
PARSEC5\Don
PARSEC5\SqlAdmin
sys
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We use the PowerShell foreach command to iterate through each of our test logins. We then make a new user, &lt;CODE&gt;$user&lt;/CODE&gt;, in the &lt;CODE&gt;$testdb&lt;/CODE&gt; with a name the same as the login name, which is a pretty typical way to add users to a database. Then we fill out the &lt;CODE&gt;login&lt;/CODE&gt; property of &lt;CODE&gt;$user&lt;/CODE&gt; with the corresponding login name. Last we call the &lt;CODE&gt;Create()&lt;/CODE&gt; method on &lt;CODE&gt;$user&lt;/CODE&gt;. A SMO user object, like just about all new objects in SMO, do not exist in the database until after the &lt;CODE&gt;Create()&lt;/CODE&gt; method has been called on them.&lt;/P&gt;
&lt;P&gt;We confirm that our test users were added by listing the names of the users in &lt;CODE&gt;$testdb&lt;/CODE&gt;.&lt;/P&gt;
&lt;P&gt;One of the nice things about having the Get-StandardTestLogins is that we can use it to create the users for our test databases. This allows us to to keep track of out standard test logins in one place, we don&amp;#8217;t need to constantly copy the list of them everywhere we need them. &lt;/P&gt;
&lt;P&gt;We should turn this foreach loop into a function so we can re-use for future test databases.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;function global:new-StandardTestUsers
([Microsoft.SqlServer.Management.Smo.Database]$database)
{
foreach ($login in StandardTestLogins $database.Parent)
{
$user = SMO_User $database $login.name
$user.login = $login.name
$user.Create()
}
}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here the new-StandardTestUsers functions requires that a database be passed into it. The body of the function is the same as the &lt;CODE&gt;ad hoc&lt;/CODE&gt; script we wrote except that the reference to the server is gotten from the &lt;CODE&gt;$database&lt;/CODE&gt; itself. Now, as you can see below, we just pass in a reference to our test database to the new-StandardTestUsers function to add all of our test users.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; new-StandardTestUsers $testdb
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now to finish out our test database we will add a table using SMO objects, then populate the table with some random data. The table will have an order number, a customer name and value column. The order number column will be the PRIMARY KEY.&lt;/P&gt;
&lt;P&gt;First of all we need to make a table. In case you don&amp;#8217;t remember to parameters to construct a table the get-SMO_ctors function will remind you.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; SMO_ctors (SMOT_Table)
Table()
Table(Database database, String name)
Table(Database database, String name, String schema)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We are not going to work with database schemas in this blog article, I&amp;#8217;ll save that for a later one. We&amp;#8217;ll use the second constructor.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $orders = SMO_Table $testdb "Orders"
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now that we have a table we need to create the columns for it. Again get-SMO&lt;EM&gt;ctors can be used to find out what parameters we need to pass to the get-SMO&lt;/EM&gt;Column function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; SMO_ctors (SMOT_column)
Column()
Column(SqlSmoObject parent, String name)
Column(SqlSmoObject parent, String name, DataType dataType)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can create a column, specify it name and type in one operation. We will need to make a DataType, so let&amp;#8217;s check what the constructor options are for it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; smo_ctors (SMOT_DataType)
DataType()
DataType(SqlDataType sqlDataType)
DataType(SqlDataType sqlDataType, Int32 precisionOrMaxLength)
DataType(SqlDataType sqlDataType, Int32 precision, Int32 scale)
DataType(SqlDataType sqlDataType, String type)
DataType(SqlDataType sqlDataType, String type, String schema)
DataType(XmlSchemaCollection xmlSchemaCollection)
DataType(UserDefinedDataType userDefinedDataType)
DataType(UserDefinedType userDefinedType)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This, in turn requires us to make a SqlDataType, which is an enum. We can look to see what the possible enumerated values are for this too.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; SMO_enum (SMOT_SqlDataType)
value__
None
BigInt
Binary
Bit
Char
DateTime
Decimal
Float
Image
Int
Money
NChar
NText
NVarChar
NVarCharMax
Real
SmallDateTime
SmallInt
SmallMoney
Text
Timestamp
TinyInt
UniqueIdentifier
UserDefinedDataType
UserDefinedType
VarBinary
VarBinaryMax
VarChar
VarCharMax
Variant
Xml
SysName
Numeric
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;No big suprise here, it has all the SQL datatypes were are use to using in SQL Server. Now we can make some columns and add them to our &lt;CODE&gt;$orders&lt;/CODE&gt; table.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $orders = SMO_Table $testdb "Orders"
PS C:\demos&amp;gt; $order_number = SMO_Column $orders "Order Number"      
    (SMO_DataType "Int")
PS C:\demos&amp;gt; $order_number = SMO_Column $orders "Order Number" 
    (SMO_DataType "Int")
PS C:\demos&amp;gt; $orders.Columns.Add($order_number)
PS C:\demos&amp;gt; $orders.Columns.Add($customer_name)
PS C:\demos&amp;gt; $value = SMO_Column $orders "Value"
    (SMO_DataType "Money")
PS C:\demos&amp;gt; $orders.Columns.Add($value)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;First we create a table named &amp;#8220;Orders&amp;#8221;. A reference to the table is in variable &lt;CODE&gt;$orders&lt;/CODE&gt;. Each column is made using the SMO&lt;EM&gt;Column function. Note that the datatype for the column is defined using the SMO&lt;/EM&gt;DataType function.&lt;/P&gt;
&lt;P&gt;Now that we have our table we need to make the $order_number column a primary key.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $pk = SMO_Index $orders "Orders__PK"
PS C:\demos&amp;gt; smo_enum (SMOT_IndexKeyType)
value__
None
DriPrimaryKey
DriUniqueKey
PS C:\demos&amp;gt; $pk.IndexKeyType=(SMOT_IndexKeyType)::DriPrimaryKey
PS C:\demos&amp;gt; $inx_col = SMO_IndexedColumn $pk "Order Number"
PS C:\demos&amp;gt; $pk.IndexedColumns.Add($inx_col)
PS C:\demos&amp;gt; $orders.Indexes.Add($pk)
PS C:\demos&amp;gt; $orders.Create()
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The index takes a little more work. First we make an SMO&lt;EM&gt;Index.and name it &amp;#8220;Orders&lt;/EM&gt;PK&amp;#8221;. An index might include more than one column and those columns are specified in SMO&lt;EM&gt;IndexedColumn objects. We need to make an SMO&lt;/EM&gt;Indexed column for each column in the index. This index is only a single column, however, so we only need to create a single SMO&lt;EM&gt;IndexedColumn. The SMO&lt;/EM&gt;IndexedColumn is added to the index, &lt;CODE&gt;$pk'. Once we have added the SMO_IndexedColumn to the SMO_Index we can add the index to the&lt;/CODE&gt;$orders` table.&lt;/P&gt;
&lt;P&gt;Lastly we call the &lt;CODE&gt;Create()&lt;/CODE&gt; method on &lt;CODE&gt;$orders&lt;/CODE&gt; to make the table on the server.&lt;/P&gt;
&lt;P&gt;Now we can add some data to the table. We can make use of the System.Random class from the .NET framework to generate some of the data. There aren&amp;#8217;t any &amp;#8220;row&amp;#8221; objects for tables in SMO. We just use standard T-SQL to do inserts to add data to a table.&lt;/P&gt;
&lt;P&gt;To fill out our table we need to produce some triples that consist of an order number, customer name, and a value. We have 30 customers with 1000 orders whose value ranges from 1 to 1000.00. With PowerShell we can use a pipeline to generate triples. Here is a simple example that generates 10 random triples.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; (1..10) | %{"{0} : {1} : {2}" -f $_, 
    $rand.next(1,10), $rand.next()}
1 : 5 : 2119806400
2 : 6 : 1944455664
3 : 7 : 323953648
4 : 9 : 1680451911
5 : 4 : 820794382
6 : 4 : 1793724046
7 : 8 : 1206229307
8 : 5 : 1466323995
9 : 6 : 1123175179
10 : 1 : 1559943559
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The first part of the pipeline is an array, initialized with values from 1 to 10. The second part of the pipeline formats the numbers from the array along with some random numbers. Now we will use this technique generate the T-SQL insert statments. To get started let&amp;#8217;s make a helper function that will create the insert statement from three input parameters.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; function get-insert
&amp;gt;&amp;gt; ($number, $customer, $value)
&amp;gt;&amp;gt; {
&amp;gt;&amp;gt; "INSERT INTO Orders Values ({0}, '{1}', {2})" -f
&amp;gt;&amp;gt; $number, $customer, $value
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; insert 1 "joe" 102.32
INSERT INTO Orders Values (1, 'joe', 102.32)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The get-insert function uses the -f operator to build an insert statement from the three parameters passed into it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; (1..10) |
    %{insert $_ ("Name_" + $rand.next(1,30)) 
    ($rand.next(1,1000000)/100.1)}
INSERT INTO Orders Values (1, 'Name_15', 1127.23276723277)
INSERT INTO Orders Values (2, 'Name_12', 9753.04695304695)
INSERT INTO Orders Values (3, 'Name_23', 9560.27972027972)
INSERT INTO Orders Values (4, 'Name_15', 2843.98601398601)
INSERT INTO Orders Values (5, 'Name_13', 2798.22177822178)
INSERT INTO Orders Values (6, 'Name_13', 4531.40859140859)
INSERT INTO Orders Values (7, 'Name_11', 4825.18481518482)
INSERT INTO Orders Values (8, 'Name_26', 8539.9000999001)
INSERT INTO Orders Values (9, 'Name_22', 9863.88611388611)
INSERT INTO Orders Values (10, 'Name_10', 4556.79320679321)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have used the get-insert function generate ten insert&amp;#8217;s. Next we need a way to execute those insert statements. A SMO database object has a number methods that can execute a SQL statement, much like a SqlCommand does in ADO.NET. In this case we will use the ExecuteNonQuery method of &lt;CODE&gt;$database&lt;/CODE&gt; to insert 1000 orders.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; (1..1000) | 
    %{insert $_ ("Name_" + $rand.next(1,30))
    ($rand.next(1,1000000)/100.1)} | 
    %{$testdb.ExecuteNonQuery($_)}
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We put an array with a thousand numbers into a pipeline, each number is used as an order number. The customer name is just the string &amp;#8220;name_&amp;#8221; with a suffix that is a random number from 1 to 30. And then for the value we generate a random number. These constructed values are passed into the get-insert function to make an insert statement. The insert statement is then used a the parameter to the ExectureNonQuery method on &lt;CODE&gt;$testdb&lt;/CODE&gt;.&lt;/P&gt;
&lt;P&gt;We can see the row count of the table is now 1000.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $orders.refresh()
PS C:\demos&amp;gt; $orders.rowcount
1000
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that before we check the rowcount we refresh the &lt;CODE&gt;$orders&lt;/CODE&gt; table.&lt;/P&gt;
&lt;P&gt;To finish up we will make use of the fact that can also execute a select statement against the Orders database.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset = $testdb.ExecuteWithResults(
    "SELECT * FROM Orders")
PS C:\demos&amp;gt; $table = $dataset.tables[0]
PS C:\demos&amp;gt; $table.Rows | format-table -autosize
Order Number Customer Name     Value
------------ -------------     -----
           1 Name_29       1837.6124
           2 Name_27       3723.3666
           3 Name_1        3221.3886
...
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The ExecuteWithResults method of &lt;CODE&gt;$database&lt;/CODE&gt; returns an ADO Dataset. We can extract the rows from the first table in the Dataset and pipe them into a PowerShell format-table command to see what they contain.&lt;/P&gt;
&lt;P&gt;We have seen that we can use PowerSMO! to create a test database, add tables to it and then fill these tables with some random data. Along the way we have seen how to use PowerSMO! to do a number of typical database operation like checking out what logins exist or adding users to database.&lt;/P&gt;
&lt;P&gt;Well that&amp;#8217;s it for this article, there will be more later.&lt;/P&gt;
&lt;P&gt;Dan &lt;A href="mailto:dan@pluralsight.com"&gt;dan@pluralsight.com&lt;/A&gt;&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41964.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerSMO!</title><link>http://pluralsight.com/blogs/dan/archive/2006/11/07/41936.aspx</link><pubDate>Tue, 07 Nov 2006 13:55:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/11/07/41936.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41936.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/11/07/41936.aspx#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41936.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41936.aspx</trackback:ping><description>&lt;P&gt;Last year I wrote a blog article about using what was then called MSH with SQL Server Management Objects &lt;A href="http://pluralsight.com/blogs/dan/archive/2005/12/29/17703.aspx"&gt;http://pluralsight.com/blogs/dan/archive/2005/12/29/17703.aspx&lt;/A&gt;. MSH is now called PowerShell and mixing some SMO with it makes PowerSMO!&lt;/P&gt;
&lt;P&gt;SMO is a set of object models for SQL Server. With PowerSMO! you can manipulate those object models from the command line or with a script. The two object models probably of most interest to SQL Server developers and DBA&amp;#8217;s are the Server and ManagedComputer object models and that&amp;#8217;s what this article is going to use them to show how to use PowerSMO!.&lt;/P&gt;
&lt;P&gt;This article assumes you are reasonably familiar with SQL Server and have some familiarity with PowerShell. You can look at some of the previous articles I have blogged about PowerShell to get familiar with PowerShell. &lt;/P&gt;
&lt;P&gt;There are a number of reasons for using PowerSMO! You can script the management, testing, status, &lt;I&gt;etc.&lt;/I&gt; of database objects. PowerSMO! also has pedagogical value too. I&amp;#8217;ve been using PowerShell + SMO in the classes I teach because it provides such a quick way too cobble up &lt;I&gt;ad hoc&lt;/I&gt; examples. It&amp;#8217;s useful in development for the same reason, you can quickly try something out before you commit the time to edit/compile/test a C# program.&lt;/P&gt;
&lt;P&gt;To use PowerSMO! you need to start up PowerShell then run the InitPowerSMO.ps1 script. This script is available at &lt;A href="http://www.pluralsight.com/dan/samples/InitPowerSMO.zip"&gt;http://www.pluralsight.com/dan/samples/InitPowerSMO.zip&lt;/A&gt;.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;Windows PowerShell
Copyright (C) 2006 Microsoft Corporation. All rights reserved.
PS C:\demos&amp;gt; . "C:\demos\InitPowerSMO.ps1"
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This script loads the SMO assemblies into PowerShell and a number of other things that we will look at later.&lt;/P&gt;
&lt;P&gt;Let&amp;#8217;s start with some basics. The &lt;CODE&gt;new-object&lt;/CODE&gt; cmd-let is used to make an instance of a .NET class. SMO has a class named &lt;CODE&gt;ManagedComputer&lt;/CODE&gt;. An instance of &lt;CODE&gt;ManagedComputer&lt;/CODE&gt; represents the root of one of the object models in SMO, the one that is used to manage the services provided by SQL Server, such as the database engine or full-text search. BTW, watch out for the line-wraps in the examples that follow.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $mc = new-object "Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer"
PS C:\demos&amp;gt; $mc.services | %{$_.name}
MSFTESQL
MSSQLSERVER
SQLBrowser
SQLSERVERAGENT
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we used an instance of the &lt;CODE&gt;ManagedComputer&lt;/CODE&gt; class find the SQL Server services on the local computer. We piped these services to a script that prints out the name of the service. Looks like a pretty typical SQL Server installation.&lt;/P&gt;
&lt;P&gt;This is handy but, as you can see, it is a bit tedious dealing with the full name of the class that &lt;CODE&gt;new-object&lt;/CODE&gt; requires. PowerShell deals with this issue in a number of places by using what it calls a &amp;#8220;type accelerator&amp;#8221; or &amp;#8220;type alias&amp;#8221;. &lt;/P&gt;
&lt;P&gt;For example &lt;CODE&gt;[wmi]&lt;/CODE&gt; is a type accelerator for &lt;CODE&gt;System.Management.ManagementObject&lt;/CODE&gt; and you can use it instead of the fully spelled out class name when you need to specify that type. Unfortunately there is no provision in PowerShell for creating your own type aliases. PowerShell supports user defined aliases for cmdlets, functions, &lt;I&gt;etc.&lt;/I&gt; but these are not useful as type accelerators.&lt;/P&gt;
&lt;P&gt;To save on typing PowerSMO! creates helper functions that can be used to create instances of the classes used by SMO. One of the reasons you see a delay when you run the &lt;CODE&gt;initPowerSMO.ps1&lt;/CODE&gt; script is that it has to create all these functions. Below is a script that uses one of these functions and duplicates the operation of the previous example.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $mc = Get-SMO_ManagedComputer
PS C:\demos&amp;gt; $mc.Services | %{$_.name}
MSFTESQL
MSSQLSERVER
SQLBrowser
SQLSERVERAGENT
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;All of these helper functions have a preface of &amp;#8220;Get-SMO_&amp;#8221; followed by the name of the class that they create.&lt;/P&gt;
&lt;P&gt;All that the &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; helper functions and new-object are doing is running the constructor for the class requested. In the previous two samples we used the parameter-less constructor to make a &lt;CODE&gt;ManagedComputer&lt;/CODE&gt; object. When you do this the managed computer is the one PowerShell is running on.&lt;/P&gt;
&lt;P&gt;One of the nice things about PowerShell is that you can always ask what kinds of things an object can do by piping it to the &lt;CODE&gt;get-member&lt;/CODE&gt; cmdlet. For example:&lt;/P&gt;
&lt;P&gt;PS C:\demos&amp;gt; $mc | get-member&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;   TypeName: Microsoft.SqlServer.Management.Smo.Wmi.ManagedComputer
Name                   MemberType Definition
----                   ---------- ----------
Equals                 Method     System.Boolean Equals(Object obj)
GetHashCode            Method     System.Int32 GetHashCode()
GetSmoObject           Method     Microsoft.SqlServer.Management.Smo.Wmi.Wmi...
...
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;PowerShell does not have the same level of builtin &amp;#8220;What can you do for me?&amp;#8221; capability for the constructors of .NET classes. .NET classes often have a number of constructors to choose from and it is often hard to remember them all. PowerSMO! has some helper functions to make it easier to see what constructors there are for a class. &lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; Get-SMO_ctors (Get-SMOT_ManagedComputer)
ManagedComputer()
ManagedComputer(String machineName)
ManagedComputer(String machineName, String userName, String password)
ManagedComputer(String machineName, String userName, String password, 
ProviderArchitecture providerArchitecture)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function lists the constructors for the type passed, in parentheses to it. For every &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; function there is a &lt;CODE&gt;Get-SMOT_&lt;/CODE&gt; that returns its type definition. The &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function requires a type definition, not the function that makes an instance of the type. Actually the &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function will work for any .NET type definition.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; Get-SMO_ctors ([System.String])
String(Char* value)
String(Char* value, Int32 startIndex, Int32 length)
String(SByte* value)
String(SByte* value, Int32 startIndex, Int32 length)
String(SByte* value, Int32 startIndex, Int32 length, Encoding enc)
String(Char[] value, Int32 startIndex, Int32 length)
String(Char[] value)
String(Char c, Int32 count)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The example above shows using the &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function to find the various constructors for System.String class from .NET.&lt;/P&gt;
&lt;P&gt;All of the &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; function will accept parameters for a constructor. You pass the parameters on the command line as you would for any other PowerShell function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $mc = Get-SMO_ManagedComputer "PARSEC5"
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;In the example above &lt;CODE&gt;$mc&lt;/CODE&gt; is attached to a computer name &amp;#8220;PARSEC5&amp;#8221;. &lt;/P&gt;
&lt;P&gt;Some constructors you will want to use require multiple parameters. The ServerConnection class serves a purpose similar to that for a SqlConnection in ADO.NET&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; Get-SMO_ctors (Get-SMOT_ServerConnection)
ServerConnection(SqlConnectionInfo sci)
ServerConnection(SqlConnection sqlConnection)
ServerConnection()
ServerConnection(String serverInstance)
ServerConnection(String serverInstance, String userName, String password)
ServerConnection(String serverInstance, String userName, SecureString password)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here you can see that there is a constructor that lets you can create a &lt;CODE&gt;ServerConnection&lt;/CODE&gt; using a SQL login. It needs the name and password for that login.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $sc = Get-SMO_ServerConnection "PARSEC5" "ambler" "Ambler"
PS C:\demos&amp;gt; $sc.ConnectionString
server='PARSEC5';uid='ambler';password='Ambler';
multipleactiveresultset =false
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;As you can see the ServerConnection just uses the user name and password to build the connection string it will need to connect to SQL Server.&lt;/P&gt;
&lt;P&gt;A note about the &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; function that is a bit different than using the &lt;CODE&gt;new-object&lt;/CODE&gt; cmdlet. The constructor parameters for the &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; functions are separated by spaces, unlike passing constructor parameters to the &lt;CODE&gt;new-object&lt;/CODE&gt; cmdlet which requires them to be separted by commas.&lt;/P&gt;
&lt;P&gt;Ok, let&amp;#8217;s use PowerSMO! to create a database. To do this we will need to make use of two classes of SMO object, a Server and a Database. We will start by looking at the constructors for each.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; Get-SMO_ctors (Get-SMOT_Server)
Server(ServerConnection serverConnection)
Server(String name)
Server()
PS C:\demos&amp;gt; Get-SMO_ctors(Get-SMOT_Database)
Database()
Database(Server server, String name)
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We see that to construct a Server can pass in the name of the instance of SQL Server we want to use, and for the Database we will have to pass in a reference to the Server and a the name we want for the new database.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $s = Get-SMO_Server "PARSEC5"
PS C:\demos&amp;gt; $db = Get-SMO_Database $s "DBDemoSample"
PS C:\demos&amp;gt; $db.create()
PS C:\demos&amp;gt; $s.Databases["DBDemoSample"].Name
DBDemoSample
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have made the Server object for the default SQL Server instance on machine &amp;#8220;PARSEC5&amp;#8221;. Next we make the Database object by passing the variable that holds the Server object into the database constructor. Just to confirm it actually worked we look at the databases collection in the server and see if we can find a database named &amp;#8220;DBDemoSample&amp;#8221; and then print out its name. If there was no database named &amp;#8220;DBDemoSample&amp;#8221; this line would not have printed out anything.&lt;/P&gt;
&lt;P&gt;Now let&amp;#8217;s do a little database management. Typically when a database is created its recovery model is &amp;#8220;Full&amp;#8221; This is going to be a development database so we want to change its recovery mode to &amp;#8220;Simple&amp;#8221;. We will start by confirming our suspicions about its recovery mode.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $db = $s.DataBases["DBDemoSample"]
PS C:\demos&amp;gt; $db.DatabaseOptions.RecoveryModel
Full
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We use &lt;CODE&gt;$s&lt;/CODE&gt; to get a reference to the &amp;#8220;DBDemoSample&amp;#8221; database. From &lt;CODE&gt;$db&lt;/CODE&gt; we can look at the recovery model in database options and see that, in fact, it is &amp;#8220;Full&amp;#8221;.&lt;/P&gt;
&lt;P&gt;The value of &lt;CODE&gt;RecoverModel&lt;/CODE&gt; is one of the enumerated values from the Microsoft.SqlServer.Management.Smo.RecoveryModel enum. You can always find the possible values of an enum by making use of .NET reflection in PowerShell.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [Microsoft.SqlServer.Management.Smo.RecoveryModel].GetFields() | %{
$_.Name}
value__
Simple
BulkLogged
Full
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Again, similar to using new-object, you must type out the entire class name to find the enumerated values of an enum. PowerSMO! provides another helper function for finding the enumerated values.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; Get-SMO_enum (Get-SMOT_RecoveryModel)
value__
Simple
BulkLogged
Full
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;CODE&gt;Get-SMO_emum&lt;/CODE&gt; function finds the enumerated values of an enum. It is similar to the &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function in that it requires a class definition, not the function that makes an instance. In this example we use the &lt;CODE&gt;Get-SMOT_RecoveryModel&lt;/CODE&gt; to get the class definition.&lt;/P&gt;
&lt;P&gt;Using an enumerated value once you know what it is, is fairly easy. You can just set it as though is were a string value and PowerShell will convert it to the appropriate enumeration.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $db.DatabaseOptions.RecoveryModel="Simple"
PS C:\demos&amp;gt; $db.Alter()
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Setting a value in a field of a SMO object does not affect the database behind it until you call the &lt;CODE&gt;Alter()&lt;/CODE&gt; function. After the &lt;CODE&gt;Alter()&lt;/CODE&gt; function completes the database has the new recovery mode. &lt;/P&gt;
&lt;P&gt;Note you need not worry about setting an improper enumeration value, PowerShell will check that for you, as you can see below.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $db.DatabaseOptions.RecoveryModel = "Fullx"
Exception setting "RecoveryModel": "Cannot convert value "Fullx" to type "Micro
soft.SqlServer.Management.Smo.RecoveryModel" due to invalid enumeration values.
 Specify one of the following enumeration values and try again. The possible en
umeration values are "Full, BulkLogged, Simple"."
At line:1 char:21
+ $db.DatabaseOptions.R &amp;lt;&amp;lt;&amp;lt;&amp;lt; ecoveryModel = "Fullx"
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now you have seen PowerSMO!. It consists of functions, prefaced by &lt;CODE&gt;Get-SMO_&lt;/CODE&gt; that you can use to create instances of SMO objects. It also includes &lt;CODE&gt;Get-SMOT_&lt;/CODE&gt; functions that you can use to retrieve the type definition for a particular kind of SMO object. The &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; function gives you a list of the constructors for a class and the &lt;CODE&gt;Get-SMO_enum&lt;/CODE&gt; function give you a list of the values for an enum.&lt;/P&gt;
&lt;P&gt;If you take a peek inside of the initPowerSMO.ps1 file, except for the &lt;CODE&gt;Get-SMO_enum&lt;/CODE&gt; and &lt;CODE&gt;Get-SMO_ctors&lt;/CODE&gt; functions, you won&amp;#8217;t see any of the functions we have used in this article. That&amp;#8217;s because the initPowerSMO.ps1 script generates these functions from the assemblies that makeup SMO. How it does this will be the topic of a later blog article.&lt;/P&gt;
&lt;P&gt;Dan &lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41936.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerShell and ADO.NET</title><link>http://pluralsight.com/blogs/dan/archive/2006/10/29/41389.aspx</link><pubDate>Sun, 29 Oct 2006 14:48:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/10/29/41389.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41389.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/10/29/41389.aspx#Feedback</comments><slash:comments>6</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41389.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41389.aspx</trackback:ping><description>&lt;P&gt;Lots of times when you are working with a SQL Server all you want to do is to poke at the data. You aren&amp;#8217;t doing any real heavy duty transactional processing, you just want to get a look at what is going on. Of course you can fire up SSMS (SQL Server Management Studio) or SqlCmd and issue some &lt;I&gt;ad hoc&lt;/I&gt; queries. Or you can copy all the tables you are interested in to a local instance of SQL Server on your system and do the poking there. Doing the latter is often times a lot more convienient but you need a copy of SQL Server locally and then you have to copy all the data, yadda, yadda, yadda.&lt;/P&gt;
&lt;P&gt;Actually it&amp;#8217;s pretty common to take the &amp;#8220;work on the data locally&amp;#8221; approach in C# applications. These applications use ADO.NET to build a DataSet in the application, then data bind, or add a little procedural programming or otherwise poke at it. But writing, compiling and running an &lt;I&gt;ad hoc&lt;/I&gt; application each time you want to make a little change isn&amp;#8217;t really practical. What does make this technique practical though is &lt;B&gt;PowerShell&lt;/B&gt;.&lt;/P&gt;
&lt;P&gt;In this article we&amp;#8217;re going to look at the basics of using ADO.NET inside of PowerShell. This article assumes you know the basics of using ADO.NET in C#.&lt;/P&gt;
&lt;P&gt;We will start with the DataSet. A DataSet is just a fancy container, just a bit more fancy than a hash table. It has tables and views and relations and queries sort of like a database does. You can use a DataSet without a database, but if you are going to use it with a database then the SqlDataAdapter is a handy way to make a snapshot of some data and save it into a dataset. Let&amp;#8217;s start by using PowerShell to get a snapshot of the data in the Customers table of the Northwind database.&lt;/P&gt;
&lt;P&gt;First of all we will need a query that returns all the data from the Customers table.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;$query = "SELECT * FROM [Customers]"
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We will also need a connection string. This one assumes a local SQL Server.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;$connString = "server=.;integrated security;database=northwind"
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we will need a DataSet to copy the customers data into. PowerShell does not initialize a variable just because it is declared so we will have to make an instance of the DataSet class.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;$dataset = new-object "System.Data.DataSet" "MyDataSet"
PS C:\demos&amp;gt; $dataset
RemotingFormat          : Xml
SchemaSerializationMode : IncludeSchema
CaseSensitive           : False
DefaultViewManager      : {System.Data.DataViewManagerListItemTypeDescriptor}
EnforceConstraints      : True
DataSetName             : MyDataSet
Namespace               :
Prefix                  :
ExtendedProperties      : {}
HasErrors               : False
IsInitialized           : True
Locale                  : en-US
Site                    :
Relations               : {}
Tables                  : {}
Container               :
DesignMode              : False
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We use the new-object command to make an instance of a DataSet. The second string passed to new-object is the name we want for the DataSet. Typing the name of a variable on the command line in PowerShell returns the properties of that object, in most cases. Here we see that the dataset has a DataSetName of &amp;#8220;MyDataSet&amp;#8221; as we wanted and that the Tables collection is empty.&lt;/P&gt;
&lt;P&gt;Next we need a SqlDataAdapter so we can fill our dataset.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $da = new-object "System.Data.SqlClient.SqlDataAdapter" ($query, $connString)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have used the new-object command again and passed into the SqlDataAdapter constructor both the query and the connection string. Note that when want to pass more than one parameter to a constructor for a .NET class in PowerShell, you must pass all the parameters in a single array of objects. That is why &lt;CODE&gt;$query&lt;/CODE&gt; and &lt;CODE&gt;$connString&lt;/CODE&gt; have been passed separated by commas and enclosed in parentheses. A comma separted list enclosed in parentheses in PowerShell creates an array of objects.&lt;/P&gt;
&lt;P&gt;We can check to see that our DataAdapter was properly constructed by looking at the text for the SelectCommand and the ConnectionString for the connection the SelectCommand uses.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $da.SelectCommand.CommandText 
SELECT * FROM [Customers]
PS C:\demos&amp;gt; $da.SelectCommand.Connection.ConnectionString
server=.;integrated security=true;database=northwind
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we have everything we need to get our snapshot.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $da.Fill($dataset)
92
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;All we did was to call the Fill method on &lt;CODE&gt;$da&lt;/CODE&gt; and pass in our &lt;CODE&gt;$dataset&lt;/CODE&gt; variable. Once we have done this we can take a look at what is changed about our dataset.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset

RemotingFormat          : Xml
SchemaSerializationMode : IncludeSchema
CaseSensitive           : False
DefaultViewManager      : {System.Data.DataViewManagerListItemTypeDescriptor}
EnforceConstraints      : True
DataSetName             : MyDataSet
Namespace               :
Prefix                  :
ExtendedProperties      : {}
HasErrors               : False
IsInitialized           : True
Locale                  : en-US
Site                    :
Relations               : {}
Tables                  : {}
Container               :
DesignMode              : False
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Nothing has really changed. Notice that the Table container is still empty. Don&amp;#8217;t worry about this, is just an artifact of how PowerShell treats DataTables. If we ask how many DataTables are in &lt;CODE&gt;$dataset&lt;/CODE&gt; we will see it contains one, as we would expect.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables.Count
1
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It would be nice of the Tables container listed the names of the tables in the DataSet, but it doesn&amp;#8217;t so we will have to write a little script to see what is there.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables | %{$_.TableName}
Table
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The Tables property of the &lt;CODE&gt;$dataset&lt;/CODE&gt; is a container, so if we pass it into a pipe it sends each of its tables down the pipe one at a time. The script inside of %{} is executed once for each item that passes through the pipe and $_ is the item being passed. &lt;CODE&gt;$_.TableName&lt;/CODE&gt; gets the name of each table that passes through the pipe. Of course there is only one in this case.&lt;/P&gt;
&lt;P&gt;As expected, the first table that a DataAdapter loads into a DataSet is named &amp;#8220;Table&amp;#8221;. The Tables collection acts like an array so we can access the individual tables in the Tables collection and set their names to whatever we want.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables[0].TableName = "Customers"
PS C:\demos&amp;gt; $dataset.Tables | %{$_.TableName}
Customers
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If we want to make it a bit easier to work with the Customers table we can pull out a reference to if from the DataSet.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $customers = $dataset.Tables["Customers"]
PS C:\demos&amp;gt; $customers.Rows.Count
92
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that just as we would do in a C# program we can index items in the Tables collection by their name. Once we have the &lt;CODE&gt;$customers&lt;/CODE&gt; reference to the Customers table we can begin to work on it. We tried about the most simple thing you can do with a DataTable, we looked at how many rows there are in it.&lt;/P&gt;
&lt;P&gt;We can look to see what kinds of columns are in the table.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $customers.Columns | Format-Table -autosize -property ColumnName, DataType
ColumnName   DataType
----------   --------
CustomerID   System.String
CompanyName  System.String
ContactName  System.String
ContactTitle System.String
Address      System.String
City         System.String
Region       System.String
PostalCode   System.String
Country      System.String
Phone        System.String
Fax          System.String
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We pipe the Columns from the &lt;CODE&gt;$customers&lt;/CODE&gt; variable into the pipe. Here we use the format-table command from PowerShell and specify that we only want to see the ColumnName and DataType properties of each column. The &lt;CODE&gt;-autosize&lt;/CODE&gt; tells PowerShell squeeze the columns as tightly together as possible.&lt;/P&gt;
&lt;P&gt;Lets try using PowerShell to process the data. Will start by doing something a little silly, we want the ContactNames from all of the customers whose CompanyName has at least 30 characters in it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $customers | ?{$_["CompanyName"].Length -ge 30} | %{$_["ContactName"]}
Ana Trujillo
Diego Roel
Lino Rodriguez
Helvetius Nagy
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;When you pass a DataTable into a pipe it passes each of the DataTable&amp;#8217;s rows in one at a time. The script inside of ?{} is a test, if the calculation done inside of the ?{} is true, then the object that was passed into this segment of the pipe is passed to the next. So ?{} acts as a filter and it is testing each row which is represented by &lt;CODE&gt;$_&lt;/CODE&gt;. The last segment of the pipe just returns the ContactName from the rows that made it through the filter.&lt;/P&gt;
&lt;P&gt;Notice that we accessed the columns of the row by using an indexer and the name of the column we wanted. That is way you access individual columns of a row when you write a C# program. However PowerShell gives us an alternate way to access a column, it add properties to the row whose names are the names of the columns. The value of each one of these column properties is the value of the column it represents. The following script does the same thing as the previous but uses the column properties.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $customers | ?{$_.CompanyName.Length -ge 30} | %{$_.ContactName}
Ana Trujillo
Diego Roel
Lino Rodriguez
Helvetius Nagy
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;You might find this more convient. There are a couple of gotcha&amp;#8217;s here though. One thing to keep in mind is that the scripting language in PowerShell is very simlar to C# on purpose. It&amp;#8217;s meant to make it straightforward to move back and forth from using PowerShell to C# without constantly have to switch your mental typing context. Another reason for this similarity is that it makes it a reasonably straightforward mechanical transformation to move code from C# to PowerShell and &lt;I&gt;vica versa&lt;/I&gt;. So if you use this shortcut you may end up unconciously typing errors in you C# code when you are working with ADO.NET&amp;#8230; pays your money and takes your choice.&lt;/P&gt;
&lt;P&gt;One more gotcha with this feature is that sometimes column names in a database table have spaces in them and these have to be handled a bit differently when you use this PowerShell property short cut. Here is a simple table definition for a table in the Scratch database.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;Create table TestPSData
(
[my id] INT
)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that is has a single column named &amp;#8220;my id&amp;#8221;, which has a space in it. We can quickly fix up our data adapter to access this table and add it to our dataset.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $da.SelectCommand.CommandText="SELECT * FROM Scratch..TestPSData"
PS C:\demos&amp;gt; $da.Fill($dataset)
2
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can print out the ID&amp;#8217;s in the TestPSData table using the ADO.NET indexer method.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables[1] | %{$_["my id"]}
1
1
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;As you can see it printed out the &amp;#8220;my id&amp;#8221; column value for the two rows in the table.&lt;/P&gt;
&lt;P&gt;If we try to use the PowerShell column property method we may get an error&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables[1] | %{$_.my id}
Unexpected token 'id' in expression or statement.
At line:1 char:32
+ $dataset.Tables[1] | %{$_.my id} &amp;lt;&amp;lt;&amp;lt;&amp;lt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;because the space in the name makes PowerShell read an extra value that it doesn&amp;#8217;t know how to handle. But PowerShell is a scriping language so this is easy to fix.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $dataset.Tables[1] | %{$_."my id"}
1
1
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Just put some quotes around the property name so PowerShell treats it as a single word.&lt;/P&gt;
&lt;P&gt;Columns names in SQL Server my also begin with a &amp;#8220;$&amp;#8221; character and this requires a bit more effort to handle, even when you are using the ADO indexer to get the column. Here is another table definition&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;Create table TestPSData2
(
[$myid] INT
)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Assuming we have filled out dataset up with this table instead of the previous TestPSData table then an attempt to access the $myid column can result in an error in a number of ways.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $ds2.Tables[0] | %{$_."$myid"}
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that this did not produce any results but it should have produced two rows. That is becuase $myid is interpreted by PowerShell as a variable which has no value, so it is the same as using [&amp;#8220;&amp;#8221;]&lt;/P&gt;
&lt;P&gt;If we try to use indexer then we get a hard error.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $ds2.Tables[0] | %{$_["$myid"]}
Unable to index into an object of type System.Data.DataRow.
At line:1 char:23
+ $ds2.Tables[0] | %{$_[" &amp;lt;&amp;lt;&amp;lt;&amp;lt; $myid"]}
Unable to index into an object of type System.Data.DataRow.
At line:1 char:23
+ $ds2.Tables[0] | %{$_[" &amp;lt;&amp;lt;&amp;lt;&amp;lt; $myid"]}
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;That is because there is no column named &amp;#8220;&amp;#8221;.&lt;/P&gt;
&lt;P&gt;The way to handle this problem in an both cases is to escape the $ with a backtick and enclose the whole thing in quotes.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $ds2.Tables[0] | %{$_["`$myid"]}
1
2
PS C:\demos&amp;gt; $ds2.Tables[0] | %{$_."`$myid"}
1
2
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;OK, enough property name trivia for now.&lt;/P&gt;
&lt;P&gt;DataTable supports a SQL-like syntax for queries. We can use that feature from PowerShell to redo are &amp;#8220;long company name&amp;#8221; query from before.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $customers.Select("len(CompanyName) &amp;gt; 30") | %{$_["ContactName"]}
Ana Trujillo
Diego Roel
Helvetius Nagy
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The &lt;CODE&gt;Select&lt;/CODE&gt; method of a Table returns a rowset. When you put a rowset into a pipe, each row is passed down the pipe as it is for a Table. In this example we, as we did before, pull the ContactNames from the rows that made it through the ADO Select filter.&lt;/P&gt;
&lt;P&gt;We can use a rowset in PowerShell the same way we would in any other language that supports ADO.NET. For example we can use it to fill a DataTable.&lt;/P&gt;
&lt;P&gt;Let&amp;#8217;s start by building a DataTable from scratch.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $datatable = new-object "System.Data.DataTable"
PS C:\demos&amp;gt; $datatable
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It looks like we didn&amp;#8217;t build a DataTable, but it just that PowerShell is giving us the rows of the table instead its properties and there are not yet any rows in this datatable. Let&amp;#8217;s proceed as though there is a table and make a column for it. In fact lets make this datatable the same a the TestPSData table we used previously.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $column = new-object "System.Data.DataColumn" ("my id", [Int32])
PS C:\demos&amp;gt; $datatable.Columns.Add($column)
PS C:\demos&amp;gt; $datatable.Columns | format-table -autosize -property ColumnName, DataType
ColumnName DataType
---------- --------
my id      System.Int32
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We started by making a DataColumn whose name is &amp;#8220;my id&amp;#8221; with datatype Int32. When you need to specify a type in PowerShell [Int32] is the equivalent of typeof(Int32) in C#.&lt;/P&gt;
&lt;P&gt;We added the column to the &lt;CODE&gt;$datatable&lt;/CODE&gt; columns collection then, as we did previously for the Customers table, check to see what columns the table has. Next let&amp;#8217;s fill this datatable with the results captured in a DataReader.&lt;/P&gt;
&lt;P&gt;The typical way we get a DataReader is to use ExecuteReader method on a SqlCommand. So lets build and execute a SqlCommand. First we will need a connection. We can use the one that is already in our DataAdapter.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $conn = $da.SelectCommand.Connection
PS C:\demos&amp;gt; $conn

StatisticsEnabled                : False
ConnectionString                 : server=.;integrated security=true;database=northwind
ConnectionTimeout                : 15
Database                         : northwind
DataSource                       : .
PacketSize                       : 8000
ServerVersion                    :
WorkstationId                    : PARSEC5
FireInfoMessageEventOnUserErrors : False
State                            : Closed
Site                             :
Container                        :
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;All we did here was to pull a reference to the SqlConnection that our DataAdapter was using. Note that the connection is closed.&lt;/P&gt;
&lt;P&gt;Next we need a command to execute. That easy once we have a connection.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $cmd = New-Object "System.Data.SqlClient.SqlCommand" ("SELECT * FROM Scratch..TestPSData", $conn)
PS C:\demos&amp;gt; $cmd

Connection             : System.Data.SqlClient.SqlConnection
NotificationAutoEnlist : True
Notification           :
Transaction            :
CommandText            : SELECT * FROM Scratch..TestPSData
CommandTimeout         : 30
CommandType            : Text
DesignTimeVisible      : True
Parameters             : {}
UpdatedRowSource       : Both
Site                   :
Container              :
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now we need to execute the command and capture the results. Of course we want to be sure that the &lt;CODE&gt;$conn&lt;/CODE&gt; is open before we use it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $conn.Open()
PS C:\demos&amp;gt; $rdr = $cmd.ExecuteReader()
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;What can we do with a reader? If we were writing a C# program we would probably put &lt;CODE&gt;$rdr&lt;/CODE&gt; in a loop and call the Read() method on it until we got to the last row. We could do that in PowerShell too if we wanted to, but there is an alternate way which is more &amp;#8220;pipeline&amp;#8221; oriented.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $rdr | &amp;amp;{
&amp;gt;&amp;gt; begin{$values = new-object "System.Object[]" $rdr.FieldCount}
&amp;gt;&amp;gt; process {$_.GetValues($values); $datatable.Rows.Add($values)}
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
1
                                                                          my id
                                                                          -----
                                                                              1
1
                                                                              1
PS C:\demos&amp;gt; $datatable | format-table -autosize
my id
-----
    1
    1
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We start by putting the DataReader we got in previous step into the pipeline. This causes a DbDataRecord to be sent down the pipeline once for each row in the result. We process the DbDataRecords in an inline function, that is the script inside of &amp;amp;{}. The &lt;CODE&gt;begin&lt;/CODE&gt; part of the function runs just once before anything is passed into the segement of the pipeline it occupies. It uses the FieldCount of the DataReader to initialize &lt;CODE&gt;$values&lt;/CODE&gt; variable to an array that can hold all of the values of the columns in a row.&lt;/P&gt;
&lt;P&gt;Once the &lt;CODE&gt;begin&lt;/CODE&gt; part of the inline function has completed the &lt;CODE&gt;process&lt;/CODE&gt; part is executed once for each DbDataRecord. It uses the GetValues method of the DbDataRecord to extract all of the values of the row and put them into the &lt;CODE&gt;$values&lt;/CODE&gt; variable. Then it uses the Add method of the Rows property of the &lt;CODE&gt;$datatable&lt;/CODE&gt; to add a row to the table.&lt;/P&gt;
&lt;P&gt;Lastly we send the &lt;CODE&gt;$datatable&lt;/CODE&gt; variable to format-table and see the the two rows from the TestPSData table in the database have been added to it.&lt;/P&gt;
&lt;P&gt;Note that there is one artifact between the filling of the &lt;CODE&gt;$datatable&lt;/CODE&gt; and the final command&amp;#8230; we see the content of the table and some other stuff. This is due to the fact that the GetValues and Rows.Add methods both return values and we did not capture so they &amp;#8220;leaked&amp;#8221; out of the script. We can fix that just by adding a dummy variable to capture that output.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $rdr | &amp;amp;{
&amp;gt;&amp;gt; begin{$values = new-object "System.Object[]" $rdr.FieldCount}
&amp;gt;&amp;gt; process {$d=$_.GetValues($values); $d=$datatable.Rows.Add($values)}
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;So we have seen that we can use ADO.NET inside of PowerShell. It a quick way to work out some &lt;I&gt;ad hoc&lt;/I&gt; queries and do some processing on the results. You can easily do &lt;I&gt;ad hoc&lt;/I&gt;SQL queries form SQL Server management Studio or SqlCmd, but with PowerShell you can mix the results of those queries with either pipeline or conventional procedural programming.&lt;/P&gt;
&lt;P&gt;Dan &lt;A href="mailto:dan@pluralsight.com"&gt;dan@pluralsight.com&lt;/A&gt;&lt;/P&gt;
&lt;P&gt;PS Thanks to Jeffery Stover for pointing out the column properties on DataTables.&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41389.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>PowerShell and XML and SQL Server</title><link>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx</link><pubDate>Sat, 28 Oct 2006 14:16:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/41337.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/10/28/41337.aspx#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/41337.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/41337.aspx</trackback:ping><description>&lt;P&gt;PowerShell offers support for XML data directly though its [xml] datatype and this article is going to look at that.&lt;/P&gt;
&lt;P&gt;First of all to make use of the builtin [xml] datatype just prefix a variable name with [xml] when you assign something to it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$order = "&amp;lt;order customer='joe'&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;line price='3.00' qty='4'&amp;gt;nut&amp;lt;/line&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;line price='1.00' qty='3'&amp;gt;hammer&amp;lt;/line&amp;gt;
&amp;gt;&amp;gt;         &amp;lt;/order&amp;gt;"
&amp;gt;&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;At first glance this appears to be an ordinary assignment of a string to a variable. But it is doing more than that, it is checking the string to make sure that it is well-formed XML, &lt;I&gt;i.e.&lt;/I&gt; text that conforms to the XML specification. For example the following will produce an error&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$xdata = "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;/Order&amp;gt;"
Cannot convert value "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;Order&amp;gt;" to type "System.Xml.Xm
lDocument". Error: "Unexpected end of file has occurred. The following elements
 are not closed: Order, order. Line 1, position 34."
At line:1 char:12
+ [xml]$xdata  &amp;lt;&amp;lt;&amp;lt;&amp;lt; = "&amp;lt;order &amp;gt;&amp;lt;line&amp;gt;stuff&amp;lt;/line&amp;gt;&amp;lt;Order&amp;gt;"
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;because of a mis-match between the opening &lt;CODE&gt;&amp;lt;order&amp;gt;&lt;/CODE&gt; tag and the closing &lt;CODE&gt;&amp;lt;\Order&amp;gt;&lt;/CODE&gt; tag. So when you use an [xml] variable in PowerShell you can be sure that what it contains is XML.&lt;/P&gt;
&lt;P&gt;So $order is an [xml] variable, what can we do with it? One of the nice things about PowerShell is that if you are not sure what you can do with a varable you can always ask by piping the variable to the get-member function.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order | get-member

   TypeName: System.Xml.XmlDocument
Name                        MemberType            Definition
----                        ----------            ----------
ToString                    CodeMethod            static System.String XmlNo...
add_NodeChanged             Method                System.Void add_NodeChange...
add_NodeChanging            Method                System.Void add_NodeChangi...
add_NodeInserted            Method                System.Void add_NodeInsert...
add_NodeInserting           Method                System.Void add_NodeInsert...
add_NodeRemoved             Method                System.Void add_NodeRemove...
add_NodeRemoving            Method                System.Void add_NodeRemovi...
AppendChild                 Method                System.Xml.XmlNode AppendC...
Clone                       Method                System.Xml.XmlNode Clone()
CloneNode                   Method                System.Xml.XmlNode CloneNo...
CreateAttribute             Method                System.Xml.XmlAttribute Cr...
CreateCDataSection          Method                System.Xml.XmlCDataSection...
CreateComment               Method                System.Xml.XmlComment Crea...
CreateDocumentFragment      Method                System.Xml.XmlDocumentFrag...
CreateDocumentType          Method                System.Xml.XmlDocumentType...
CreateElement               Method                System.Xml.XmlElement Crea...
CreateEntityReference       Method                System.Xml.XmlEntityRefere...
CreateNavigator             Method                System.Xml.XPath.XPathNavi...
CreateNode                  Method                System.Xml.XmlNode CreateN...
CreateProcessingInstruction Method                System.Xml.XmlProcessingIn...
CreateSignificantWhitespace Method                System.Xml.XmlSignificantW...
CreateTextNode              Method                System.Xml.XmlText CreateT...
CreateWhitespace            Method                System.Xml.XmlWhitespace C...
CreateXmlDeclaration        Method                System.Xml.XmlDeclaration ...
Equals                      Method                System.Boolean Equals(Obje...
GetElementById              Method                System.Xml.XmlElement GetE...
GetElementsByTagName        Method                System.Xml.XmlNodeList Get...
GetEnumerator               Method                System.Collections.IEnumer...
GetHashCode                 Method                System.Int32 GetHashCode()
GetNamespaceOfPrefix        Method                System.String GetNamespace...
GetPrefixOfNamespace        Method                System.String GetPrefixOfN...
GetType                     Method                System.Type GetType()
get_Attributes              Method                System.Xml.XmlAttributeCol...
get_BaseURI                 Method                System.String get_BaseURI()
get_ChildNodes              Method                System.Xml.XmlNodeList get...
get_DocumentElement         Method                System.Xml.XmlElement get_...
get_DocumentType            Method                System.Xml.XmlDocumentType...
get_FirstChild              Method                System.Xml.XmlNode get_Fir...
get_HasChildNodes           Method                System.Boolean get_HasChil...
get_Implementation          Method                System.Xml.XmlImplementati...
get_InnerText               Method                System.String get_InnerText()
get_InnerXml                Method                System.String get_InnerXml()
get_IsReadOnly              Method                System.Boolean get_IsReadO...
get_Item                    Method                System.Xml.XmlElement get_...
get_LastChild               Method                System.Xml.XmlNode get_Las...
get_LocalName               Method                System.String get_LocalName()
get_Name                    Method                System.String get_Name()
get_NamespaceURI            Method                System.String get_Namespac...
get_NameTable               Method                System.Xml.XmlNameTable ge...
get_NextSibling             Method                System.Xml.XmlNode get_Nex...
get_NodeType                Method                System.Xml.XmlNodeType get...
get_OuterXml                Method                System.String get_OuterXml()
get_OwnerDocument           Method                System.Xml.XmlDocument get...
get_ParentNode              Method                System.Xml.XmlNode get_Par...
get_Prefix                  Method                System.String get_Prefix()
get_PreserveWhitespace      Method                System.Boolean get_Preserv...
get_PreviousSibling         Method                System.Xml.XmlNode get_Pre...
get_SchemaInfo              Method                System.Xml.Schema.IXmlSche...
get_Schemas                 Method                System.Xml.Schema.XmlSchem...
get_Value                   Method                System.String get_Value()
ImportNode                  Method                System.Xml.XmlNode ImportN...
InsertAfter                 Method                System.Xml.XmlNode InsertA...
InsertBefore                Method                System.Xml.XmlNode InsertB...
Load                        Method                System.Void Load(Stream in...
LoadXml                     Method                System.Void LoadXml(String...
Normalize                   Method                System.Void Normalize()
PrependChild                Method                System.Xml.XmlNode Prepend...
ReadNode                    Method                System.Xml.XmlNode ReadNod...
RemoveAll                   Method                System.Void RemoveAll()
RemoveChild                 Method                System.Xml.XmlNode RemoveC...
remove_NodeChanged          Method                System.Void remove_NodeCha...
remove_NodeChanging         Method                System.Void remove_NodeCha...
remove_NodeInserted         Method                System.Void remove_NodeIns...
remove_NodeInserting        Method                System.Void remove_NodeIns...
remove_NodeRemoved          Method                System.Void remove_NodeRem...
remove_NodeRemoving         Method                System.Void remove_NodeRem...
ReplaceChild                Method                System.Xml.XmlNode Replace...
Save                        Method                System.Void Save(String fi...
SelectNodes                 Method                System.Xml.XmlNodeList Sel...
SelectSingleNode            Method                System.Xml.XmlNode SelectS...
set_InnerText               Method                System.Void set_InnerText(...
set_InnerXml                Method                System.Void set_InnerXml(S...
set_Prefix                  Method                System.Void set_Prefix(Str...
set_PreserveWhitespace      Method                System.Void set_PreserveWh...
set_Schemas                 Method                System.Void set_Schemas(Xm...
set_Value                   Method                System.Void set_Value(Stri...
set_XmlResolver             Method                System.Void set_XmlResolve...
Supports                    Method                System.Boolean Supports(St...
Validate                    Method                System.Void Validate(Valid...
WriteContentTo              Method                System.Void WriteContentTo...
WriteTo                     Method                System.Void WriteTo(XmlWri...
Item                        ParameterizedProperty System.Xml.XmlElement Item...
order                       Property              System.Xml.XmlElement orde...

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Notice that an [xml] PowerShell variable is an .NET XmlDocument. So everything you know about an XmlDocument can be applied to a PowerShell [xml] variable. In a later article we will look at how you can leverage this fact, but for now we will stick to the XML support that is directly part of PowerShell.&lt;/P&gt;
&lt;P&gt;Look closely at the information returned by get-member&amp;#8230; notice there is a property named &amp;#8220;order&amp;#8221; of type XmlElement. If you lookup XmlDocument in MSDN you will not find an order property for it, this is something that PowerShell has added.&lt;/P&gt;
&lt;P&gt;The order property is in fact the root, or document, element for the &lt;CODE&gt;$order&lt;/CODE&gt; document. PowerShell has a simple path language for XML that makes use of the additional properties that PowerShell adds to XML elements. This language is not XPath but still very useful. The simplest thing you can do with an XML variable is ask what it contains:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order
order
-----
order

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;It produces a table that shows the children of the &lt;CODE&gt;$order&lt;/CODE&gt; variable. It shows that the &lt;CODE&gt;$order&lt;/CODE&gt; variable has a single child named &amp;#8220;order&amp;#8221; whose value is &amp;#8220;order&amp;#8221;. Since we saw that order is a property of the $order variable we can append it with a &amp;#8220;.&amp;#8221; to that variable to see what that contains:.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order
customer                                line
--------                                ----
joe                                     {line, line}

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;When PowerShell displays the children of an element in at table, or list, it show both the elements and attributes of that element.&lt;/P&gt;
&lt;P&gt;If you prefer to see the children of an [xml] variable in list form you can pipe the results to the format-list function:&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order | format-list

customer : joe
line     : {line, line}
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we see that line has a number of &lt;CODE&gt;line&lt;/CODE&gt;s as children. &lt;/P&gt;
&lt;P&gt;We can extend this dotted property syntax further and find the value for customer:&lt;/P&gt;
&lt;P&gt;PS C:\demos&amp;gt; $order.order.customer joe PS C:\demos&amp;gt;&lt;/P&gt;
&lt;P&gt;We can tell that this is just a plain old value because the telltale &amp;#8220;&amp;#8212;&amp;#8212;-&amp;#8221; used to separate the names of properties from their values is not there. Likewise if we had used a list form the &lt;CODE&gt;name :&lt;/CODE&gt; is not there.&lt;/P&gt;
&lt;P&gt;Now let&amp;#8217;s use the dotted property syntax to see what the value of line is.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line
price                      qty                        #text
-----                      ---                        -----
3.00                       4                          nut
4.00                       2                          hammer
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we can see that PowerShell has output a two lines, one for each line element in the &lt;CODE&gt;order&lt;/CODE&gt; element. &lt;CODE&gt;price&lt;/CODE&gt; and &lt;CODE&gt;qty&lt;/CODE&gt; are attributes of the line and the special name &lt;CODE&gt;#text&lt;/CODE&gt; indicates that the line element contains some text. &lt;/P&gt;
&lt;P&gt;Since there are a number of &lt;CODE&gt;line&lt;/CODE&gt; elements we can refer to them individually using an array syntax.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line[0]
price                      qty                        #text
-----                      ---                        -----
3.00                       4                          nut

PS C:\demos&amp;gt; $order.order.line[1]
price                      qty                        #text
-----                      ---                        -----
4.00                       2                          hammer
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;We can even ask how many line elements there are.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line.Length
2
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Under the covers &lt;CODE&gt;$order.order.line&lt;/CODE&gt; is a .NET array and we can do with it anything we might do with a .NET array. If we want the price of the second &lt;CODE&gt;line&lt;/CODE&gt; element&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line[1].price
4.00
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The dotted property syntax is actually a simple path language and is very similar to that used in data binding in ADO.NET, Windows Workflow, and Windows Presentation Framework. Now that we see the basics of this syntax lets see if we can do some calculations. Let&amp;#8217;s calculate the value of the order.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $value = 0
PS C:\demos&amp;gt; $order.order.line | %{$value += [double]$_.qty * $_.price}
PS C:\demos&amp;gt; $value
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that we had to cast the qty attribute to a double to force a numeric computation.&lt;/P&gt;
&lt;P&gt;First we create a &lt;CODE&gt;$value&lt;/CODE&gt; variable and initialize it to 0. Then we use the dotted property syntax to select the line elements in the &lt;CODE&gt;$order&lt;/CODE&gt; variable. &lt;CODE&gt;$order.order.line&lt;/CODE&gt; is actually a .NET array. When a .NET array is passed into a segment of a pipeline, as indicated by the &lt;CODE&gt;|&lt;/CODE&gt; symbol, each item in the array is passed into that segment one at a time for processing.&lt;/P&gt;
&lt;P&gt;The %{} syntax in the second segment of the pipeline says to execute what is between the braces once for each item that flows into the pipe. The &lt;CODE&gt;$_&lt;/CODE&gt; refers to the individual item that is flowing through that segment, in this case a line element. So as the line elements flow through the pipe the script between the braces calculates the extended price, &lt;I&gt;i.e.&lt;/I&gt; the &lt;CODE&gt;price&lt;/CODE&gt; times the &lt;CODE&gt;qty&lt;/CODE&gt;, for the line element and accumulates that product into the &lt;CODE&gt;$value&lt;/CODE&gt; element. Last we get the value of the &lt;CODE&gt;$value&lt;/CODE&gt; element.&lt;/P&gt;
&lt;P&gt;Next let&amp;#8217;s do this same calculation again, but be more &amp;#8220;pipeline&amp;#8221; oriented.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $order.order.line | %{[double]$_.qty * $_.price} | &amp;amp;{begin{$v = 0}process{$v += $_}end{$v}}
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;This is making use of an in-line function, the part inside of &lt;CODE&gt;&amp;amp;{}&lt;/CODE&gt;. It starts the same way the previous solution did, by passing the line elements into the pipeline. The second segment of the pipeline calculates the extended price for the line element and passes it onto the last segment.&lt;/P&gt;
&lt;P&gt;The last segement is the in-line function and consists of three parts. The first part is inside of &lt;CODE&gt;begin{}&lt;/CODE&gt; and is executed just once no matter how many elements pass through the pipeline and it initializes the &lt;CODE&gt;$v&lt;/CODE&gt; variable to 0.&lt;/P&gt;
&lt;P&gt;The second part of the function, the part inside of &lt;CODE&gt;process{}&lt;/CODE&gt;, is executed once on each item that passes through the pipeline. It just adds each of the extended prices it receives to the &lt;CODE&gt;$v&lt;/CODE&gt; variable.&lt;/P&gt;
&lt;P&gt;The last part of the function, the part inside of the &lt;CODE&gt;end{}&lt;/CODE&gt;, is also executed just once, but only after the process part is finished processing all of the items passing through the pipeline. All it does is refer to the &lt;CODE&gt;$v&lt;/CODE&gt; so it is returned.&lt;/P&gt;
&lt;P&gt;We can carry this even further. If we know we will be just summing up some numbers that pass though the pipeline we can change our in-line function into a named function and reuse it over and over.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; function sum {
&amp;gt;&amp;gt; begin{$v = 0}
&amp;gt;&amp;gt; process{$v += $_}
&amp;gt;&amp;gt; end{$v}
&amp;gt;&amp;gt; }
&amp;gt;&amp;gt;
PS C:\demos&amp;gt; $order.order.line | %{[double]$_.qty * $_.price} | sum
20
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;So far our example has used literal XML, that is we have literally typed in the XML document we want to process. In real life you will probably have some XML files to process. There are a number of ways to read a file with PowerShell but the easiest it to use for our purposes is the &lt;CODE&gt;${}&lt;/CODE&gt; syntax. The file c:\demos\order.xml contains the same XML document we used to initialize $order in the previous examples.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$orders2 = ${c:\demos\order.xml}
PS C:\demos&amp;gt; $orders2
order
-----
order

PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Note that the full path was specified.&lt;/P&gt;
&lt;P&gt;Another source of XML data is SQL Server. It uses a FOR XML clause in a SELECT statement to return the results of a query in the form of XML. For example the following query is made against a table that contains information about last names there were filled out on United States census forms.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;SELECT name, rank FROM LastNames FOR XML AUTO, ROOT('Names')
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;The LastNames table contains the names from the census forms along with their rank, ranked by how many forms contained that name. Now we have to somehow use PowerShell to execute a query against SQL Server. Besides its builtin functionality PowerShell can use any functionality provided by a .NET class. So we will use ADO.NET to execute the query. The tricky part, as we shall see in shortly, is how to stick the results into an [xml] PowerShell variable.&lt;/P&gt;
&lt;P&gt;First we need a connection to SQL Server.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $conn = new-object System.Data.SqlClient.SqlConnection
PS C:\demos&amp;gt; $conn.ConnectionString = "server=.;integrated security=true;database=scratch"
PS C:\demos&amp;gt; $conn.Open()
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;To make the connection we use the PowerShell new-object function to create an instance of SqlConnection then fill out the connection string. Now we need a command.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $cmd = new-object System.Data.SqlClient.SqlCommand
PS C:\demos&amp;gt; $cmd.CommandText="SELECT name, rank FROM LastNames FOR XML AUTO,ROOT('Names')"
PS C:\demos&amp;gt; $cmd.Connection=$conn
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Similar to making the connection we make the command by creating an instance of a SqlCommand and fill out the CommandText and Connection.&lt;/P&gt;
&lt;P&gt;When ever a SqlCommand uses FOR XML it must be executed using ExecuteXmlReader, which returns its result in the form of an XmlReader.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $xrdr = $cmd.ExecuteXmlReader()
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;If were were writing a program in C# or VB.NET then at this point we would use the XmlReader to Load an XmlDocument. Wait! Remember what we found out when we used get-member to find out what we could do with an [xml] PowerShell variable? We found out that is was an XmlDocument. So we can just use the Load method on an [xml] varible load it with the results of our query. These is one problem though&amp;#8230;&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names.Load($xrdr)
You cannot call a method on a null-valued expression.
At line:1 char:18
+ [xml]$names.Load( &amp;lt;&amp;lt;&amp;lt;&amp;lt; $xrdr)
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;A freshly created [xml] variable has not yet been initialized so we cannot use it to load anything. There are a couple of ways to intialize it. One is to just assign an XmlDocument to it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names = New-Object System.Xml.XmlDocument
PS C:\demos&amp;gt; [xml]$names.Load($xrdr)
PS C:\demos&amp;gt; $names
Names
-----
Names
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we have use new-object to create an XmlDocument and then assign it to an [xml] variable. After we have done that the Load method can be used to read the results of our FOR XML query into the &lt;CODE&gt;$order3&lt;/CODE&gt; variable. The we have PowerShell to output the &lt;CODE&gt;$name&lt;/CODE&gt; variable in tablular form. Here we can see it contains the expected &amp;#8216;Names&amp;#8221; root element.&lt;/P&gt;
&lt;P&gt;Another way to initialize the [xml] varible is to just put some XML into it, then write over it when the Load method is used.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$names = '&amp;lt;a/&amp;gt;'
PS C:\demos&amp;gt; $names.Load($xrdr)
PS C:\demos&amp;gt; $names
Names
-----
Names
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Now that we have our Names XML document let&amp;#8217;s take a quick look inside of it.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; $names.Names
LastNames
---------
{SMITH, JOHNSON, WILLIAMS, JONES...}

PS C:\demos&amp;gt; $names.Names.LastNames.Length
88799
PS C:\demos&amp;gt;
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;Here we can see that there are 88799 names the document and the first few are SMITH,JOHNSON and so on.&lt;/P&gt;
&lt;P&gt;One last thing&amp;#8230; there is a bug in the RC2 PowerShell that hopefully will be fixed by the time the RTM version is released. Elements whose name is &amp;#8220;item&amp;#8221; will cause problems when the table or list formatter processes them.&lt;/P&gt;&lt;PRE&gt;&lt;CODE&gt;PS C:\demos&amp;gt; [xml]$stuff = '&amp;lt;stuff&amp;gt;&amp;lt;item/&amp;gt;&amp;lt;item/&amp;gt;&amp;lt;/stuf
PS C:\demos&amp;gt; $stuff
stuff
-----
stuff

PS C:\demos&amp;gt; $stuff.stuff
format-default : The member "Item" is already present.
&lt;/CODE&gt;&lt;/PRE&gt;
&lt;P&gt;You will still be able to process &amp;#8220;item&amp;#8221; element otherwise, it is just an issue when format-table or format-list is used.&lt;/P&gt;
&lt;P&gt;We have seen the PowerShell has some pretty straightforward support for XML in its simple dotted path expressions. You can work with literal XML, XML from files and XML as it is returned from SQL Server in FOR XML queries. The PowerShell builtin path language that is very similar to that used in data binding which you may have already used. I didn&amp;#8217;t cover it in this article, but PowerShell also allows you to make use of all the XML capabilities built into .NET&amp;#8230; but that leaves me something to work on for the next article.&lt;/P&gt;
&lt;P&gt;Dan&lt;/P&gt;
&lt;P&gt;dan@pluralsight.com&lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/41337.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Comparing XML in SQL Server 2005</title><link>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx</link><pubDate>Fri, 01 Sep 2006 07:37:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/36829.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/09/01/36829.aspx#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/36829.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/36829.aspx</trackback:ping><description>&lt;H1&gt;Comparing XML in SQL Server 2005&lt;/H1&gt;
&lt;P&gt;Xml is not text so a literal compare of two xml documents may lead to a false negative, that is it may indicate that two documents are not equal when in fact they are. For example these two xml documents are the same: &lt;/P&gt;&lt;PRE&gt; &amp;lt;item x="1" y="2"/&amp;gt;
 
 
 &amp;lt;item y="2" x="1"/&amp;gt;
 &lt;/PRE&gt;
&lt;P&gt;because the order of attributes in xml is not signicant. SQL Server has not "built in" way to compare xml documents and in fact makes a point about the fact that it cannot compare xml documents. This is not a deficiency of SQL Server, it is just due to the fact that xml may not be treated as ordinary text. &lt;/P&gt;
&lt;P&gt;You can try to compare to xml documents by converting them to a string or a varbinary type, but this will lead to false negatives. For example: &lt;/P&gt;&lt;PRE&gt; DECLARE @x1 xml
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item x="1" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item y="2" x="1"/&amp;gt;'
 if CAST(@x1 AS VARBINARY(MAX)) = CAST(@x2 AS VARBINARY(MAX))
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;&lt;/P&gt;This prints out 'not equal' 
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;Things get more interesting if an xml schema is involved. For example: &lt;/P&gt;&lt;PRE&gt; CREATE XML SCHEMA COLLECTION Test AS
 '
 &amp;lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
 targetNamespace = "urn:test"&amp;gt;
 &amp;lt;xs:element name="item"&amp;gt;
 &amp;lt;xs:complexType&amp;gt;
 &amp;lt;xs:attribute name="x" type="xs:int"/&amp;gt;
 &amp;lt;xs:attribute name="y" type="xs:int"/&amp;gt;
 &amp;lt;/xs:complexType&amp;gt;
 &amp;lt;/xs:element&amp;gt;
 &amp;lt;/xs:schema&amp;gt;'
 
 DECLARE @x1 xml(Test)
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 if CAST(@x1 AS VARBINARY(MAX)) = CAST(@x2 AS VARBINARY(MAX))
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 
 &lt;/PRE&gt;
&lt;P&gt;this prints out 'not equal' even though both @x1 and @x2 appear to be literally the same. This is due to the fact that SQL Server does not store literal form of the xml when it is strongly typed, it stores each of the simple types in the xml document in it's XML Schema canonical form. The conanical form of '00' for xs:int is '0'. So in this case the comparison is not equal becuase SQL Server is comparing '0' in the strongly typed @x1 to '00' in the weakly typed @x2. &lt;/P&gt;
&lt;P&gt;In order to compare two xml documents the must both be converted to a canonical form (http://www.w3.org/TR/2001/REC-xml-c14n-20010315). You can then do a byte by byte comparison of the two canonical forms to determine if they are the same according to the XML 1.1 recomendation, but will not take into account the canonical represenation of data types from the XML Schema recomendation. &lt;/P&gt;
&lt;P&gt;The XmlDsigC14NTransform class in the .NET Framwork can convert xml to canonical xml so that it can be compared. You can create a CLR user defined function that uses this class to do the comparison. For example: &lt;/P&gt;&lt;PRE&gt; public partial class UserDefinedFunctions
 {
  [Microsoft.SqlServer.Server.SqlFunction]
  public static SqlBoolean CompareXML(SqlXml x1, SqlXml x2)
  {
   if (x1.IsNull || x2.IsNull)
   {
    return SqlBoolean.Null;
   }
   XmlDocument xdoc1 = new XmlDocument();
   xdoc1.Load(x1.CreateReader());
   XmlDocument xdoc2 = new XmlDocument();
   xdoc2.Load(x2.CreateReader());
   XmlDsigC14NTransform xcanonical1 = new XmlDsigC14NTransform();
   xcanonical1.LoadInput(xdoc1);
   XmlDsigC14NTransform xcanonical2 = new XmlDsigC14NTransform();
   xcanonical2.LoadInput(xdoc2);
   // this returns the conanical form
   Stream s1 = (Stream)xcanonical1.GetOutput(typeof(Stream));
   Stream s2 = (Stream)xcanonical2.GetOutput(typeof(Stream));
   // do byte by byte compare
   if (s1.Length != s2.Length)
   {
    return new SqlBoolean(false);
   }
   for (Int32 index = 0; index &amp;lt; s1.Length; index++)
   {
    if (s1.ReadByte() != s2.ReadByte())
    {
     return new SqlBoolean(false);
    }
   }
   return new SqlBoolean(true);
  }
 };
 
 &lt;/PRE&gt;
&lt;P&gt;Then in T-SQL you can do the comparison: &lt;/P&gt;&lt;PRE&gt; DECLARE @x1 xml
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item x="1" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item y="2" x="1"/&amp;gt;'
 DECLARE @b bit
 set @b = dbo.CompareXml(@x1, @x2)
 IF @b = 1
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;and this will print out 'equal' as expected. &lt;/P&gt;
&lt;P&gt;Note that the assembly the contains the CompareXML function will have to be loaded with PERMISSION_SET = UNSAFE because XmlDsigC14NTransform.LoadInput may not be used from partially trusted assemblies. &lt;/P&gt;
&lt;P&gt;If the following comparison is done: &lt;/P&gt;&lt;PRE&gt; 
 DECLARE @x1 xml(Test)
 DECLARE @x2 xml
 
 set @x1 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 set @x2 = '&amp;lt;item xmlns="urn:test" x="00" y="2"/&amp;gt;'
 if dbo.CompareXml(@x1,@x2) = 1
 PRINT 'equal'
 ELSE
 PRINT 'not equal'
 &lt;/PRE&gt;
&lt;P&gt;It will return 'not equal' because SQL Server does not store literal xml for a strongly typed xml variable. It stores strongly typed xml datatype in their XML Schema canonical form and this is '0' for '00' if the datatype is xs:int. Again this is expected because of the way that SQL Server treats strongly typed xml. &lt;/P&gt;Dan &lt;img src ="http://pluralsight.com/blogs/dan/aggbug/36829.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>CLR Based Histogram Functions and SQL Server 2005</title><link>http://pluralsight.com/blogs/dan/archive/2006/08/26/36317.aspx</link><pubDate>Sat, 26 Aug 2006 11:43:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/08/26/36317.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/36317.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/08/26/36317.aspx#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/36317.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/36317.aspx</trackback:ping><description>&lt;H1&gt;CLR Based Histogram Functions and SQL Server 2005&lt;/H1&gt;
&lt;P&gt;One of the enhancements to SQL Server 2005 is the ability to create your own aggregates, or User Defined Aggregate as they are called. They are pretty easy to make and I'll go over the basics later in this article. But the real question is why would you want to make your own UDA? To answer that question we have to look some of the details of aggregates in general.&lt;/P&gt;
&lt;P&gt;The code for the examples in this article can be found at &lt;A href="http://www.pluralsight.com/dan/samples/udahisto.zip"&gt;code samples&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;For example SQL Server has only a few built-in aggregates compared to Oracle and you might like to add some of the ones found in Oracle. Oracle has an aggregate named COVAR_POP that you might like to be able to use in SQL Server. However most aggregates that are basically some kind of accumulation of arithmetic calculations can be composed out of the aggregates that already exist in SQL Server. For example:&lt;/P&gt;
&lt;P&gt;&lt;PRE&gt;SELECT (SUM(col1 * col2) - SUM(col2) * SUM(col1) / count(*)) / count(*) FROM myTable
WHERE col1 IS NOT NULL and col2 IS NOT NULL&lt;P&gt;&lt;/P&gt;&lt;P&gt;
&lt;/P&gt;&lt;/PRE&gt;produces the same result as the Oracle aggregate. So for most arithmetic aggregates you need not create a CLR UDA. In general you can use a composition of the built-in aggregates in SQL Server to create most aggregates that accumlate arithmetic calculations.
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;A couple of kinds of aggregates that you can't compose out of the aggregate functions built into SQL Server are a median and string concatenation aggragates. I've talked about median calculations last year. You can find the blog articles at &lt;A href="http://pluralsight.com/blogs/dan/archive/2005/09/29/15082.aspx"&gt;Psuedo Median Aggregrate&lt;/A&gt; and &lt;A href="http://pluralsight.com/blogs/dan/archive/2005/09/28/15073.aspx"&gt;Yet Another Median Calculation&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;The string concatenation aggregate is at least a candidate for a UDA, but a UDA has a limitation that requires its intermediate state to be serializable in 8000 bytes. You can make a UDA that concatenates strings but it seems like it would be a bit scary to use in practice. It could be working fine in production for months and then suddenly fail because it happens produce a string that is too long. I would call this brittle and avoid it, but YMMV. Also, if you don't need a aggregate function, but just an aggregate calculation, there are other ways to do this in SQL Server.&lt;/P&gt;
&lt;P&gt;There is another kind of "aggregate" that is useful, a histogram aggregate. I put the term aggregate in quotes because all of SQL Server's built-in aggregate functions and UDA's return scalars and a histogram is a table. But we are going to look at a way to use a UDA to create a histogram and, with a little slight of hand, make it return a table. We will call this a UDA/Histogram.&lt;/P&gt;
&lt;P&gt;There are at least two kinds of UDA/histograms we could look at. One is a histogram of the letter usage in words. For example the letter usage for the the pharse "Attention all aardvarks." would show that the letter &lt;B&gt;A&lt;/B&gt; was used five times, assuming we ignore case. This UDA/histogram could be used against a table of customer names to find out the of letter usage all customer names.&lt;/P&gt;
&lt;P&gt;Another historgram we could look at is more of a classic histogram, it would find the usage of numbers in a set of ranges, maybe as the count of numbers in the range 0 though 1, 1 through 2, and so on up to 9 through 10. &lt;/P&gt;
&lt;P&gt;There are a number of ways to implement a histogram in SQL Server 2005. One is via the UDA/histogram that will will be looking at shortly, one is by JOINing with a range table, and the third is to use a CURSOR. In this article we will look at the letter histogram. In later blog articles we will look at using CLR UDA to implement more "classic" histograms.&lt;/P&gt;
&lt;P&gt;We will look at the letter usage UDA/histogram now. First of all a UDA has four methods that you must implement: Init, Accumulate, Merge and Terminate. If you use Visual Studio and make a language/database project it will skeleton UDA for you, you will just have to fill in these methods.&lt;/P&gt;
&lt;P&gt;This implementation has 26 Int32 fields, one for each letter in the English alphabet, to accumulate the letter counts. It might seem better to use an array to do this but if you do it is a bit more complicated to implement. A fragement of the accumlators and Init method are shown below:&lt;/P&gt;
&lt;P&gt;&lt;/P&gt;&lt;PRE&gt;public struct LetterHistogramAgg
{
 Int32 As;
 Int32 Bs;
 Int32 Cs;
...
 public void Init()
 {
  As = 0;
  Bs = 0;
...
&lt;/PRE&gt;
&lt;P&gt;The Accumulate method converts the incoming string to upper case, the checks out each of its letters to increment the approprate accumlator. Here is a fragement of that:&lt;/P&gt;
&lt;P&gt;&lt;/P&gt;&lt;PRE&gt; public void Accumulate(SqlString Value)
 {
  Char[] letters = Value.Value.ToUpper().ToCharArray();
  foreach (Char letter in letters)
  {
   if (letter == 'A')
   {
    As = As + 1;
    continue;
   }
&lt;/PRE&gt;
&lt;P&gt;The Merge method just has to add together the accumlators, you can see that in the example code.&lt;/P&gt;
&lt;P&gt;The terminate method has to return the histogram. Because this is a UDA that must be a scalar, so what it returns is a ";" separated string with the counts from each letter accumulator, in order. Here is a fragment of that method:&lt;/P&gt;
&lt;P&gt;&lt;/P&gt;&lt;PRE&gt; public SqlString Terminate()
 {
  StringBuilder sb = new StringBuilder();
  sb.Append(As.ToString());
  sb.Append(";");
  sb.Append(Bs.ToString());
  sb.Append(";");
&lt;/PRE&gt;
&lt;P&gt;At first glance it might seem less than useful to return a histogram as a string, but shortly we will see it is very easy to make a table valued user defined function that converts it to a table.&lt;/P&gt;
&lt;P&gt;One of the pieces of test data I have are tables of male and female first names and last names that were used to fill out United States census forms. You can get the raw data for these tables from &lt;A href="http://www.census.gov/genealogy/names/dist.all.last"&gt;http://www.census.gov/genealogy/names/dist.all.last&lt;/A&gt;, &lt;A href="http://www.census.gov/genealogy/names/dist.female.first"&gt;http://www.census.gov/genealogy/names/dist.female.first&lt;/A&gt;, and &lt;A href="http://www.census.gov/genealogy/names/dist.male.last"&gt;http://www.census.gov/genealogy/names/dist.male.last&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;I've named the tables MaleFirstName, FemaleFirstNames, and LastNames. Using the LetterHistogramAgg on the MaleFirstName table produces:&lt;/P&gt;&lt;PRE&gt;SELECT dbo.LetterHistogramAgg(Name) from MaleFirstNames)
-------------------------------------------------------------------------
664;135;234;333;785;86;117;191;448;104;94;507;229;597;561;52;13;677;286;304;168;73;83;10;205;18;
&lt;/PRE&gt;
&lt;P&gt;Next lets look at making a CLR table valued user defined function that can turn this into something more useful. In order to create a table valued function using the CLR, the function must return an object with an IEnumerable interface. This can be a bit tedious to implement, but in C# the yield keyword will in fact do most of the implementation for you. Below is the code for a UDF that converts the string produced by the LetterHistorgramAgg into a table. The name of the function it implements is LetterHistogramTable.&lt;/P&gt;&lt;PRE&gt;public partial class UserDefinedFunctions
{
 public static void GetHistRow(Object obj, out Char c, out Int32 count)
 {
  HistSample hs = obj as HistSample;
  c = hs.letter;
  count = hs.count;
 }
 class HistSample
 {
  internal Char letter;
  internal Int32 count;
  internal HistSample(Char letter, Int32 count)
  {
   this.count = count;
   this.letter = letter;
  }
 }
 [Microsoft.SqlServer.Server.SqlFunction(
  FillRowMethodName="GetHistRow",
  IsPrecise=true,
  IsDeterministic=true,
  TableDefinition="Letter NCHAR(1), [Count] INT")]
 public static IEnumerable LetterHistogramTable(SqlString histogram)
 {
  if (!histogram.IsNull)
  {
   String[] counts = histogram.Value.Split(new char[] { ';' });
   String alphabet = "ABCDEFGHIJKLMNOPQUSTUVWXYZ";
   for (Int32 index = 0; index &amp;lt; 26; index++)
   {
    yield return new HistSample(alphabet[index], Int32.Parse(counts[index]));
   }
  }
 }
};
&lt;/PRE&gt;
&lt;P&gt;Once we have deployed the LetterHistogramTable UDF we can retry making the histograms of the letters in the MaleFirstNames table. We use the LetterHistogramAgg to make the histogram string, then use the LetterHistogramTable to turn the string into a table.&lt;/P&gt;
&lt;P&gt;&lt;/P&gt;&lt;PRE&gt;SELECT * FROM dbo.LetterHistogramTable((
 SELECT dbo.LetterHistogramAgg(Name) from MaleFirstNames))

Letter Count
------ -----------
A      664
B      135
C      234
D      333
E      785
F      86
G      117
H      191
I      448
J      104
K      94
L      507
M      229
N      597
O      561
P      52
Q      13
U      677
S      286
T      304
U      168
V      73
W      83
X      10
Y      205
Z      18
&lt;/PRE&gt;Let's do the same thing with the FemaleFirstNames table:
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;&lt;PRE&gt;SELECT * FROM dbo.LetterHistogramTable((
 SELECT dbo.LetterHistogramAgg(Name) from FemaleFirstNames))
Letter Count
------ -----------
A      4146
B      327
C      717
D      811
E      3370
F      142
G      346
H      796
I      2350
J      311
K      405
L      2064
M      775
N      2330
O      986
P      147
Q      43
U      1793
S      1097
T      1217
U      423
V      262
W      100
X      29
Y      686
Z      94&lt;P&gt;&lt;/P&gt;&lt;P&gt;

&lt;/P&gt;&lt;/PRE&gt;
&lt;P&gt;&lt;/P&gt;
&lt;P&gt;There is another way to implement the letter historgram and that is to use a CURSOR. You create a table to hold the accumulations of letter counts and then use the CURSOR to fill that table. Here is an example:&lt;/P&gt;
&lt;P&gt;
&lt;P&gt;&lt;PRE&gt;CREATE TABLE #letterCounts
(
letter CHAR(1) PRIMARY KEY,
count INT
)
INSERT INTO #letterCounts SELECT 'A', 0
INSERT INTO #letterCounts SELECT 'B', 0
.....

UPDATE #letterCounts SET Count=0
DECLARE  Name_Cursor CURSOR FOR SELECT Name FROM MaleFirstNames
DECLARE @name VARCHAR(MAX)
OPEN Name_Cursor;
FETCH NEXT FROM Name_Cursor INTO @name;
WHILE @@FETCH_STATUS = 0
BEGIN
 DECLARE @index INT
 SET @index = LEN(@name)
 WHILE @index &amp;gt; 0
 BEGIN
  -- go through each letter in string
  UPDATE #letterCounts SET count = count + 1 WHERE SUBSTRING(@name, @index, 1) = letter
  SET @index = @index - 1
 END
    FETCH NEXT FROM Name_Cursor INTO @name
END;
CLOSE Name_Cursor;
DEALLOCATE name_Cursor;
SELECT * FROM #letterCounts&lt;P&gt;&lt;/P&gt;&lt;P&gt;
letter count
------ -----------
A      664
B      135
C      234
D      333
E      785
F      86
G      117
H      191
I      448
J      104
K      94
L      507
M      229
N      597
O      561
P      52
Q      13
R      677
S      286
T      304
U      168
V      73
W      83
X      10
Y      205
Z      18
&lt;/P&gt;&lt;/PRE&gt;
&lt;P&gt;In general any aggregate can be implemented using a CURSOR. The difference between using a CURSOR to implement the aggregate and a CLR user defined aggregate is speed. In general a CLR user defined aggregate will be more than two orders of magnitude, that is 100 times, faster than the same aggregate implemented using a CURSOR. On my little portable running SQL Server Developer Edition the Cursor based implementation takes about 50 seconds to run and the user defined aggregate one runs in well under one second. Of course this is just a generalization to show that CLR UDA's in general run faster than CURSOR based one... always check you data in your usage patterns before making any kind of decision about performance&lt;/P&gt;
&lt;P&gt;This isn't the whole store on user defined aggregates but it is a start. In later blog articles we will look at "classic" aggregates and also look at some more trade-off between the implementations of aggregates.&lt;/P&gt;
&lt;P&gt;Dan &lt;/P&gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/36317.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>What cost xml, SQL Server?</title><link>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx</link><pubDate>Sun, 06 Aug 2006 15:36:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/32814.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/08/06/32814.aspx#Feedback</comments><slash:comments>4</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/32814.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/32814.aspx</trackback:ping><description>&lt;P&gt;How much space does xml cost in SQL Server 2005? This question seems to come up a lot because xml has a lot of meta data in it and to top it off SQL Server saves the xml data type using UTF-16 encoding. This is, at least for xml that is mostly ascii type characters, up twice as much space as the encoding you typically see for xml, UTF-8. So if you just want to save xml in SQL Server then return it later you're better off storing as VARCHAR(MAX) or maybe VARBINARY(MAX), right? Maybe not, but that is what this article is going to look at...&lt;/P&gt;
&lt;P&gt;Even before we look at the space issues involved I'd like to mention that I think it is really being "too clever by half" to store xml as anything other than xml. Imagine if someone gave you a CSV file that looked like:&lt;/P&gt;
&lt;P&gt;pipe, 100&lt;BR&gt;rope, 250&lt;/P&gt;
&lt;P&gt;and and told you the first column was the name of an item and the second was the price in whole dollars, then asked you to make a table and import it into SQL Server. You could make a table that looked like:&lt;/P&gt;
&lt;P&gt;CREATE TABLE Stuff&lt;BR&gt;(&lt;BR&gt;name VARCHAR(MAX),&lt;BR&gt;price VARCHAR(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;Later when people when to use the table they could issue a query like this:&lt;/P&gt;
&lt;P&gt;SELECT name, CAST(price AS INT) FROM Stuff&lt;/P&gt;
&lt;P&gt;Of couse if one of the files you imported had a line like:&lt;/P&gt;
&lt;P&gt;hose, XVII&lt;/P&gt;
&lt;P&gt;the query would get an error at runtime when it ran. The problem here was that the data was stored as something that it wasn't. Storing xml as a string or binary is just like store an INT as text. When you insert something into an xml column SQL Server makes sure that is really is xml or rejects it, so you will not have any errors if you happen to make use of some of the xml functionality SQL Server 2005 provides. Even if you don't do make any use of the xml processing functionality in SQL Server, the client that reads the xml as text you've stored will get the error. It's usually best to catch errors as early as possible, especially if the client thinks your server made the error :-).&lt;/P&gt;
&lt;P&gt;OK, now lets look at how much space things really take up. To do this we are going to make four tables that store xml each in a different way, fill them up with a bunch of xml then see how big they are. You can down load the code for trying these experiments yourself from &lt;A href="http://www.pluralsight.com/dan/samples/whatcostxml.zip"&gt;http://www.pluralsight.com/dan/samples/whatcostxml.zip&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;-- save as binary&lt;BR&gt;CREATE TABLE XmlBinSizeTest&lt;BR&gt;(&lt;BR&gt;data VARBINARY(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;--save as varchar&lt;BR&gt;CREATE TABLE XmlTextSizeTest&lt;BR&gt;(&lt;BR&gt;data VARCHAR(MAX)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;--save save as xml with a schema&lt;BR&gt;CREATE TABLE XmlSchemaSizeTest&lt;BR&gt;(&lt;BR&gt;data xml(XmlSizeSchema)&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;-- save as xml without a schema&lt;BR&gt;CREATE TABLE XmlSizeTest&lt;BR&gt;(&lt;BR&gt;data xml&lt;BR&gt;)&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;Now we want to fill these tables up with a big walop of xml. When I need some static string or xml for testing I usually wrap it in a function so I don't have to put the literal text in my SQL expressions. &lt;/P&gt;
&lt;P&gt;CREATE &lt;BR&gt;--ALTER&lt;BR&gt;FUNCTION XmlSizeTestData4()&lt;BR&gt;RETURNS VARCHAR(MAX)&lt;BR&gt;AS&lt;BR&gt;BEGIN&lt;BR&gt;RETURN &lt;BR&gt;'&lt;EMPLOYEES XMLSchema-instance? 2001 www.w3.org http: xmlns:xsi="&lt;A href="&gt;http://www.w3.org/2001/XMLSchema-instance&lt;/A&gt;"&amp;gt;&lt;BR&gt;&amp;nbsp; &lt;E title="Production Technician - WC60" ModifiedDate="2004-07-31T00:00:00" rowguid="AAE1D04A-C237-4974-B4D5-935247737718" CurrentFlag="1" SickLeaveHours="30" VacationHours="21" SalariedFlag="0" HireDate="1996-07-31T00:00:00" Gender="M" MaritalStatus="M" BirthDate="1972-05-15T00:00:00" ManagerID="16" LoginID="adventure-works\guy1" ContactID="1209" NationalIDNumber="14417807" EmployeeID="1"&gt;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;C ContactID="1209" mailto:guy1@adventure-works.com? EmailAddress="&lt;A href=" LastName="Gilbert" MiddleName="R" FirstName="Guy" NameStyle="0"&gt;guy1@adventure-works.com&lt;/A&gt;" EmailPromotion="0" Phone="320-555-0195" PasswordHash="1196EB7D5425B281CACDCDD2F60F52D9689D9E49" PasswordSalt="Lanmhoo=" rowguid="D366A33A-8EDE-42BD-BF79-3E7FB9713FE1" ModifiedDate="1996-07-24T00:00:00"/&amp;gt;&lt;BR&gt;&amp;nbsp; &lt;/E&gt;&lt;BR&gt;&lt;?-- a few hundred more E's here --&gt;&lt;BR&gt;&amp;nbsp;&lt;/EMPLOYEES&gt;'&lt;BR&gt;END &lt;/P&gt;
&lt;P&gt;Then a little batch to fill the tables:&lt;/P&gt;
&lt;P&gt;DECLARE @t VARCHAR(MAX)&lt;BR&gt;SET @t = dbo.XmlSizeTestData4();&lt;BR&gt;DECLARE @index INT&lt;BR&gt;SET @index = 100&lt;BR&gt;WHILE @index &amp;gt; 0&lt;BR&gt;BEGIN&lt;BR&gt;SET @index = @index - 1&lt;BR&gt;INSERT INTO XmlTextSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlSchemaSizeTest VALUES (@t);&lt;BR&gt;INSERT INTO XmlBinSizeTest VALUES (CAST(@t AS VARBINARY(MAX)));&lt;BR&gt;END&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;We can use sys.allocation_units to find out how much space the tables take up. It takes a few joins to make this work off of table names...&lt;/P&gt;
&lt;P&gt;SELECT o.name AS table_name, au.type_desc, au.used_pages&lt;BR&gt;FROM sys.allocation_units AS au&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JOIN sys.partitions AS p ON au.container_id = p.partition_id&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; JOIN sys.objects AS o ON p.object_id = o.object_id&lt;BR&gt;WHERE o.name in (N'XmlTextSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlSchemaSizeTest',&lt;BR&gt;&amp;nbsp;N'XmlBinSizeTest')&lt;/P&gt;
&lt;P&gt;In SQL Server 2005 large objects, i.e. things like xml and VARCHAR(MAX) overflow in to supplementary pages outside of the pages for the table itself. This query tells us about both the pages used in the table and the extra overflow pages. Note that DBCC CHECKTABLE won't give you the right answer, because it won't tell you about the overflow pages. Running this query produces:&lt;/P&gt;
&lt;P&gt;table name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type_description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; used pages&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2802&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2802&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 4226&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp;2907&lt;/P&gt;
&lt;P&gt;The numbers tell an interesting story. First of all no suprise, storing text as binary takes the same amount of space as storing it as VARCHAR(MAX). However storing xml without a schema takes only about 5% more space than storing it as VARCHAR(MAX) even though the xml is being stored as UTF-16, not single byte characters as VARCHAR does. How can this be?&lt;/P&gt;
&lt;P&gt;Let's use different xml data that really exacerbates the difference between storing xml as text and storing xml as xml. Here is another function that cobbles up some xml on the fly:&lt;/P&gt;
&lt;P&gt;CREATE &lt;BR&gt;FUNCTION XmlTestData1()&lt;BR&gt;RETURNS VARCHAR(MAX)&lt;BR&gt;AS&lt;BR&gt;BEGIN&lt;BR&gt;DECLARE @t VARCHAR(MAX)&lt;BR&gt;SET @t = CAST('&lt;?xml:namespace prefix = a /&gt;&lt;a:Simple  VARCHAR(MAX))&lt;BR AS ? xmlns:a="urn:stuff"&gt;+ REPLICATE(CAST(' ' AS VARCHAR(MAX)), 10000)&lt;BR&gt;+ '&amp;gt;abc&lt;/a:Simple &gt;'&lt;BR&gt;RETURN @t&lt;BR&gt;END&lt;/P&gt;
&lt;P&gt;It's basically inserting a lot of ignorable space into the the xml. A fragment of the xml it generates looks like:&lt;/P&gt;
&lt;P&gt;&lt;a:Simple                                                                                                                 &gt;&lt;/P&gt;
&lt;P&gt;Actually it has a lot more spaces than that. To try this next you should drop all the tables and recreate them, then use the batch that fill the tables, but change the source of the xml to dbo.XmlTestData1().&amp;nbsp; After you run that batch that finds the sizes you see something like:&lt;/P&gt;
&lt;P&gt;table name&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; type_description&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; used pages&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlBinSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 252&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;BR&gt;XmlTextSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 252&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; IN_ROW_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&lt;BR&gt;XmlSchemaSizeTest&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IN_ROW_DATA&amp;nbsp; 3&lt;BR&gt;XmlSizeTest&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LOB_DATA&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 0&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;It looks like saving this xml as text or binary takes up about two orders of magnitude more space than just saving it as xml!&lt;/P&gt;
&lt;P&gt;When SQL Server saves xml it need not save it as literal text, and it doen't. It doesn't have to save the the "pointies" and it doesn't have to save whitespace that doesn't matter. And most of the the time the whitespace between consecutive opening tags doesn't matter.&amp;nbsp; In other words SQL Server 2005 goes out of its way to efficiently store xml.&lt;/P&gt;
&lt;P&gt;So it not a slam dunk to figure out which way of storing xml takes up the least amount of space. The point here isn't that xml will always take about the same or less as a corresponding VARCHAR(MAX),&amp;nbsp; because sometimes the VARCHAR(MAX) or VARBINARY(MAX) will take up less space. But you will probably find that the difference isn't as great as you might suspect. In any case this is a database we are talking about, you should store xml as xml.&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;Dan&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&amp;nbsp;&lt;/P&gt;
&lt;P&gt;&lt;BR&gt;&amp;nbsp;&lt;BR&gt;&lt;/P&gt;&lt;/a:Simple                                                                                                                 &gt;&lt;img src ="http://pluralsight.com/blogs/dan/aggbug/32814.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>Dan Sullivan</dc:creator><title>Evaluating String Arithmetic Expressions in SQL Server 2005</title><link>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx</link><pubDate>Thu, 27 Jul 2006 10:50:00 GMT</pubDate><guid>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx</guid><wfw:comment>http://pluralsight.com/blogs/dan/comments/32597.aspx</wfw:comment><comments>http://pluralsight.com/blogs/dan/archive/2006/07/27/32597.aspx#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://pluralsight.com/blogs/dan/comments/commentRss/32597.aspx</wfw:commentRss><trackback:ping>http://pluralsight.com/blogs/dan/services/trackbacks/32597.aspx</trackback:ping><description>&lt;P&gt;I saw a wish list blogged at http://omnibuzz-sql.blogspot.com/2006/07/next-version-of-sql-server-wish-list.html. It wanted a sql function that would evaluate an string arithmetic expression in the next version of SQL Server. It turns out that function is already available, sort of, in T-SQL on SQL Server 2005. Here is an example: &lt;/P&gt;&lt;PRE&gt; 
DECLARE @x xml
DECLARE @v FLOAT
SET @x = ''
SET @v = @x.value('(1 + 4) div 3', 'FLOAT')
SELECT @v
 ----------------
1.666666
&lt;/PRE&gt;
&lt;P&gt;Unfortunately the following will not work...&lt;/P&gt;&lt;PRE&gt;DECLARE @x xml
SET @x = ''
DECLARE @e VARCHAR(MAX)
DECLARE @v FLOAT
SET @e = '(1 + 4) div 3'
SET @v = @x.value(@v, 'FLOAT')
SELECT @v
&lt;/PRE&gt;
&lt;P&gt;... because value and other XQuery functions require literal XQuery expressions. But all is not lost!&lt;/P&gt;
&lt;P&gt;We can weave together a number of .NET technologies in SQL Server 2005 and make our own EvaluateArithmethicExpression scalar valued user defined function. All we have to do is add some basic XPath to&amp;nbsp;an XPathNavigator and regular expressions. XPath itself has builtin support for evaluating arithmetic expressions, albeit some of the operations are names rather than single characters, but a regular expression can clean that up. At the end of this article is a C# function that implements a CLR based user defined function that evaluates string arithmetic expressions. Here is an example of using this function: &lt;/P&gt;&lt;PRE&gt;&lt;FONT color=#0000ff size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;'(50.1234 * 1 + 2)/3'&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;DECLARE&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000 size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt;FLOAT&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000&gt;DECLARE&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;VARCHAR&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;MAX&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT size=2&gt;'(50.1234 * 1 + 2)/3'&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;@e&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt; @f&lt;/FONT&gt;&lt;/P&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @e &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT size=2&gt;'((50.1234 * 1 + 2)/3) % 5'&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SET&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT color=#000000&gt;&lt;FONT size=2&gt; @f &lt;/FONT&gt;&lt;FONT size=2&gt;=&lt;/FONT&gt;&lt;FONT size=2&gt; dbo&lt;/FONT&gt;&lt;FONT size=2&gt;.&lt;/FONT&gt;&lt;FONT size=2&gt;EvaluateArithmethicExpression&lt;/FONT&gt;&lt;FONT size=2&gt;(&lt;/FONT&gt;&lt;FONT size=2&gt;@e&lt;/FONT&gt;&lt;FONT size=2&gt;)&lt;/P&gt;&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;P&gt;&lt;FONT color=#000000&gt;SELECT&lt;/FONT&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&lt;FONT color=#000000&gt; @f&lt;/FONT&gt;&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT size=2&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;&lt;FONT size=2&gt;&lt;FONT size=1&gt;&lt;P&gt;&lt;FONT color=#000000&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;17.3744666666667&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;(1 row(s) affected)&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;&lt;/FONT&gt;&amp;nbsp;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#000000 size=2&gt;----------------------&lt;/FONT&gt;&lt;/P&gt;&lt;P&gt;&lt;FONT color=#00