Dynamic Pivot in TSQL

November 6th, 2009 by Lee Everest

Making 'dynamic' even more dynamic

In this month’s SQL Server Magazine (11/2009) an article was featured on the concept of dynamic pivot.  This was of interest to me because, since I peruse the stats on how people arrive at this site, I have seen several searches from Google! looking for dynamic pivot examples.  Truth be told, I haven’t run into a situation to dynamically pivot anything in a production environment, so I’m not sure what all of the drooling is all about. But, since there is a demand, I thought I’d throw my .02 cents in, and I’ll use SQLMag’s example as my base reference.

The author did a good job with the write-up - my hat is off to him. However, glancing at the article one can see that concept could be carried further…very little is “dynamic” about his code, so l invite you to look at it and mine and decide for yourself. First, let’s set up the tables and data. I have included both his [1] DDL as well as that from another blog posting elsewhere on this site (to demonstrate that it will work in a dynamic fashion, all things being equal).

 

DROP TABLE DemoTable

GO

CREATE TABLE DemoTable

(

 AssignmentName VARCHAR(255),

 StudentName VARCHAR(255),

 Grade INT

)

GO

INSERT INTO DemoTable

 Values('Assignment1', 'John Smith', 70)

INSERT INTO DemoTable

 Values('Assignment1', 'Jane Smith', 80)

INSERT INTO DemoTable

 Values('Assignment1', 'Paul Smith', 75)

INSERT INTO DemoTable

 Values('Assignment2', 'John Smith', 50)

INSERT INTO DemoTable

 Values('Assignment2', 'Paul Smith', 65)

INSERT INTO DemoTable

 Values('Assignment3', 'Jane Smith', 70)

GO

 

 

 

 

DROP TABLE PlantMetrics;

GO

CREATE TABLE PlantMetrics

(PlantID int

,TransDt datetime

,Metric char (4)

,Value nvarchar (10)

,CONSTRAINT pk_plmt PRIMARY KEY CLUSTERED (PlantID, TransDt, Metric)

);

GO

INSERT PlantMetrics

VALUES (1,'7/1/09', 'MHRS', '24')

,(1,'7/1/09', 'WTHR', 'Sunny')

,(1,'7/1/09', 'TEMP', '88')

,(2,'7/1/09', 'TEMP', '94')

,(3,'7/2/09', 'DOWN', '7')

GO

 

 

 

 

Below is his code [1]:

DECLARE @SQL as VARCHAR (MAX)

DECLARE @Columns AS VARCHAR (MAX)

 

SELECT @Columns=

       COALESCE(@Columns + ',','') + QUOTENAME(AssignmentName)

FROM

(

       SELECT DISTINCT AssignmentName

       From DemoTable

) AS B

ORDER BY B.AssignmentName

 

SET @SQL='

WITH PivotData AS

(

       SELECT

         AssignmentName,

         StudentName,

         Grade

       FROM DemoTable

)

SELECT

       StudentName,

       ' + @Columns + '

FROM PivotData

PIVOT

(

       SUM(Grade)

       FOR AssignmentName

       IN (' + @Columns + ')

) AS PivotResult

ORDER BY StudentName'

 

EXEC (@SQL)

 

A few things to point out: don’t use SUM for the aggregate. MIN or MAX works better, because sometimes the value that is the subject of the pivot is not an integral (integer, numeric, etc.) type.  Also, I see some columns that could be replaced here to make the entire code dynamic.  Finally, here is my code:

 

DECLARE @SQL as VARCHAR(MAX)

DECLARE @NewColumnList AS VARCHAR(MAX)

DECLARE @table table (SourceColumnNames varchar (100))

DECLARE @TargetTable varchar (100)

-- Column names

DECLARE @PivotColumn varchar (100)

DECLARE @AnchorColumn varchar (100)

-- Pivot

DECLARE @PivotValue varchar (100)

DECLARE @Operation char (3)

 

SET @TargetTable = 'DemoTable'

SET @PivotColumn = 'AssignmentName'

SET @AnchorColumn = 'StudentName'

SET @PivotValue = 'Grade'

SET @Operation = 'MAX'

 

INSERT @table

EXEC ('SELECT ' + @PivotColumn + ' FROM ' + @TargetTable )

SELECT @NewColumnList = COALESCE(@NewColumnList + ', ','') + QUOTENAME(SourceColumnNames)

FROM

(

       SELECT DISTINCT SourceColumnNames

       FROM @table

) AS B

 

 

SET @SQL = '

WITH PivotData AS

(

SELECT

       ' + @PivotColumn + ',

       ' + @AnchorColumn + ',

       ' + @PivotValue + '

FROM ' + @TargetTable+ '

)

SELECT

       ' + @AnchorColumn + ',

       ' + @NewColumnList + '

FROM PivotData

PIVOT

(

       '+@Operation+'(' + @PivotValue + ')

       FOR ' + @PivotColumn + '

       IN (' + @NewColumnList + ')

) AS PivotResult

ORDER BY ' + @AnchorColumn + ''

 

EXEC (@SQL)

GO

 

You can see that there is nothing terribly difficult or complicated about this.  Change up a few things, add a few variables, and we’re set! Notice my naming convention for the variables used to help in understanding this thing.  I named these for no reason other than they seemed to make sense to me, considering Books Online didn’t attempt to help out much with the components of the syntax. Here’s the skinny on the var names:

·          @TargetTable – the name of the dynamic table

·          @PivotColumn –the row name that will be used to generate columns in the pivot

·          @AnchorColumn – the column that stays put and is unchanged in its’ purpose in life

·          @PivotValue –the metric that in the pivot table

·          @Operation – probably shouldn’t have even used this, could have hard coded it MAX or MIN because sometimes values can’t be summed or averaged.

In closing, do we need ‘dynamic’ pivot (or unpivot for that matter)?  Not sure, but I know that I haven’t needed it in production for either of my past two clients. Who knows…maybe someone can use something like this; as always let me know.

Happy dynamic pivoting!

Lee

 

-----------------------------

If I start walking to Las Vegas tonight, I think I can make SQLServerConnections before its over

 

 

 

 

[1]  Sellers, M. (2009). Pivoting the Dynamic Way. SQL Server Magazine, November 2009. Retrieved 11/6/2009 from http://www.sqlmag.com/Article/ArticleID/102722/Pivoting_the_Dynamic_Way.html

 

 

 

 

SQL University History Week II: Current Events

November 6th, 2009 by Jorge Segarra

pass_logo Welcome back students! In our last lesson we covered the history of SQL Server, the product. This is important stuff to know in order to understand the evolution of the technology you’re working with. SQL Server has made many strides and it continues to do so to this day. Today’s lesson we’re going to cover current events. The reason we’re covering current events is because this past week was the annual conference for PASS called the PASS Community Summit. PASS Summit Unite 2009 is the largest, most focused, and intensive Microsoft SQL Server conference in the world.

Sounds great doesn’t it? Well you might be asking yourself: What is PASS? I’m glad you asked! PASS stands for the Professional Association of SQL Server. PASS is an independent, not-for-profit association, dedicated to supporting, educating, and promoting the Microsoft SQL Server community. From local user groups and special interest groups (Virtual Chapters) to webcasts and the annual PASS Community Summit – the largest gathering of SQL Server professionals in the world – PASS is dedicated to helping its members Connect, Share, and Learn by:

  • Facilitating member networking and exchange of information through our websites, Chapters, annual Summits, regional and local events, and virtual communities
  • Working with Microsoft to influence the evolution of SQL Server products and services
  • Delivering high quality, up-to-date, technical content, presentations, and other educational material for professional development

PASS was founded in 1999 by Microsoft and CA and continues to enjoy executive level endorsement from both organizations. As the number one user community for Microsoft SQL Server, Microsoft looks to the PASS community for valued feedback, input, and inspiration. (Source: About PASS)

If you’ve been following anyone in the SQL community either via Twitter, reading SQL blogs, or browsing around any SQL-related forums you may have noticed a very large buzz in the community about the conference this week. There’s very good reason for this: The PASS Summit is the Superbowl of SQL Conferences (or World Cup for my international friends). This is the one conference during the year where folks from all over the world unite in one place and share their SQL skills, experiences and friendships. The motto for PASS is Connect, Learn, Share and that’s definitely what the Summit embodies. I personally have never been but this year I followed along via Twitter and it almost felt like I was there (Twitter is even funnier when you see the after-hours activities like Karaoke).

There have been a slew of blog posts about why the Summit (and I stress that the conference is called Summit, the organization is called PASS but many mistakingly refer to the conference as just PASS) is so great to attend. This post will try to not turn into one giant PASS Summit commercial but its hard not to gush over how awesome it is when you see all the knowledge shared and how the community rallies together. Suffice to say the annual conference is definitely worth your time and money to try and attend.

Beyond the conference PASS offers much more to the community. As a professional association they offer education resources for all members. How does one become a member? Don’t worry, it’s free and everyone is invited to join! Just follow this link to register. So what kind of learning resources does PASS offer? Well for starters the website offers links to resources to just about anything you can think of from technical articles, webcasts, tips/tricks from the experts, newsletters, tips/resources for speakers and much more. But the website is just the tip of the iceberg! PASS truly is a worldwide organization and that fact can be seen through the proliferation of local and regional SQL Server User groups. PASS has chapters all over the world and with each passing year it keeps growing! You can check the PASS Chapters page and check your region to see if there is a user group in your area. If there isn’t a user group located near you then guess what? You can establish one yourself! To get started on that journey check out the page on what steps are needed to set one up and become an official PASS chapter!

PASS Chatpers Worldwide as of 2009

PASS Chatpers Worldwide as of 2009

Another great resource PASS has established in the past year has been the proliferation of Virtual Chapters (formerly called Special Interest Groups or SIGS). These virtual chapters each have a focus on a different aspect of SQL Server such as Business Intelligence, Application Development, Professional Development, Virtualization, Women in Technology, Performance and Database Administration. These Virtual Chapters act just like local chapters in that they meet on a regular basis but rather than meeting in a physical location they meet online via LiveMeeting. These meetings are great and open to everyone. At these meetings they have someone in the community do a one-hour presentation (much like you would see at a conference or a local user group meeting). These are fantastic, and best of all free, learning resources which helps you advance your knowledge and career!

Another great item to come out of the PASS umbrella are the free, local training events called SQLSaturday. These are free 0ne-day training events modeled after code camp events. At SQLSaturday there are several tracks based on different aspects of SQL Server (i.e. Administration, BI, Development etc.). These events attract speakers from all over and gives a chance for attendees to not only learn about SQL Server and PASS but gives them a taste of the community as well. If you’re interested go check out their events page and see if there is one scheduled for your area!

While there are many, many learning resources for SQL Server out there such as SQL Server Central, SQLShare and SQLServerpedia, know that we are all united when it comes to PASS since we are all members of the same fraternity of data professionals.

In our last class for this week I’ll cover the future of SQL Server, PASS and you!

Post to Twitter Post to Digg Digg This Post Post to StumbleUpon Stumble This Post

XML PATHs Of Glory

November 6th, 2009 by Brad Schulz
In a past blog post, I illustrated how you can use the FOR XML PATH clause to create comma-separated lists of items. In this entry, I’ll go into detail as to how FOR XML PATH can be used for what it was designed for: to shape actual XML output. Finally, I’ll use FOR XML PATH to create some HTML output as well.

The PATH option was introduced in SQL2005 to provide a flexible and easier approach to constructing XML output. I thank my lucky stars that I started with T-SQL at the SQL2005 level, because the SQL2000 method of using the EXPLICIT option looks like a complete nightmare. (If you’re into torture, take a look at Books Online for documentation on how to use the EXPLICIT option. When you're done screaming, then come back and read on).

Let's take a quick look at the output that results with the FOR XML PATH clause. If you pass no specific path name, then it assumes a path of ‘row’:

select ID=ContactID
      ,FirstName
      ,LastName
      ,Phone
from Person.Contact
where ContactID between 90 and 94
for xml path
/*
<row>
  <ID>90</ID>
  <FirstName>Andreas</FirstName>
  <LastName>Berglund</LastName>
  <Phone>795-555-0116</Phone>
</row>
<row>
  <ID>91</ID>
  <FirstName>Robert</FirstName>
  <LastName>Bernacchi</LastName>
  <Phone>449-555-0176</Phone>
</row>
<row>
  <ID>92</ID>
  <FirstName>Matthias</FirstName>
  <LastName>Berndt</LastName>
  <Phone>384-555-0169</Phone>
</row>
<row>
  <ID>93</ID>
  <FirstName>John</FirstName>
  <LastName>Berry</LastName>
  <Phone>471-555-0181</Phone>
</row>
<row>
  <ID>94</ID>
  <FirstName>Steven</FirstName>
  <LastName>Brown</LastName>
  <Phone>280-555-0124</Phone>
</row>
*/
For the query below, let's supply a specific path name of ‘Contact’. And it’s usually good practice to create XML with a root tag, and we can do that by adding the ROOT directive like so:

select ID=ContactID
      ,FirstName
      ,LastName
      ,Phone
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact>
    <ID>90</ID>
    <FirstName>Andreas</FirstName>
    <LastName>Berglund</LastName>
    <Phone>795-555-0116</Phone>
  </Contact>
  <Contact>
    <ID>91</ID>
    <FirstName>Robert</FirstName>
    <LastName>Bernacchi</LastName>
    <Phone>449-555-0176</Phone>
  </Contact>
  <Contact>
    <ID>92</ID>
    <FirstName>Matthias</FirstName>
    <LastName>Berndt</LastName>
    <Phone>384-555-0169</Phone>
  </Contact>
  <Contact>
    <ID>93</ID>
    <FirstName>John</FirstName>
    <LastName>Berry</LastName>
    <Phone>471-555-0181</Phone>
  </Contact>
  <Contact>
    <ID>94</ID>
    <FirstName>Steven</FirstName>
    <LastName>Brown</LastName>
    <Phone>280-555-0124</Phone>
  </Contact>
</Contacts>
*/
You’ll note that the column names were used as the tags for each element in the XML. For example, I renamed the first column to be ID rather than ContactID and therefore the element tag <ID></ID> was created.

You have the ability to shape the XML in whatever ways you wish based on what names you give to your columns. For example, any column that starts with an at-sign (@) will create attributes rather than elements, as illustrated below:

select "@ID"=ContactID
      ,"@FirstName"=FirstName
      ,"@LastName"=LastName
      ,"@Phone"=Phone
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact ID="90" FirstName="Andreas" LastName="Berglund" Phone="795-555-0116" />
  <Contact ID="91" FirstName="Robert" LastName="Bernacchi" Phone="449-555-0176" />
  <Contact ID="92" FirstName="Matthias" LastName="Berndt" Phone="384-555-0169" />
  <Contact ID="93" FirstName="John" LastName="Berry" Phone="471-555-0181" />
  <Contact ID="94" FirstName="Steven" LastName="Brown" Phone="280-555-0124" />
</Contacts>
*/
You can mix attributes and elements together like so:

select "@ID"=ContactID
      ,FirstName
      ,LastName
      ,Phone
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact ID="90">
    <FirstName>Andreas</FirstName>
    <LastName>Berglund</LastName>
    <Phone>795-555-0116</Phone>
  </Contact>
  <Contact ID="91">
    <FirstName>Robert</FirstName>
    <LastName>Bernacchi</LastName>
    <Phone>449-555-0176</Phone>
  </Contact>
  <Contact ID="92">
    <FirstName>Matthias</FirstName>
    <LastName>Berndt</LastName>
    <Phone>384-555-0169</Phone>
  </Contact>
  <Contact ID="93">
    <FirstName>John</FirstName>
    <LastName>Berry</LastName>
    <Phone>471-555-0181</Phone>
  </Contact>
  <Contact ID="94">
    <FirstName>Steven</FirstName>
    <LastName>Brown</LastName>
    <Phone>280-555-0124</Phone>
  </Contact>
</Contacts>
*/
And you can create nested attributes and elements, as illustrated below:

select "@ID"=ContactID
      ,"Name/@Title"=Title
      ,"Name/@Suffix"=Suffix
      ,"Name/First"=FirstName
      ,"Name/Last"=LastName
from Person.Contact
where ContactID between 92 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact ID="92">
    <Name Title="Mr.">
      <First>Matthias</First>
      <Last>Berndt</Last>
    </Name>
    <Phone>384-555-0169</Phone>
  </Contact>
  <Contact ID="93">
    <Name>
      <First>John</First>
      <Last>Berry</Last>
    </Name>
    <Phone>471-555-0181</Phone>
  </Contact>
  <Contact ID="94">
    <Name Title="Mr." Suffix="IV">
      <First>Steven</First>
      <Last>Brown</Last>
    </Name>
    <Phone>280-555-0124</Phone>
  </Contact>
</Contacts>
*/
In the above query, I introduced a Name element with two attributes (Title and Suffix) and two sub-elements (First and Last). You can also see that some of the contacts had NULL for the Title and Suffix and therefore those attributes were not created for those contacts.

Note that attributes must be introduced first, before the elements. For example, if I tried to do the following, I would get an error:

select "@ID"=ContactID
      ,"Name/First"=FirstName
      ,"Name/Last"=LastName
      ,"Name/@Title"=Title
      ,"Name/@Suffix"=Suffix
      ,Phone
from Person.Contact
where ContactID between 92 and 94
for xml path('Contact'),root('Contacts')
/*
Msg 6852, Level 16, State 1, Line 1
Attribute-centric column 'Name/@Title' must not come after a 
non-attribute-centric sibling in XML hierarchy in FOR XML PATH.
*/
When you have two adjacent columns with the same name, then their data will be concatenated together in one element, like so:

select Name=Title
      ,Name=FirstName
      ,Name=MiddleName
      ,Name=LastName
      ,Name=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact><Name>AndreasBerglund</Name></Contact>
  <Contact><Name>Mr.RobertM.Bernacchi</Name></Contact>
  <Contact><Name>Mr.MatthiasBerndt</Name></Contact>
  <Contact><Name>JohnBerry</Name></Contact>
  <Contact><Name>Mr.StevenB.BrownIV</Name></Contact>
</Contacts>
*/
Note again that NULL column values are ignored in the concatenation.

If you wanted to construct a nice readable single element consisting of the contact’s full name (Title, FirstName, MiddleName, LastName, and Suffix), you could approach it like this:

select Name=coalesce(Title+' ','')
           +FirstName+' '
           +coalesce(MiddleName+' ','')
           +LastName
           +coalesce(' '+Suffix,'')
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact><Name>Andreas Berglund</Name></Contact>
  <Contact><Name>Mr. Robert M. Bernacchi</Name></Contact>
  <Contact><Name>Mr. Matthias Berndt</Name></Contact>
  <Contact><Name>John Berry</Name></Contact>
  <Contact><Name>Mr. Steven B. Brown IV</Name></Contact>
</Contacts>
*/
But look all the logic required to handle possible NULL values in the Title and MiddleName and Suffix columns. Well, good news! You can use the following trick. Incorporate data() into the column name as illustrated below, and it will take care of concatenating it all together with spaces between and eliminating all the NULL values automatically:

select "Name/data()"=Title
      ,"Name/data()"=FirstName
      ,"Name/data()"=MiddleName
      ,"Name/data()"=LastName
      ,"Name/data()"=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact><Name>Andreas Berglund</Name></Contact>
  <Contact><Name>Mr. Robert M. Bernacchi</Name></Contact>
  <Contact><Name>Mr. Matthias Berndt</Name></Contact>
  <Contact><Name>John Berry</Name></Contact>
  <Contact><Name>Mr. Steven B. Brown IV</Name></Contact>
</Contacts>
*/
However, this approach will not work if you were trying to construct a Name attribute as opposed to a Name element:

select "@Name/data()"=Title
      ,"@Name/data()"=FirstName
      ,"@Name/data()"=MiddleName
      ,"@Name/data()"=LastName
      ,"@Name/data()"=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
Msg 6850, Level 16, State 1, Line 1
Column name '@Name/data()' contains an invalid XML identifier as required by FOR XML; 
'@'(0x0040) is the first character at fault.
*/
But you can handle that through a sub-query like so:

select "@Name"=(select "data()"=Title
                      ,"data()"=FirstName
                      ,"data()"=MiddleName
                      ,"data()"=LastName
                      ,"data()"=Suffix
                from Person.Contact c2
                where c2.ContactID=Contact.ContactID
                for xml path(''))
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact Name="Andreas Berglund" />
  <Contact Name="Mr. Robert M. Bernacchi" />
  <Contact Name="Mr. Matthias Berndt" />
  <Contact Name="John Berry" />
  <Contact Name="Mr. Steven B. Brown IV" />
</Contacts>
*/
Besides data(), you can also incorporate text() or node() into a column name or give a column a wildcard name (*) and the data will be inserted directly as text. They are all interchangeable, as you can see in the following example:

select "text()"=Title
      ,"node()"=FirstName
      ,"*"=MiddleName
      ,"node()"=LastName
      ,"text()"=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact>AndreasBerglund</Contact>
  <Contact>Mr.RobertM.Bernacchi</Contact>
  <Contact>Mr.MatthiasBerndt</Contact>
  <Contact>JohnBerry</Contact>
  <Contact>Mr.StevenB.BrownIV</Contact>
</Contacts>
*/
Remember, two adjacent columns with names that incorporate data() will be separated by a space, but, as you see above, those named with text() or node() or a wildcard are just concatenated directly with no intervening space.

You only really need to specify text() or node() or wildcard names if you want to insert a text element directly subordinate to the main path element, as we saw in the previous query. If, on the other hand, you are inserting text in a sub-element like so…:

select "Name/text()"=Title
      ,"Name/node()"=FirstName
      ,"Name/*"=MiddleName
      ,"Name/node()"=LastName
      ,"Name/text()"=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact><Name>AndreasBerglund</Name></Contact>
  <Contact><Name>Mr.RobertM.Bernacchi</Name></Contact>
  <Contact><Name>Mr.MatthiasBerndt</Name></Contact>
  <Contact><Name>JohnBerry</Name></Contact>
  <Contact><Name>Mr.StevenB.BrownIV</Name></Contact>
</Contacts>
*/
…then you’ll see that they are really unnecessary, since the following query (which we looked at earlier) does the exact same thing:

select Name=Title
      ,Name=FirstName
      ,Name=MiddleName
      ,Name=LastName
      ,Name=Suffix
from Person.Contact
where ContactID between 90 and 94
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact><Name>AndreasBerglund</Name></Contact>
  <Contact><Name>Mr.RobertM.Bernacchi</Name></Contact>
  <Contact><Name>Mr.MatthiasBerndt</Name></Contact>
  <Contact><Name>JohnBerry</Name></Contact>
  <Contact><Name>Mr.StevenB.BrownIV</Name></Contact>
</Contacts>
*/
You can also incorporate comment() or processing-instruction() into the column names to create those kinds of elements, as illustrated below:

select "@ID"=ContactID
      ,"comment()"='Modified on '+convert(varchar(30),ModifiedDate,126)
      ,"comment()"=case when ContactID=92 then 'Here is Contact#92' end
      ,"processing-instruction(EmailPromo)"=EmailPromotion
      ,"Name/@First"=FirstName
      ,"Name/@Last"=LastName
      ,"Name"='This is inserted directly as text'
      ,"Name"='...And so is this'
      ,"*"='This is inserted as text in the main Contact path'
from Person.Contact      
where ContactID between 90 and 92
for xml path('Contact'),root('Contacts')
/*
<Contacts>
  <Contact ID="90">
    <!--Modified on 2001-08-01T00:00:00-->
    <?EmailPromo 0?>
    <Name First="Andreas" Last="Berglund">
      This is inserted directly as text...And so is this
    </Name>
    This is inserted as text in the main Contact path
  </Contact>
  <Contact ID="91">
    <!--Modified on 2002-09-01T00:00:00-->
    <?EmailPromo 1?>
    <Name First="Robert" Last="Bernacchi">
      This is inserted directly as text...And so is this
    </Name>
    This is inserted as text in the main Contact path
  </Contact>
  <Contact ID="92">
    <!--Modified on 2002-08-01T00:00:00-->
    <!--Here is Contact#92-->
    <?EmailPromo 1?>
    <Name First="Matthias" Last="Berndt">
      This is inserted directly as text...And so is this
    </Name>
    This is inserted as text in the main Contact path
  </Contact>
</Contacts>
*/
You’ll note above that the two adjacent columns named comment() do NOT concatenate together like other adjacent columns with the same name. They are always separate elements. The same is true for processing-instruction() columns.

You can also concatenate whole individual XML documents together, as illustrated below, where we use two scalar subqueries to construct XML data from the Sales.SalesPerson and Sales.SalesReason tables. Since we did not give actual column names to the two subqueries, they are inserted directly as is. (Note that we could have named each of them node() or a wildcard and it would have worked the same. However, it’s important to note that you may NOT use text() or data() in naming true XML datatype columns):

select (select "@ID"=SalesPersonID
              ,"@Quota"=SalesQuota
        from Sales.SalesPerson
        for xml path('Person'),root('SalesPeople'),type)
      ,(select "@ID"=SalesReasonID
              ,"@Name"=Name
              ,"@Type"=ReasonType
        from Sales.SalesReason
        for xml path('Reason'),root('SalesReasons'),type)
for xml path('MyData')
/*
<MyData>
  <SalesPeople>
    <Person ID="268" />
    <Person ID="275" Quota="300000.0000" />
    <Person ID="276" Quota="250000.0000" />
    <Person ID="277" Quota="250000.0000" />
    <Person ID="278" Quota="250000.0000" />
    <Person ID="279" Quota="300000.0000" />
    <Person ID="280" Quota="250000.0000" />
    <Person ID="281" Quota="250000.0000" />
    <Person ID="282" Quota="250000.0000" />
    <Person ID="283" Quota="250000.0000" />
    <Person ID="284" />
    <Person ID="285" Quota="250000.0000" />
    <Person ID="286" Quota="250000.0000" />
    <Person ID="287" Quota="300000.0000" />
    <Person ID="288" />
    <Person ID="289" Quota="250000.0000" />
    <Person ID="290" Quota="250000.0000" />
  </SalesPeople>
  <SalesReasons>
    <Reason ID="1" Name="Price" Type="Other" />
    <Reason ID="2" Name="On Promotion" Type="Promotion" />
    <Reason ID="3" Name="Magazine Advertisement" Type="Marketing" />
    <Reason ID="4" Name="Television  Advertisement" Type="Marketing" />
    <Reason ID="5" Name="Manufacturer" Type="Other" />
    <Reason ID="6" Name="Review" Type="Other" />
    <Reason ID="7" Name="Demo Event" Type="Marketing" />
    <Reason ID="8" Name="Sponsorship" Type="Marketing" />
    <Reason ID="9" Name="Quality" Type="Other" />
    <Reason ID="10" Name="Other" Type="Other" />
  </SalesReasons>
</MyData>
*/
Note that the ,TYPE directive was used to make sure that the XML subqueries came through as true XML datatypes. This is very important. If we had left off the ,TYPE directive, they would be processed as strings and then when they were incorporated into the main query, the main FOR XML PATH(‘MyData’) would encode all of the less-than and greater-than signs into this ugly mess:

select (select "@ID"=SalesPersonID
              ,"@Quota"=SalesQuota
        from Sales.SalesPerson
        for xml path('Person'),root('SalesPeople'))
      ,(select "@ID"=SalesReasonID
              ,"@Name"=Name
              ,"@Type"=ReasonType
        from Sales.SalesReason
        for xml path('Reason'),root('SalesReasons'))
for xml path('MyData')
/*
<MyData>
  &lt;SalesPeople&gt;
    &lt;Person ID="268" /&gt;
    &lt;Person ID="275" Quota="300000.0000" /&gt;
    &lt;Person ID="276" Quota="250000.0000" /&gt;
    &lt;Person ID="277" Quota="250000.0000" /&gt;
    &lt;Person ID="278" Quota="250000.0000" /&gt;
    &lt;Person ID="279" Quota="300000.0000" /&gt;
    &lt;Person ID="280" Quota="250000.0000" /&gt;
    &lt;Person ID="281" Quota="250000.0000" /&gt;
    &lt;Person ID="282" Quota="250000.0000" /&gt;
    &lt;Person ID="283" Quota="250000.0000" /&gt;
    &lt;Person ID="284" /&gt;
    &lt;Person ID="285" Quota="250000.0000" /&gt;
    &lt;Person ID="286" Quota="250000.0000" /&gt;
    &lt;Person ID="287" Quota="300000.0000" /&gt;
    &lt;Person ID="288" /&gt;
    &lt;Person ID="289" Quota="250000.0000" /&gt;
    &lt;Person ID="290" Quota="250000.0000" /&gt;
  &lt;/SalesPeople&gt;
  &lt;SalesReasons&gt;
    &lt;Reason ID="1" Name="Price" Type="Other" /&gt;
    &lt;Reason ID="2" Name="On Promotion" Type="Promotion" /&gt;
    &lt;Reason ID="3" Name="Magazine Advertisement" Type="Marketing" /&gt;
    &lt;Reason ID="4" Name="Television  Advertisement" Type="Marketing" /&gt;
    &lt;Reason ID="5" Name="Manufacturer" Type="Other" /&gt;
    &lt;Reason ID="6" Name="Review" Type="Other" /&gt;
    &lt;Reason ID="7" Name="Demo Event" Type="Marketing" /&gt;
    &lt;Reason ID="8" Name="Sponsorship" Type="Marketing" /&gt;
    &lt;Reason ID="9" Name="Quality" Type="Other" /&gt;
    &lt;Reason ID="10" Name="Other" Type="Other" /&gt;
  &lt;/SalesReasons&gt;
</MyData>
*/
Now that we’ve learned so much about FOR XML PATH, let’s put our knowledge to use. Let’s say that you want to construct a webpage or an e-mail that incorporates a table in HTML format. Using our knowledge of FOR XML PATH, we will construct all the HTML between the <table></table> tags. That can then be incorporated into the correct spot in the webpage or e-mail.

Note that this query below uses most of what we learned in this article. You’ll see the following:
  • We create ALIGN and VALIGN attributes to align the table headers correctly.
  • We put a <br /> tag into the Phone Number header to split it into two lines.
  • We use data() to construct the Full Name of the contact.
  • We create a hyperlink for the E-Mail Address
  • We subtly color the E-Mail Address in a pale yellow color if EmailPromotion is equal to 1.
  • We use the ,TYPE directive in our XML CTEs so that we can concatenate them in subsequent CTEs.

Here’s the query, which creates a single NVARCHAR(MAX) variable called @TableHTML:

declare @TableHTML nvarchar(max);
  
with HTMLTableHeader(HTMLContent) as
(
  select "th/@align"='right'
        ,"th/@valign"='bottom'
        ,"th"='ContactID'
        ,"*"=''
        ,"th/@valign"='bottom'
        ,"th"='Full Name'
        ,"*"=''
        ,"th"='Phone'
        ,"th/br"=''
        ,"th"='Number'
        ,"*"=''
        ,"th/@valign"='bottom'
        ,"th"='Email Address'
  for xml path('tr'),type
)
,HTMLTableDetail(HTMLContent) as
(
  select "td/@align"='right'
        ,"td"=ContactID
        ,"*"=''
        ,"td/data()"=Title
        ,"td/data()"=FirstName
        ,"td/data()"=MiddleName
        ,"td/data()"=LastName
        ,"td/data()"=Suffix
        ,"*"=''
        ,"td"=Phone
        ,"*"=''
        ,"td/@bgcolor"=case when EmailPromotion=1 then '#FFFF88' end
        ,"td/a/@href"='mailto:'+EmailAddress
        ,"td/a"=EmailAddress
  from Person.Contact
  where ContactID between 90 and 94
  for xml path('tr'),type
)
,HTMLTable(HTMLContent) as
(
  select "@border"=1
        ,(select HTMLContent from HTMLTableHeader)
        ,(select HTMLContent from HTMLTableDetail)
  for xml path('table')  /*No TYPE because we want a string */
)
select @TableHTML=(select HTMLContent from HTMLTable);
Remember the rule that if two adjacent columns have the same name, their data will concatenated? I had to prevent that from happening with adjacent columns that I named th and td by inserting a blank column with a wildcard name between them to force them to come out as discrete elements.

And here are the contents of that variable as a result of that query:

/*
<table border="1">
  <tr>
    <th align="right" valign="bottom">ContactID</th>
    <th valign="bottom">Full Name</th>
    <th>Phone<br />Number</th>
    <th valign="bottom">Email Address</th>
  </tr>
  <tr>
    <td align="right">90</td>
    <td>Andreas Berglund</td>
    <td>795-555-0116</td>
    <td>
      <a href="mailto:andreas1@adventure-works.com">andreas1@adventure-works.com</a>
    </td>
  </tr>
  <tr>
    <td align="right">91</td>
    <td>Mr. Robert M. Bernacchi</td>
    <td>449-555-0176</td>
    <td bgcolor="#FFFF88">
      <a href="mailto:robert4@adventure-works.com">robert4@adventure-works.com</a>
    </td>
  </tr>
  <tr>
    <td align="right">92</td>
    <td>Mr. Matthias Berndt</td>
    <td>384-555-0169</td>
    <td bgcolor="#FFFF88">
      <a href="mailto:matthias1@adventure-works.com">matthias1@adventure-works.com</a>
    </td>
  </tr>
  <tr>
    <td align="right">93</td>
    <td>John Berry</td>
    <td>471-555-0181</td>
    <td>
      <a href="mailto:john11@adventure-works.com">john11@adventure-works.com</a>
    </td>
  </tr>
  <tr>
    <td align="right">94</td>
    <td>Mr. Steven B. Brown IV</td>
    <td>280-555-0124</td>
    <td>
      <a href="mailto:steven1@adventure-works.com">steven1@adventure-works.com</a>
    </td>
  </tr>
</table>
*/
Our webpage template looks like this, with a placeholder where we want to insert our table:

/*
<html>
  <head>
    <title>HTML Table constructed via FOR XML PATH</title>
  </head>
  <body style="font-family:Arial; font-size:small">
    <span style="font-size:x-large">
      <b>Selected Contacts:</b>
    </span>
    <!-- Insert Table Here -->
  </body>
</html>
*/
And here is the final result, with the table data inserted in the placeholder position, when we look at the web page in Internet Explorer:

HTML Table constructed via FOR XML PATH

I hope this article gave you a tantalizing look at the possibilities of things you can accomplish with the FOR XML PATH clause. In future blog entries, I’ll explore some other aspects of XML.

SQL Pass Summit Postgame – What I learned at summer camp

November 6th, 2009 by Jen McCown

Brain Dump

  • These people are the coolest mother(shut your mouth!) you'll ever run across.
  • It's really cool to have a whole conference focused on SQL Server. I like TechEd quite a lot, but this aspect of the Summit - which Sean has the opposite opinion on - makes it extra appealing to me.
  • Bad karaoke is the best karaoke evar, unless we're talking about "I'm feelin good" or "Love in the Club".
  • It is MUCH more than six blocks to Bush Garden karaoke bar, but it's all downhill.  Walk down, eat at Ho Ho Seafood restaurant after, and take a cab home.

Getting the Absolute Most 

Fully 3/4 of the conference experience is being a part.  I kept calling SQLPASS "summer camp", because it is. You have pen pals beforehand, you pack your bag and go stay all in the same place...you have fun and learn new things all day, you get awards for volunteering and participation, you're directed to the right places by camp counselors, you stay up late giggling with friends.  It's all true, but just like summer camp, it SUCKS if you don't know anyone, don't meet anyone, and don't feel sad to leave a new friend when it's over.  In that vein, here are the absolute requirements for attending SQL Pass Community Summit 2010 and beyond:

1. Go to your local SQL Server User Group meetings.  Some of them, if not all.  Get on the board, or hang out with the board.  Bring swag if you have it, get swag from them if you don't. Volunteer for the meetings and SQL Saturdays and code camps, if you have them.  Sure, it takes time, but (a) not all that much, (b) it can be an awful lot of fun, and (c) you'll likely start seeing yourself hanging out in groups of MVPs, authors, and speakers...the kind of people who can show you the way to fun and profit.

2. Get on Twitter. It may seem pointless or stupid, but it's not.  SQL professionals all over the WORLD use Twitter to trade tips, websites, free resources, troubleshooting, and yes: jokes and whatnot.  I got on Twitter for my own personal updates, and then I opened a Twitter account for MidnightDBA. I posted blog announcements and followed peers and authors, and found myself in discussions with people I'd never heard of, and SQL celebrities I'd never dreamed of being in contact with.  Another benefit: It kind of gets you over being starstruck, if that's a problem with you (it was for me).  Yet another benefit: it was through Twitter that I heard of the SQLServerPedia.com contest, where I entered and won this very trip to PASS Summit 09, AND the HP mini netbook I'm typing this on.  Membership has its priviledges, babycakes.

3. Go to sessions, but don't be afraid to miss sessions - even really good ones - for the chance to chat with your peers and mentors.  Get into the chalk sessions, spotlight sessions, troubleshooting, contests, special interest breakfasts and lunches. You're going to be tired, sure, but that's summer camp!

 3a. Make a point, by the way, to hit the Women in Technology lunch.  If you're rolling your eyes, even mentally, I understand...I did too (sorry WIT folks!).  But it's not really what you expect...it's really impressive what these people - men and women - are doing, the message they're getting across.  I'm going to get involved when I get back, and be sure to bring my daughter to a meeting or two.  Like I said in my blog post about WIT, she needs to see that what I do - being a technogeek - is really something cool and worthwhile.

 3b. Also make a point to get the conference DVDs.  You can't attend all the sessions, and you'll want to revisit the ones you DID attend.  Maybe you could get your company to spring for them as a training aid, or split the cost with a friend, or get funding from your user group.

4. Talk to people.  Talk to peers, authors, Microsoft reps, speakers, MVPs, chapter leaders, organizers, and vendors. Everyone there is there largely to talk to other people.  Some of these people may dwarf us with their insanely detailed knowledge of SQL Server, but it doesn't mean they're assholes.  Every single person I spoke to was extremely approachable, friendly, and accomodating.  A couple may tell you to RTFM (Hi Joe!), but they'll do it kindly, and only if it's a very basic question.  Don't be afraid to ask questions in sessions, either.  There's a very, VERY good chance that you're not the dumbest person in the room (especially if I'm in the same room), and most likely several other people have the same question and are afraid to ask it.  I can't count the number of times I've asked a question, or heard someone else ask a question, and seen several people nodding before the speaker started their answer.

Traveling

  • Use SeatGuru.com to get the best seat on any plane. Be sure to actually log on and GET the best seat once you have your reservations, and as early as possible.
  • Eat well, eat light, hydrate, take your vitamins. And if you ask me, don't overindulge. I know it's fun to drink yourself silly, but come on - we're all getting too old for that, and you'll hate yourself in the 8:30am session.
  • Charge phone and laptop and phone as often as possible. If you use them half as much as I do, you'll find yourself out of power in the middle of a GREAT Kim Tripp session.
  • The Sheraton has a gym, pool and hot tub on the top floor.  Hot tub often, eh?
  • Your employer will assume that the conference goes for the whole week. If I were you, I wouldn't correct them.

Spending

  • Splurge on the hotel and the DVDs.
  • Save on the food and drink - conferences are getting MUCH better about serving healthy stuff, even if breakfast gets a little repetetive.  And between the demos and parties and receptions, you can find something catered for almost every meal. If you really feel like it though, or if the company's footing the bill, the Sheraton Daily Grill is a VERY safe bet; I ate there four times, and every single thing I had was very good.
     The Taphouse is a popular haunt, too...good food, and something like 100 beers on tap.
  • Bring business cards. If you have a website or a business to promote (or even if you dont'), I'd recommend putting some thought into some cute/attractive/funny stickers to hand out...they were the hot ticket item this year.
  • Don't bother renting a car. There are plenty of ways to get from airport to hotel - arrange with friends on Twitter to meet and split a cab, even - and you'll have NO time to sightsee if you do things right.
  • Pre and Post cons are completely up to you, I have no recommendation. They can be well worth it, but then again, it is an extra $X you might not have.

Final Wrapup

A big shout-out to all the folks I met (and didn't get to meet - I'm lookin at you, Jorge!)...you guys rock, and you know that. An even BIGGER (if possible) shout-out to the MVPs and authors that took the time out to sit down with me and Sean for our little interviews; it means a lot to us.  Big big hugs to Paul Randal, Kim Tripp, Andy Leonard, the canook, Grant Fritchey, Buck Woody, Cindy Gross, Peter DeBetta, about a zillion others (sorry, I'm still running on 4 hrs sleep), and especially Allen and Cindy White...great big heartfelt hugs to you two.

Watch this space (and Twitter, of course)...we'll be publishing our celebrity videos over the course of the next several days.  I've also started a page for the MVP Deep Dives book launch photo shoot here: http://midnightdba.itbookworm.com/midnightdba/SQLPASS09/  (Go buy the book!!) I'll get all those photos and vids up as soon as possible.

Watch this space, too, for some big expansion. PASS Summit inspired us to do several new things with the site that you're gonna love.

See you next year, so practice your karaoke. I'll be online till then...

-Jen McCown, http://www.MidnightDBA.com

Blogging for http://www.SQLServerPedia.com

Data Warehouse: Facts and Measures

November 5th, 2009 by John Sterrett

A few days ago I wrote a post that gave an introduction to dimensions.  Today, we are going to continue covering the basic concepts included in dimensional modeling by covering an introduction to fact tables and measures.  These posts are all part of the introduction to building a data warehouse with sql server series.

What is a Fact Table?

A fact table is a table that joins dimension tables with measures.  For example, Lets say you wanted to know the time worked by employees, by location, by project and by task.  If you had a dimension for employees, location, project and task you would create a composite primary key using these foreign keys and add an additional column for the time worked measure. (more on measures in a little bit)

Keep in mind that fact tables are huge unlike dimension tables.  Fact tables are usually built to contain a hundred thousand records on the low side up to billions of records on the high side.  Therefore, these tables must be normalized to be efficient.

A fact table is normalized when it consists of the following two things:

    1. A composite primary key using the foreign keys to the dimension tables.
    2. Measure(s) used for numerical analysis.

What is a measure?

A measure is normally an additive numerical value that represents a business metric.  You are also not limited to a single measure.  You can have multiple measures within a fact table.  For example, if your fact table is used to track purchases internationally you might have measures for each type of currency.  If you are building a fact table for the retail industry you might also have the following measures cost, list price, average sale price.

Next we will conclude or session on dimensional modeling and start to build our first cube.

Share/Save

PASS Summit 2009 Day 3 Keynote Live Blog

November 5th, 2009 by Kendal Van Dyke

10:25 AM – And that's it for the week! Thanks for tuning in. Be sure to order the Summit DVDs. David's keynote alone was worth it.

10:20 AM – Time's up for David. Mentioning key points. Waaaaay too much information on the slides to type before he's moved on to the next one. Other bloggers doing live keynotes were smart enough to grab photos of the screen. Hopefully they'll post the contents.

10:05 AM – David's comparing different compression techniques. Run Length Encoding, Bit-Vector Encoding, and Dictionary Encoding.

10:00 AM – Now talking about compression in column stores. Basically it trades I/O cycles for CPU cycles (remember that CPUs have gotten 1000x faster while disks have only gotten 65x faster).

9:51 AM – Boiling down the row store vs. column store discussion, basically we're looking at a 7X performance improvement in column store architecture for certain types of queries. "Select * from …" will never be faster though. Best bet: Store some data row wise and some data column wise.

9:45 AM – L2 cache misses are due to row store architecture. Now David's talking about column store architecture.

9:43 AM – David talking about why we have L2 cache misses in CPU when fetching data off disk. Highly technical, and my head is beginning to hurt!

9:36 AM – OK, lots of technicals on CPUs now. Too hard to write about what he's talking about. It's deep. Really deep.

9:35 AM – Now talking CPU trends. This is awesome stuff, really. Best keynote so far in my opinion.

9:34 AM – 1980, sequential/random 5:1; 2009 sequential/random 33:1. Takeaway: RDBMS must avoid random disk I/O when possible.

9:32 AM – Incredibly inexpensive drives & processors have made it possible to collect, store, and analyze huge quantities of data. But considering the metric transfer bandwidth/byte, when relative capacities are factored in, drives are 150X SLOWER today.

9:30 AM – After 30 years of CPU and memory improvements, relative performance of CPUs and disk are totally out of whack in terms of performance. The benefits from a 1,000x improvement in CPU performance and memory sizes are almost negated by the 10x in disk accesses/second. David thinks that SSDs will change our lives in this area. (Side note, I've got some GOOD stuff coming on SSDs soon. Stay tuned!)

9:27 AM – Drilling down into disk trends now. 10,000 x capacity, 65x transfer rates, but the BIG change is in seek times.

9:24 AM – Going back to 1980, comparing hardware then to hardware today. A LOT has changed: improvements of 2,000x CPU, 1,000x CPU Caches, 1,000X memory, & 10,000x disk.

9:21 AM – This is going to be a really deep dive into technical stuff: trends in CPU, memory, disk, and how they impact design. Disclaimer: This is an academic talk – no marketing speak (yay!)

9:20 AM – No product announcements, no motorcycles, but already a lot of humor. This is going to be interesting.

9:18 AM – Bill back on stage, introducing Dr. David DeWitt, Technical Fellow at Microsoft. He's going to present "From 1 to 1000 MIPS"

9:18 AM – Dell Keynote over…not a lot to mention, honestly.

9:05 AM – More talk about disaster recovery…(BTW, we're a room full of DBAs at PASS. I sure hope we all understand disaster recovery)

8:59 AM – Benefits of configuration management:

  • Provides clear picture of the SQL Server Environment
  • Improves operational efficiency
  • Drives priority for SQL Server Disaster Recovery and Consolidation

8:55 AM – Patrick talking about configuration management…types of data to collect (e.g. Name of server, Server Properties, CPU, Location Information, OS Version, SQL Server Version, etc). Basically stuff that anybody in IT should already know and be collecting.

8:50 AM – PASS business out of the way, now Patrick Ortiz from Dell's Infrastructure Consulting Services coming onstage to talk about "Managing SQL Server In The Enterprise"

8:49 AM – Thursday's To-Do List:

  • SQL Server Clinic
  • Hands-On Labs
  • Eat lunch with regional and local Chapter Leaders
  • Sign up for one of tomorrow's Post-Cons
  • Order Summit 2009 DVDs for $125
  • Submit your session and Summit evals
  • Drop comments & questions in the Suggestion Box

8:48 AM – Announcing 2010 PASS Summit, November 8-10 in Seattle again. Register now for $995. No contract signed for 2011 yet; feedback on location will be solicited.

8:47 AM – And now the new executive board: Rushabh Mehta (President), Bill Graziano (Executive VP), Rick Heiges (VP, Marketing), and Wayne Snyder (Immediate Past President)

8:46 AM – Bill introducing new board members Brian Moran, Jeremiah Peschka, and Tom LaRock.

8:45 AM – Wayne is tearing up telling stories about Kevin…Kevin receiving a standing ovation as he receives his award for 10 years of service.

8:44 AM – Kevin taking the stage now. Wayne Snyder joining in to present Kevin with a special award for his service.

8:43 AM – BIll thanking outgoing PASS board members Pat Wright, Greg Lowe, and Kevin Kline. Special video tribute playing for Kevin's years of service to PASS. Big applause for Kevin.

8:40 AM – Bill Graziano, PASS VP of Marketing, taking the stage now. Promises this will be the shortest keynote of the week. Big applause.

8:38 AM – I'm back for the final day of the 2009 PASS Summit. Today is the Dell sponsored keynote. Journey's "Don't Stop Believin'" is blaring through the speakers as videos from the conference play on the big screens.

Live Blogging: Keynote at PASS, Day 3

November 5th, 2009 by Michelle Ufford

Today is the 3rd and final day of keynotes at the PASS Summit. Following is highlights of the keynotes. During the keynote, refresh often for updates!

8:36 AM PST
Keynote kicks off with Don’t Stop Believing by Journey. Awesome.

8:38 AM PST
PASS VP of Marketing Bill Graziano just takes the stage. He promises the shortest keynote of the conference, and appears to deliver on it. First up are Board announcements.

Outgoing Board Members are:

If you these folks at Summit, make sure to thank them for their hard work!

PASS President Wayne Snyder comes out to honor and thank Kevin Kline for his 10 YEARS of service.  Yes, that’s right, 10 YEARS. Wayne doesn’t get far into his speech before he gets choked up.  Great quote from Wayne:  “Kevin (@kekline) is a man of honor and integrity. He’s… well, he’s full of it.”  Kevin then gets a well-deserved standing ovation from the entire audience.

New Directors-at-Large are also announced:

Next year’s PASS Summit is also announced.  It will be in Seattle from November 8th - 11th, 2010. The decision was made to have the conference in Seattle because it’s a launch year, so access to Microsoft employees will be invaluable.  The registration rate is $995 if you register soon. Details and registration can be found on the PASS site at www.sqlpass.org/summit/na2010.

8:52 AM PST
Dell keynote just started with Patrick Ortiz, Solution Architect with Dell’s Infrastructure Consulting Services for SQL Server & BI.  The keynote is pretty uneventful.

9:17 AM PST
Woot! Dr. David DeWitt, Technical Fellow, Data & Storage Platform Division at Microsoft, takes the stage. His presentation is entitled, “From 1 to 1000 MIPS.” He promises a very technical talk, against Microsoft Marketing’s wishes. He’s not going to be announcing any products, but instead plans to discuss the changes in database technology and what’s in store for us in the next 10 years.

Highlights (or at least, the ones that my simple mind was able to grasp):

  • Basic RDMS design is essentially unchanged, but the hardware has changed dramatically.
  • Interesting statistics in disk trends last 30 years: 10,000x capacity, 65x transfer rate, 10x avg seek time… not balanced at all
  • “CPU’s and disks are totally out-of-whack in terms of performance.”
  • The benefits of 1,000x improvement in CPU is almost negated by lack of improvement in disk
  • Transfer bandwidth/byte trends: 1980 = 0.015, in 2009 = 0.0001… 150x slower today! “It’s like trying to provide drinking water for the town through a garden hose.”
  • “Takeaway: DBMS must avoid doing random disk I/O as much as possible.”
  • “Can incur up to one L2 data cache miss per row processed if row size is greater than size of cache line.”
  • DBMS transfers the ENTIRE ROW from disk to memory even though the query required just 3 attributes.
  • “Takeaway: DBMS must avoid doing random disk I/O as much as possible.”

DeWitt shows us the power of indexing and vertical partitioning in very technical terms. Awesomeness.

PASS Summit Day 2: the brief wrapup #sqlpass

November 5th, 2009 by Stuart Ainsworth

I’m writing this post quickly, because I have a 6:30 SQLSaturday alumni meeting at a local donut stop.  Please don’t let the brevity of the post throw you off; lots of goals accomplished yesterday.  Unfortunately, going to sessions was not one of them :)

  • I spent a lot of time networking, and discussing Agile techniques for database developers with a lot of different people, as well as looking at some of the latest and greatest tools for SQL Server.
  • Speaking of vendors, I shipped home two huge boxes of free stuff, most of it from Quest, Idera, and Red Gate.  My shipping costs were $80, so you can imagine the stuff I had.
  • Finally, and I’ll probably return to this in a later post, I met with the Board of Directors of PASS in an intimate session on governance.  It was fascinating, and I’m surprised that more chapter leaders were not there, because we discussed A LOT of issues related to the local chapters, and how PASS runs itself.  I don’t think that any particular goals were met, but I do believe that the following is true:
    • Change is coming for the local chapters; the PASS board is going to try to do more to connect with the local chapter, but will want more from the local chapter as well.  Think of it as having more concrete discussions about how the local community uses SQL Server, and what niche can PASS fill.
    • Steve Jones asked a series of great questions, including: “What have you done for me?”  He wanted the Board to be able to be more transparent, and expose their accomplishments beyond their individual audiences (for example, Greg Low’s work with the chapters should be available to those who are not chapter leaders).
    • More to come.
  • The day finally wrapped with an afterparty at a local arcade, hosted by Microsoft.  Lots of fun, but I was exhausted.

Gotta run; day 3 will be a blast.

  • Digg
  • del.icio.us
  • DZone
  • DotNetKicks
  • Google Bookmarks
  • Ping.fm
  • Reddit
  • StumbleUpon
  • TwitThis

SQLPASS Summit Wednesday – Women in Technology

November 5th, 2009 by Jen McCown

I wanted to clean this up and make it pretty, but I'm running out of time. Gotta pack before I go blog the last keynote (3 keynotes, PASS? Really?). 

I was double booked yesterday for lunch.  Early in the summit I'd promised Paul Nielsen I'd be the unofficial official photographer for the MVP Deep Dives book launch (which I'll blog more about another time, with pix and vid).  And I'd already wanted to go to the Women in Technology (WIT) lunch when Allison ?lastname asked me to come and blog it.  So I did a lot of running back and forth.  On the book though, a quick retweet and pic: 

http://twitpic.com/o9wnq The Deep Dive book they are signing for Bill Gates just passed me in line. #SQLPASS

I did see the introductory video at the luncheon.  It was this great montage of women saying I'm a woman in technology, and women and men saying "I support women in technology". 

It was near the end of the session when I was finally able to sit down and listen, and I was quickly blown away.  They were talking about teaching young girls technology - not just programming or SQL admin but Facebook and Twitter, things that are (surprisingly) becoming a huge part of doing business.  I think it was panelist Jessica Moss who suggested finding a women in technology chapter and bringing your daughter.  I was stunned by what a good idea this is, and I intend to follow it with my 10 year old girl.

From here, I'm just going to use my notes wholesale:

Tweeted: The Women in Tech lunch touched me, and I didn't expect that. I'll have a really good blog about that soon, I think. #sqlpass #sqlwit

Kathi Kellenberger: too afraid to talk to their children about race, so they wind up saying embarassing things.  You havae to talk about these things specifically.  Story: pharmacist, had to go to another town to get the rx. daughter said "I didn't know men could be pharmacists".

One of the big things that shaped my life was my image of my mother (this is me, Jen, talking here), from a very young age. She was a pilot, and she flew us in a little 4 seater.  I remember specifically watching her put on her uniform, how cool that was. I still has one of those shirts. 

Someone asked me yesterday what the big deal was with women in technology, why do we need our own cause?  And we can talk about how prevalent sexism is, or injustice, or what have you, but at a base level it's largely about this lingering underlying assumption that certain things are men's jobs, not women's.  A woman executive is still the exception. A female techie is still the exception.  My kids saw me as a stay at home mom for years, until I went back to the workforce this year, and I'm glad I stayed home. But I'm also glad I get to show them by example that being a geek is something I do well, enjoy well, and I'm paid well for it.

To say that slightly more efficiently, we have to understand at a very deep level - from childhood - that some things just aren't an option for girls, not really.

I couldn't have a better partner in all this than Sean.  When it comes to me, "supportive" isn't even the word, because that implies
 he feels that I'm lesser in some way. He's always treated me like he treats anyone else: when I get something right, he knows it. When I screw up, he understands.  When I'm being dumb, he'll say so.

And he's brilliant with our kids, because again, he doesn't see the gender difference as an impediment to ability.  We bought our daughter a computer when she was 5. Sean started computer lessons, including web, email, typing, and HTML, with her when she was 7 or 8.  My 4 year old son just got his sister's hand-me-down computer, and I fully expect he'll have the same at-home computer regimen his sister did. 


@BenchmarkIT got big laughs with his question

 

 

Tweets:

benchmarkIT: Wow @jessicammoss = another great ambassador for WIT #sqlpass

sqlfool: Cathi Rodgveller shares how she started IGNITE (http://bit.ly/YzRrI); goal is to excite young women about technology #sqlpass #sqlwit
 Rodgveller: "You can have an impact in your community." "One [positive technology] event can change a young girl's life." #sqlpass #sqlwit
 Rodgveller: Ignite also targets young women & men, minority races, & low-income youth to stimulate interest in technology #sqlpass #sqlwit

benchmarkIT; Cathi Rodgveller has done some amazing things for WIT and the community in general #sqlpass
 I'm a technical woman (ok not really) but I support WIT #sqlpass

sqldba: Listening to @llangit talk at the WIT luncheon at #sqlpass ... she's an engaging speaker!

[blog] Women in Technology Luncheon - http://twurl.nl/4imr01 (via @way0utwest)

-Jen McCown, MidnightDBA.com

Blogging for SQLServerPedia.com

Table dump in SSIS, not in order

November 4th, 2009 by Bob Horkay

Don't expect your queries to be in order without a select statement, guess what same goes for SSIS!

Today I had to trouble-shoot a package (not developed by me or our SQL DBA Group), but regardless, it becomes our problem !

SSIS was exporting a table to a flat file, turns out this flat file is really an XML File.  The table is populated with XML from a stored procedure prior to the export.  The data pump ole db source was a table, turns out it's not always in order, resulting in malformed XML.  Change the source from table to a select statement with an order by clause and all is working well, fortunately there was a rowid (identity) column in the table already.

Frustruating to the end user, as it was a package that had been working fine for months.

Of course there was no primary key, making this a heap table; but either way, if the requirement is to have the data ordered, than you need an order by statement, even if the data is "in the table that way", and even for SSIS.  [Be nice if Microsoft put a warning on there that the data access mode may not be in order.]

This seems to be more of a problem on heap tables (no primay key) and Enterprise edition sql (which supports Merry-go-round scans [i think that's what it's called]).