Adios SQLJunkies

Thanks for getting me started but it's time to close the door.

 Allen
 




Cross-posted from SQLBlog! - http://www.sqlblog.com


Change Tracking and Change Data Capture

Tonight's meeting of the Ohio North SQL Server Users Group will include the Detroit SQL Server Users Group as well. Here's the agenda:

5:30 - 6:00 PM - Early Arrival/PASS Intro and Presentation Tips and Techniques - Allen White
6:00 - 6:10 PM - Get Pizza and Settle/Coordinate Live Meeting with Detroit
6:15 - 7:15 PM - Bill Wolohan - SQL Server 2008 Change Tracking and Change Data Capture
7:15 - 7:30 PM - Q/A & Wrap Up

Go to http://www.bennettadelson.com/sql and click on the link "reserve your seat".

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Start PowerShell in Management Studio

In the February CTP of SQL Server 2008 a new feature has appeared in SQL Server Management Studio. You now have the ability to right-click on an object in the Object Explorer window and open up a PowerShell window. In this window you can navigate the database structures much like you can a disk file subsystem, or like you can navigate the registry in PowerShell.

So, let's say you right-click on your server name and open up a PowerShell window. You can then issue the command:

cd Databases/AdventureWorks

Now you're pointing to the AdventureWorks database. Using PowerShell in this way allows you to browse the Server Management Objects (SMO) tree structures. (By the way, you'll have to remember to use proper case when browsing the SMO objects. They are case-sensitive in this environment.) Now, you can type this:

dir Tables

What returns is a list of the tables in AdventureWorks. It's kind of neat idea, and definitely fun to play with. I haven't found a productive use for it yet, but it's too new for me to make a judgement yet. I like it, but I don't yet know why.

Let me know what you think.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Ohio North SQL Server Users Group February Meeting - Moved to February 21

Normally our meetings are on the second Thursday of the month, but this year that falls on Valentine's Day, so we've moved the meeting to Thursday, February 21.

Here's the agenda for the February meeting of the Ohio North SQL Server Users Group:

5:00 - 5:45 PM - PASS Chapter Acceptance/SQL 2008 Heroes Happen Here: Presentation Tips and Tricks - Allen White

The Ohio North SQL Server Users Group is now officially a PASS chapter. We'll present the PASS Welcome presentation, followed by a "Heroes Happen Here" presentation on how to deliver quality presentations, because you have something interesting to share with the rest of us, and this presentation will show you some of the ways the top presenters get their message across effectively.

5:45 - 6:00 PM - Dinner

6:00 - 7:00 PM - Change Tracking and Change Data Capture in SQL Server 2008 - Bill Wolohan, Bennett-Adelson

SQL Server 2008 has 2 new features that can notify you when your data changes. Both are easy to set up and do not require you to create triggers on each table. Change Tracking requires less overhead, but Change Data Capture gives you more detail. This presentation will explain how each of these features works and when you might use one or the other.

Registration: Go to http://www.bennettadelson.com/sql and click on the link "reserve your seat".

I look forward to seeing you there!

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


PowerShell, SQL Server and Large Resultsets

Last week I posted about using PowerShell and SQL Server together, and I used a SQLDataAdapter and populated a DataTable with the results. This approach is fine if what you're doing returns a reasonably small resultset, but what if you're returning millions of rows. In this case a DataTable isn't too practical, and a forward-only DataReader is the best way to go.

In my company we have some legacy applications which need data from a large relational database. The preferred format is a comma-separated data file with the columns quoted (to handle whatever imbedded data might be found). I needed to supply separate text files, with subscriber lists, with the list name as the file name of the text file, and the first row a list of the column names of the data.

The first thing I need to do is open a connection to the database.

#extract_subs.ps1
#This script will extract information for subscribers
#and write the results into text files named with the list name.

$cn = new-object System.Data.SqlClient.SqlConnection("Data Source=MyServer/MyInstance;Integrated Security=SSPI;Initial Catalog=Subscribers");
$cn.Open()

Once the connection is open I build a SqlCommand object with the query to be processed. (To prevent collisions with the application using the database, and because dirty reads are acceptable to this process, I use the NOLOCK hint.) The results are ordered by list name and email address - the list name order is important because I'm creating separate files based on that list name. I also need to set the CommandTimeout property of the SqlCommand object to 0 to prevent PowerShell from timing out waiting for results - as I mentioned, there are millions of rows coming back.

$q = "SELECT	List,"
$q = $q + "	EmailAddr,"
$q = $q + "	FullName,"
$q = $q + "	UserID,"
$q = $q + "	CompanyName,"
$q = $q + "	Address1,"
$q = $q + "	CityName,"
$q = $q + "	StateProvince,"
$q = $q + "	PostalCode,"
$q = $q + "	Country,"
$q = $q + "	Telephone,"
$q = $q + "  FROM .[dbo].[SubList] WITH (NOLOCK)"
$q = $q + "  ORDER BY [List], [EmailAddr]"
$cmd = new-object "System.Data.SqlClient.SqlCommand" ($q, $cn)
$cmd.CommandTimeout = 0

Now I can use the ExecuteReader method of the SqlCommand object to return a SqlDataReader object. I'll also initialize a variable I'll use to know when the List name changes, so I can write a header row to the output file. I then use a while loop to iterate through the results. The DataReader Read() method returns true if it has data and false if it is done, so using that method in the while condition returns a row and tests for the end of the data.

$dr = $cmd.ExecuteReader()
$l = ""

while ($dr.Read()) { 

There are a number of methods to return column data from a DataReader, but GetValue returns a datatype appropriate to the data returned, so I use that method pretty consistently. I return that data to variables for clarity.

	$List = $dr.GetValue(0)
	$EmailAddr = $dr.GetValue(1)
	$FullName = $dr.GetValue(2)
	$UserID = $dr.GetValue(3)
	$CompanyName = $dr.GetValue(4)
	$Address1 = $dr.GetValue(5)
	$CityName = $dr.GetValue(6)
	$StateProvince = $dr.GetValue(7)
	$PostalCode = $dr.GetValue(8)
	$Country = $dr.GetValue(9)
	$Telephone = $dr.GetValue(10)

Now that I have the row data in variables I test to see if the List name changed. If it did I build a header row. I then use the new List name and concatenate the ".txt" extension onto it for the output file name, then pipe the header row to the out-file cmdlet. I specify -append in case the file from the previous run still exists (it shouldn't), and I specify -encoding ASCII because by default PowerShell will create a Unicode format file. I also set my list change variable to the name of the list.

	if ($List -ne $l) {
		$r = """List"",""EmailAddr"",""FullName"",""UserID"
		$r = $r + """,""CompanyName"",""Address1"
		$r = $r + """,""CityName"",""StateProvince"",""PostalCode"
		$r = $r + """,""Country"",""Telephone"""
		
		$f = $List + ".txt"
		$r | out-file $f -append -encoding ASCII
		
		$l = $List
		}

Now I build the row data into its comma-separated string and write out the data using the out-file cmdlet.

	$r = """" + $List + """,""" + $EmailAddr + ""","""
	$r = $r + $FullName + """,""" + $UserID + ""","""
	$r = $r + $CompanyName + """,""" + $Address1 + ""","""
	$r = $r + $CityName + """,""" + $StateProvince + """,""" + $PostalCode + ""","""
	$r = $r + $Country + """,""" + $Telephone + """"
	
	$f = $List + ".txt"
	$r | out-file $f -append -encoding ASCII
	
	}

The last thing we need to do is to close the DataReader and the Connection.

$dr.Close()
$cn.Close()

There are many different ways to accomplish a task like this. PowerShell gives us one more tool in the toolbox to get tasks done.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Using PowerShell and SQL Server Together

I've mentioned before my company manages trade shows, and we've got a series of web sites managed by an application which uses a different SQL Server database for each site, with a master database (I'll call it Global, to differentiate it from SQL Server's master database. Well, we have a number of shows which have multiple show locations, and these use a parent-child set of databases, where information pertaining to all the shows is in the parent database, and then information to each specific location is in the child database.

There is information that is important to both parent and child, and that's kept in the parent database, and we have views in the child which return the data from the parent database that's specific to the child. The problem is when the application is changed, and the views need to be modified. Each child site has a technically different view based on it's show ID and the name of the parent database.

Yesterday I had to make such a change, and I decided a PowerShell script was the best way to approach the problem. Now, there's a table defining all the parent/child relationships in the Global database, so the first step is to query that table to get the necessary information:

#build_exibitors_view.ps1
#This script will recreate the 'exhibitors' view in all child databases

$cn = new-object system.data.SqlClient.SqlConnection("Data Source=MyServer\MyInstance;Integrated Security=TRUE;Initial Catalog=Global");
$ds = new-object "System.Data.DataSet" "dsChildSites"
$q = "SELECT [childShowID]"
$q = $q + "      ,[parentDBName]"
$q = $q + "      ,[childDBName]"
$q = $q + "  FROM [Global].[dbo].[ParentChild]"
$da = new-object "System.Data.SqlClient.SqlDataAdapter" ($q, $cn)
$da.Fill($ds)

Now, once the DataSet is populated it's time to loop through the results building the new view for each child database. I create a DataTable from the DataSet, then use the FOREACH-OBJECT cmdlet to step through the results, and then create variables for the values returned for each iteration.

$dtChild = new-object "System.Data.DataTable" "dsChildSites"
$dtChild = $ds.Tables[0]
$dtChild | FOREACH-OBJECT { 
	$pDB = $_.parentDBName
	$cDB = $_.childDBName
	$cshowID = $_.childShowID

Inside the loop I need to connect to each child database and first drop the existing view. I concatenate the name of the child database to the connection string for the SqlCommand object.

	$cn = new-object System.Data.SqlClient.SqlConnection("Data Source=MyServer\MyInstance;Integrated Security=TRUE;Initial Catalog=" + $cDB)
	$cn.Open()
	$sql = "IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[exhibitors]')) DROP VIEW [dbo].[exhibitors]"
	$cmd = new-object "System.Data.SqlClient.SqlCommand" ($sql, $cn)
	$cmd.ExecuteNonQuery() | out-null

Once the existing view is deleted I can create the new one. Here I concatenate the name of the parent database in the FROM clause, and filter the resultset based on the showID of the child show.

	$sql = "CREATE VIEW [dbo].[exhibitors]"
	$sql = $sql + " AS"
	$sql = $sql + " SELECT exhibID, name, description"
	$sql = $sql + " FROM " + $pDB + ".dbo.exhibitors AS e"
	$sql = $sql + " WHERE (showID = " + [string]$cshowID + ")"

Now that the view has been built for the child database I can execute the query to create it.

	$cmd2 = new-object "System.Data.SqlClient.SqlCommand" ($sql, $cn)
	$cmd2.ExecuteNonQuery() | out-null
	$cn.Close()

	}

(Note that the actual view used is much more complicated than this, but I wanted to share the technique. Rather than use an editor to make changes to a couple of dozen views across as many databases I used PowerShell to automate the process and made the changes in a few seconds.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Create Agent Jobs to run PowerShell Scripts

Actually, let's use PowerShell and SMO to create an Agent job which will run a PowerShell script. (Or is that a circular reference?)

I've created a number of PowerShell scripts which automate database management processes, and I wanted to be able to run them from an Agent job. The trick, of course, is to be able to get to the PowerShell environment from within Agent. It's not as hard as I thought. Let's say I have a script to back up my SQL Server user databases, and that script is in C:\Admin and it's called backupdb.ps1. The command from a Windows command window is this:

 

powershell "& c:\Admin\backupdb.ps1"

 

Try it - it's a lot easier than I'd thought. Once you have that little tidbit it's just a matter of setting up the job to execute that exact command line from within an Agent job. First, we need to load the SMO dll, and connect to the SQL Server where we want the job to run.

 

[System.Reflection.Assembly]::LoadWithPartialName('Microsoft.SqlServer.SMO') | out-null
$s = new-object ('Microsoft.SqlServer.Management.Smo.Server') 'MyServer\MyInstance'

 

Next we instantiate an Agent Job object, set its properties, and create it.

 

$j = new-object ('Microsoft.SqlServer.Management.Smo.Agent.Job') ($s.JobServer, 'FullBackup')
$j.Description = 'Backup User Databases'
$j.Category = '[Uncategorized (Local)]'
$j.OwnerLoginName = 'sa'
$j.Create()

 

Once the job exists we can create the step to execute the PowerShell script. By default the step default SubSystem is TransactSQL, so we have to specify that we want the command shell, so we set the JobStep.SubSystem property to 'CmdExec' and the command property to the powershell command we looked at earlier.

 

$js = new-object ('Microsoft.SqlServer.Management.Smo.Agent.JobStep') ($j, 'Step 01')
$js.SubSystem = 'CmdExec'
$js.Command = 'powershell "& C:\Admin\backupdb.ps1"'
$js.OnSuccessAction = 'QuitWithSuccess'
$js.OnFailAction = 'QuitWithFailure'
$js.Create()

 

The job needs to be scheduled so we create a JobSchedule object and set its properties. The time of day properties require a System.Timespan object, so we have to instantiate a couple of those, one for the start time and one for the end time, but that's pretty straightforward.

 

$jsch = new-object ('Microsoft.SqlServer.Management.Smo.Agent.JobSchedule') ($j, 'Sched 01')
$jsch.FrequencyTypes = 'Daily'
$jsch.FrequencySubDayTypes = 'Once'
$startts = new-object System.Timespan(2, 0, 0)
$jsch.ActiveStartTimeOfDay = $startts
$endts = new-object System.Timespan(23, 59, 59)
$jsch.ActiveEndTimeOfDay = $endts
$jsch.FrequencyInterval = 1
$jsch.ActiveStartDate = get-date
$jsch.Create()

 

Once this is done the job will run every day at 2am and execute the backupdb.ps1 script we created for this purpose.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Automating SQL Server Administrative tasks with PowerShell

I'll be presenting this topic at the January meeting of the Ohio North SQL Server Users Group on Thursday, January 10, 2008. Here's the Agenda:

5:00 - 5:45 PM - Early Arrival/Discussions around SQL Server Management Tools - Bruce Szabo
Are you new to SQL Server? Are you looking to start building your skills set but aren’t sure where to turn. We are going to use the early time to have a very open dialogue around various SQL Tools and Techniques to build your skill set. We are hopeful this will be an open dialogue lead by various User Group members to try and build the skill set of new SQL users. This month we will tackle some of the Management tools Including SQL Server Management Studio (SSMS), SQL Profiler and Database Tuning Advisor.

5:45 - 6:00 PM - Dinner

6:00 - 7:00 PM - Automating SQL Server Administrative tasks with PowerShell - Allen White
Most administrative functions consist of repetitive sequences of tasks which just take time. PowerShell is a powerful scripting environment which allows an administrator to automate those repetitive tasks, providing better results to the organization while saving the administrator time, and ensuring that the tasks get done properly every time. This session will give an introduction to PowerShell and demonstrate a number of scripts which help SQL Server administrators effectively manage their environments.

7:00 - 7:30 PM - Wrap up / Ongoing discussions

To register, go to www.bennettadelson.com/sql and click on the link "reserve your seat".

I'd love to see you there!

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


[OT] Daddy's Little Girl

Today at 4:45PM EST my daughter is getting married.

Her fiance is in the Army and will fly to Frankfurt, Germany for assignment as a Combat Engineer. They're getting married today so she can join him on base in Germany as soon as he arranges housing for the two of them. The big wedding she's always wanted will wait until next year, when they'd planned it originally, but unless they're married she can't join him. So my beautiful darling baby girl is getting married today.

I wish them the best of luck together. He's a good kid, and they adore each other.

While she was growing up I watched her in all her various activities. She was in gymnastics for a while, and Girl Scouts, and Rec Center Basketball, and something called "Odyssey of the Mind" - a kind of creative, problem-solving, improvisational competition they had for kids from about 8 to 16 years old. She would remind me of her mother in many ways, of me in many others (very scary), yet has always been a unique and wonderful individual herself.

When she was in Fifth and Sixth grades she took an interest in the special needs kids in her school, and gave up her recess time to help out in their classroom. She earned an award as the VFW's State of Ohio Volunteer of the Year when she was in middle school for the work she did. In High School she was active in Band, Orchestra, Theatre, National Honor Society, and other groups I don't remember now.

She went to college to study Special Education, never having lost that love for working with special needs kids. While in college she joined Phi Mu Sorority and actively participated in the Dance Marathon every year, which raises thousands of dollars for the Children's Miracle Network. She graduated last May with a Bachelor's Degree in Education. She's been working as a substitute this year, while her fiance was in Basic Training.

And today she's getting married, and I want to cry. I'm so proud of her.

I hope you all, like her mother and me, wish them the best for the future.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Accessing WMI Information Using SMO

While doing some research on SMO I found a couple of very handy features of the WMI accessibility features of SMO. I'm playing with PowerShell because it's easy to browse the SMO objects in that environment, but here are a couple of tips I found useful.

We need to load the SMO assembly into PowerShell - here's the command:

[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")  | out-null

First, when building a script to automate processes I don't want to have to hard code the names of the instances on a given server. How do I find out what instances are installed? We need to instantiate a ManagedComputer object. Once that's done we simply query the ServerInstances collection and have the information we need.

$m = new-object ('Microsoft.SqlServer.Management.Smo.WMI.ManagedComputer') 'MyServer'
$m.ServerInstances | SELECT Name

You can then pipe that into a for-each loop to perform whatever maintenance you wish on each instance.

Another useful piece of information is the IP port number used by a given instance of SQL Server. To get this we need to follow a couple of steps, first getting the specific instance, then drilling down into the ServerProtocols collection.

$i=$m.ServerInstances['MyInstance']
$p=$i.ServerProtocols['Tcp']

Once we've grabbed the TCP protocol object we need to reference 'IPAll' from the IP Address collection, because the port number is set independent of the specific IP address for the server. Here's how we get that.

$ip=$p.IPAddresses['IPAll']

Once we've captured that we just need to query the value of the 'TcpDynamicPorts' property and we have the port number for that instance.

$ip.IPAddressProperties['TcpDynamicPorts'].Value

When applications run into difficulties connecting to a given server it's important to know the port number so you can set up the client connectivity properly. Having a script to return that information easily will save you time.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Virtual PC - Making Life Easier

When I became an MCT (Microsoft Certified Trainer) I started using Virtual PC's as part of the classroom demonstrations and labs. Until then I'd used them on occasion, but they never struck me as very useful. In preparing for the classes and presenting in user groups and conferences I learned to appreciate how nice it is to isolate a demo or test environment from my "real" work environment.

Last year I got an MSDN subscription, which gave me a variety of operating system installation media, which I'm allowed to use for development purposes. I built a VPC image for Windows XP SP2 and for Windows Server 2003 R2, registered each one, and used Windows Update to bring them to current patch levels. Once that was done I shut down the VPC and copied the image files to separate directories (leaving the original files in place.

Now, whenever an issue comes up requiring a particular version of SQL Server (be that SQL 2000, SQL 2005 or SQL 2008, and specific to whatever patch level I need, I can copy the "backup" image files to the primary folder, start up the VPC image I need, then install whatever version of SQL Server I need to get some work done.

Today, for example. A while back I had a job on a production server that was no longer needed (per the business user), so I dropped it. Today I found out it was needed, but the backup from when that job was dropped was from SQL 2005 SP1. (I know, I should have scripted it before I dropped it.) In any event, by using my virtual machine I was able to install SQL 2005 SP1 and restore the msdb database. I was then able to retrieve the job and it's back and everyone's happy.

It's also a great environment for testing the various CTP releases of SQL Server 2008 and Visual Studio 2008. Instead of uninstalling previous versions and cleaning up registry entries, just copy over a fresh VPC image and I'm off to the races.

Good times.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


Very Large Database Maintenance

I've got a third-party app that uses a SQL Server 2005 database. The database has grown to 190GB, with 150GB of that in one table (116M rows). I've been trying to get the greenlight to partition this table since June, and finally got it this weekend.

I wasn't able to start my process until Friday at noon, and had space on my backup drive to export the table data. (By law I have to keep two years worth of data in that table for auditing purposes - it has less than a month over that now.) I used bcp native mode to export the data and it took from Friday noon until 9am this morning.

Once the data was exported I dropped the table. (Until this point all tables are in the PRIMARY filegroup, and it has three physical files in the group. Now I have two ndf files of 80-90GB each that have 16-18GB of data, the rest free. I need that space to allocate for the partition files, so I need to shrink the files.

I started the shrink (dbcc shrinkfile - sorry Paul [Randal], I know, but I need the space) at 9am. After 8 hours I decided it was not the correct solution.

I then built a new database to temporarily house the data so I could drop and recreate the application database with appropriate file sizes. The copy, using a combination of "select into" for small tables, and SSIS data transfer for the larger tables, took over 11 hours. By this time it was after 4:30am and the start of business was closing in rapidly. I knew I didn't have time to build the new database and get the data back in from the temporary database, so I abandoned the task. The import of the large table was causing enough performance problems that we've decided (in a meeting with the business owners) to restore the full database from the backup I have from before I dropped the large table.

The problem we have with the database still exists. That table grows by over 8 million rows per month, increasing by as much as 10 percent each month. All tables are in the PRIMARY file group so I have to backup data that stays constant every night.

The point really is that size makes a real difference in how to solve a problem. Solutions that work well on databases of 1 or 2 GB aren't at all appropriate for databases approaching 200GB. I have a couple of ideas on how I could have done some things differently, but of course they're based on the experience I gained in attempting to perform this maintenance.

Hindsight isn't 20/20, but it's close.

Allen




Cross-posted from SQLBlog! - http://www.sqlblog.com


SQL Server 2008 - Is It Time Yet?

At the PASS Conference in Denver the question was frequently asked - is it time yet for another release of SQL Server? Microsoft execs have said repeatedly at TechEd and at PASS that they've "heard us loud and clear" that 5 years is too long between releases. As a DBA with a company that maintains (and pays handsomely for) Software Assurance I understand that the 5 year window is too long.

On the other hand, I still have to support SQL Server 2000 because many of the applications we run are third-party applications, and the vendor(s) aren't yet ready to support SQL Server 2005. (I've even tested apps against SQL Server 2005 to show them it works fine, but they won't support it, so I can't upgrade.)

There are features in SQL Server 2008 that look like they'll save my company some significant time and effort. Things like Change Data Capture and the Dynamic Management Framework are really exciting. What I'm wondering is if it might be worth holding off a little bit to make certain that these work as they should work. It's hard to tell at this point, because of the slow release of CTP's for Katmai. (CTP5 was being called the "October CTP" at PASS. I even teased Paul Mestemaker about "October" meaning October 31. It's now November and CTP5 doesn't appear close to ready for public consumption.)

I'm well aware of the urgency at Microsoft on getting the code done and into the test cycle so the 2nd Quarter RTM schedule is maintained, but I also know that some features that should be in SQL Server 2008 won't be there because