Development

#passvotes Why I voted for Allen Kinsel–three times….

I’m a little late with this endorsement, in part because I debated whether or not that I should admit that I’m a serial voter.  In case you haven’t heard, PASS is holding it’s elections for the Board of Directors again; ballots went out on Wednesday, September 25 to “all members in good standing as of June 2013”.  Apparently, I’m a member thrice over, since I received three ballots at three different email addresses.

This has happened in every PASS election that I can remember, and I’ve always taken the moral high ground before, casting one vote and ignoring the other two; however, this time is different.  It’s different because Allen Kinsel’s on the ballot.

Lots of people have lots of good things to say about Allen, so I won’t rehash those; however, the thing that swayed me to vote for him (multiple times) was the following bullet point from his platform page:

    • Making an additional IT investment to bring PASS’s membership roster up-to-date. Updating the roster will allow PASS to have better knowledge of our worldwide membership and to provide members with better benefits.

Allen wants to update the database, and clean it up.  As a database professional, data integrity appeals to me; it’s a little sad that my professional organization has dirty data, and is relying on that dirty data to make key business decisions (like generating ballots).   So this time, I decided to leverage all of my electoral power and cast all of my votes for Allen because I believe he’s going to try and do something to prevent this from happening in the future.

I know; it seems wrong for me to cast three votes, but I figure I’m not the only one with multiple ballots.  Maybe some other members of the organization don’t have the same moral hesitation about “pulling the lever” at every opportunity.  Perhaps some devious prankster has registered 500 email addresses, and is slowly stealing the election away.  All I know is that this should change, and Allen’s the only candidate which has mentioned this as a priority.

Vote.

SQL In The City Atlanta, Oct 11, 2013

SITC13_Banner_300x250_SpeakerJust a quick note to say that I’m super excited to be presenting at the Red Gate SQL In the City event in Atlanta this year; I’ll be covering some of their development tools (many of which I use daily).  As far as I know, there are still some seats left, but I wouldn’t count on them being available too much longer.

It’s free, and you’ll get a chance to hear lots of great topics presented by Grant Fritchey, Steve Jones, and Tim Radney, as well as me. Click on the image for more information, and I hope to see you there.

SQL Server XQuery: .value() method

Back to XQuery; the third method I wanted to discuss in this series is the .value() method. Like the .query() method, .value() bridges the SQL Server database engines and XQuery engines by extracting information from an XML document.  The difference is that while .query() returns XML, .value() translates the specific information requested into a scalar SQL value.  For example:

DECLARE @T TABLE ( ID INT, x XML )

INSERT  INTO @T
        ( ID, x )
VALUES  ( 1, '<root><plant><fruit>tomato</fruit></plant></root>' )
,       ( 2, '<root><plant><fruit>banana</fruit></plant></root>' )
,       ( 3, '<root><plant><vegetable>tomato</vegetable></plant></root>' )

SELECT ID, x.value('(.)[1]', 'varchar(max)')
FROM @T t

Note that the .value() method has two parameters, an XQuery statement and a string representing the expected SQL Server type for the scalar value returned.  The results are as follows:

image

So, what is the XQuery statement requesting?  The axis is referring to the self-node of the XML fragment (and remember that in SQL Server’s implementation of XQuery, the query begins at the top of the document), there is no node test (so any node will satisfy the test), and the predicate is referring to the first position [1] of the path.  The .value() function returns the value found at that location, and converts it to a sql value of type varchar(max).  So what happens if there’s more than one value at a given XPath?

INSERT  INTO @T
        ( ID, x )
VALUES    ( 4, '<root><plant><fruit>tomato</fruit><fruit>banana</fruit><vegetable>tomato</vegetable></plant></root>' )


SELECT ID, x.value('(.)[1]', 'varchar(max)')
FROM @T t

The above script inserts a slightly more complex XML document than used before; there are now multiple nodes under the <plant> node (in addition to the three rows we already have); the output looks like:

image 

Note how row 4 has a single string that represents all of the values of the XML document.  Without a specific XQuery, the value() function returns the values that satisfy the request; in this case, all of them.  In the case where the XQuery cannot be satisfied, .value() will return a NULL value to the SQL Server engine, as illustrated by the following code examples:

SELECT ID, x.value('(//plant/fruit)[1]', 'varchar(max)')
FROM @T t 

SELECT ID, x.value('(//plant/fruit)[2]', 'varchar(max)')
FROM @T t 

The output should look like:

image

In the first query, we are looking for an axis of any node in the document, a node test of fruit under the <plant> node, and a positional predicate of 1.  Rows 1,2, and 4 satisfy this query; the XML document in row 3 doesn’t have a <fruit node>, and thus returns a NULL.  The second query is similar in that several of the XML documents in our table don’t satisfy the query; only the document in row 4 has node in the second [2] position of the <fruit> node.

Summary

Key takeaways:

  1. The .value() function allows you to specify an XQuery statement against an XML datatype looking for a specific value, and
  2. that returned value is cast to a SQL datatype.

Next up: .nodes()

SQL Server XQuery: .query() method

Continuing in my discussion of XQuery in SQL Server, let me discuss another XML data type method: .query().  The .query() method is a great illustration of the bridging concept I discussed earlier.  Simply put, this method allows you to run an XQuery against the XML data type, and return the results of that query as an XML document to SQL Server.  Using our example from the .exists() post:

DECLARE @T TABLE ( ID INT, x XML )

INSERT  INTO @T
        ( ID, x )
VALUES  ( 1, '<root><plant><fruit>tomato</fruit></plant></root>' )
,       ( 2, '<root><plant><fruit>banana</fruit></plant></root>' )
,       ( 3, '<root><plant><vegetable>tomato</vegetable></plant></root>' )

SELECT x.query('.') 
FROM @T 

Note that the parameter supplied to the method is an XQuery statement consisting of a single axis (.), which refers to the current node.  Since SQL Server’s implementation of XQuery starts at the top and works it’s way down the document, the current node is the entire document.  Running that query will give you the following output:

image

which isn’t very exciting.  However, if we begin to build out a XPath statement with a node test and a predicate, we can begin to narrow down the results quickly:

SELECT ID, x.query('.//fruit[text()="banana"]')
FROM @T t

And here’s the results:

image

While the results may be simple, there’s a lot going on behind the scenes, and it may have returned something different than what you expected.  Remember that when you execute an XQuery method against an XML column, there’s actually two queries happening:

For the XQuery portion, the XPath axis, node test, and predicate specified that we were looking for an XML node that was a fruit and had a value of a banana; this only existed in the XML in row 2, so the node was returned for row 2, but row 1 and row 3 were empty.  The SQL query then ran, and returned all rows in the table because there was no WHERE clause filtering the results.  If we only wanted to return the results where the XQuery returned a node, we could move the .query() method to the where clause.

One thing to always remember is that regardless of how specific your XQuery is, the type of the data being returned will always be XML.  For example, if you specify the text() function as part of the axis, and not the predicate:

SELECT ID, x.query('.//text()')
FROM @T t 

You’ll get what looks like a table with a column of character values representing the values of the various nodes:

image

However, the visual representation of the result set uses hyperlinks to indicate that it’s an XML column; if you click on the value (banana), the XML explorer will open up, showing you that it is an XML fragment, albeit one that has no tags.  If you need to return the data as the actual datatype, you’ll either need to use a SQL method (like CAST or CONVERT), or the .value() method (to be discussed soon).

Summary

Wrapping up with a few key points:

  1. The query() method allows you to specify an XQuery statement against an XML document, and
  2. It always returns an XML document.

SQL Server & XQuery: XPath Axes, Node Tests, and Predicates

In order to really get a grasp on what’s going on within an XQuery method in SQL Server (like .exist()), we need to examine the relationship between XQuery and XPath.  Semantically, there’s little difference in the usage of the two names, since XQuery as a language uses XPath to navigate through an XML fragment; however, strictly speaking, XPath is a subset of XQuery. 

XPath expressions contain one or more of three elements: an axis, a node test, and a predicate.  An axis refers to the locational reference of a path when navigating an xml structure.  For example consider the following XML:

<root>
  <plant>
    <fruit>tomato</fruit>
    <fruit>banana</fruit>
    <vegetable>tomato</vegetable>
  </plant>
  <animal>
    <canine>Doberman</canine>
  </animal>
</root>

and the XQuery below:

/root/plant/fruit

based on the axis (path) supplied above, the results will be the two <fruit> nodes in the XML document.  It’s important to remember that in the SQL Server implementation of XPath, axis expressions always start at the top of an XML document and work down, but there are some navigational shortcuts.  Below is a small table describing the axes supported:

Abbreviation Full Syntax Notes
/ / steps through nodes
// // steps through all descendant nodes
  child returns a specific child of a given node; e.g. /root/child::plant is the same as /root/plant
.. parent steps up a node; .. equivalent to parent::node()
@ attribute identified attribute-based XML
. self refers to the current node
* * refers to all nodes at the current level; e.g. /root/* will return both the <plant> and <animal> nodes from our previous example.

A node test is a part of the location path that when used with an axis restricts the XQuery to return only the results of interest. Taking our same XQuery from above:

/root/plant/fruit

The axis is the “/” symbol which tells XQuery to walk down a node at a time, and there are two node tests: plant, and fruit.  The first node test tells XQuery to only look at the plant nodes, and the second node test restricts it to just the fruit nodes.  The above XQuery could be shortened to:

//fruit

All nodes would be considered, and all fruit nodes would be returned, regardless of where they live in the XML hierarchy. 

The final expression in XPath is the predicate; a predicate limits the results of an XPath expression through evaluation (much like the WHERE clause of a SQL statement).  Predicates are specified using square brackets, and allow for the use of several functions to return either a positional reference or an evaluation of a value or node.  For example, the results of the following two XQuery expressions against our test XML document:

//fruit[2]

//fruit[last()]

would return the same positional results:

<fruit>banana</fruit>

likewise, the XQuery expression below:

//fruit[text()="banana"]

also returns the same results.  last() and text() are both XPath supported functions, and are part of the subset supported by Microsoft SQL Server.

Summary

Why is all of this important?  It’s dry, I know. However, the foundations of XPath allow us to do some pretty powerful querying of XML before handing the results “across the bridge” to SQL Server.  Now that we’ve got the basics identified, we can continue our previous discussion of the XML methods in SQL Server.

SQL Server XQuery: .exist() method

I’m trying to beef up my writing about XQuery in advance of the upcoming PASS Summit 2013; I got accepted to present, and I want to make sure that I start covering a lot of topics related to XML so that I’ve honed my focus a bit.  Unfortunately, blogging doesn’t come easy to me these days, and while I would generally start a session from the beginning and move forward, I think it would be best for me to start with something easy for me to write about, even though it means I’ll be doing a bit of backtracking later.

A little bit of background

As you may already know, SQL Server supports XQuery against an XML data type through the use of five basic methods:

  • .query()
  • .value()
  • .exist()
  • .modify()
  • .nodes()

Conceptually, each of these methods act a bridge between the the SQL processing engine and the xml parser baked into SQL Server.  This concept of a bridge allows me to think about where query work is being performed; is it being done as a SQL statement against the table containing the XML fragment of interest, or is it being performed by XQuery before bubbling up to the SQL level? 

image

.exist()

I want to focus on probably the easiest method against the xml datatype: .exist().  Simply put, this method checks to see whether or not the results of an XQuery expression against the supplied XML value are nonempty; it returns 1 if true, and 0 if false.  Let’s start with a simple sample:

DECLARE @T TABLE ( ID INT, x XML )

INSERT  INTO @T
        ( ID, x )
VALUES  ( 1, '<root><plant><fruit>tomato</fruit></plant></root>' )
,       ( 2, '<root><plant><fruit>banana</fruit></plant></root>' )
,       ( 3, '<root><plant><vegetable>tomato</vegetable></plant></root>' )


SELECT ID, X, x.exist('//root') AS "Does It Exist?" 
FROM @T 

Running this query will return the following results:

image

Every row has an XML value in the X column which contains a node named <root>.  We can change the output to identify rows which have vegetable nodes like so:

SELECT ID, X, x.exist('//root/plant/vegetable') AS "Does It Exist?" 
FROM @T 

The results then change to the following:

image

.exist() can also be used in the WHERE clause of a SQL statement; if we want to identify only the rows which have an XML fragment containing a tomato, we can alter the SQL statement like so:

SELECT ID, X  
FROM @T 
WHERE  x.exist('//.[text()="tomato"]') = 1

image

Note that the .exist() method interpreted an XQuery statement to look for nodes which had a text() value of “tomato”; two rows matched that requirement, so .exist() returned a 1 for those two rows to the calling SQL statement, and the WHERE clause identified those two rows.

Wrap-up

So, I’ve covered a few basic concepts while talking about the .exist() method:

  1. The xml methods act as a bridge between XQuery and SQL for the xml data types.
  2. The .exist() method supports XQuery expressions including both simple paths (//<node name>) and XQuery functions (e.g, text()).  This will become important later when I get around to discussing FLWOR.

#SQLSATATL simply rocked…

sqlsatatlSo, last Saturday, I went to the first SQL Saturday in Atlanta that I had absolutely no responsibility on the actual day of the event.  I wasn’t an organizer, I didn’t really even volunteer.  As a chapter leader for AtlantaMDF, I do have to go in and pay the bills later this week, but for the most part, I got to walk around and revel in the day. 

And it was a good day.  The team (led by Geoff Hiten) pulled off another great event; 555 people attended the event, and there were some AWESOME sessions throughout the day.  I was in the 9 am slot, and my session on Biggish Data went well, even if (as usual) I had WAY too many slides.  Speaking of slides, you should be able to download the deck from the SQLSaturday 220 schedule (as well as many other great presentations).  I did have to slip out early since this weekend was my weekend with the kids.  My current youngest (Grace) had to get service hours for beta club, so she got to hang out with me, but I needed to get back home early to hang out with the oldest one.

So what was cool?  What did I learn?

  1. The number one highlight for me was watching Louis Davidson demo Red Gate’s SQL Monitor tool, and he chose one of my custom metrics to use in the show.  I respect Louis a lot, and it made my day.
  2. I learned that I need to cut out about 10 slides out of my presentation.
  3. I had a great conversation with a few people about table partitioning and performance gains; I think there’s enough material for a future session explaining why you will or won’t see performance benefits from partitioning.  Hmmmm….
  4. I had another excellent conversation with Robert Cain regarding the growth of SQLSaturday’s, and bounced around a few ideas about moving to a mid-tier model.  Kind of like what SQLRally was supposed to become, but different. 
  5. I also got to hang out with Andy Leonard.   He and my Grace had a good time discussing movies (Where’s my super suit?).

Mostly, I just got jazzed about being back in the #sqlfamily.  Hopefully, I can build off that momentum, and start blogging again.   Granted, the next few months are going to be personally interesting, but I need to make time to share again.

Couple of upcoming presentations

I know; I suck at blogging.

Anyway, I have a couple of upcoming presentations this month, so I figured I needed to get back and gear and at least post a notice about them.  First, I’ll be presenting at A Bunch of Devs (http://www.meetup.com/A-Bunch-of-Devs/) on the Red Gate development suite of tools.  Funny story; I actually work in that building.  The organizers reached out to Red Gate to see if they had a Friend nearby.  I guess I qualified.

Next, I’ll be back at SQL Saturday Atlanta to present on Biggish Data; this is the first Atlanta SQL Saturday that I actually had almost nothing to do with (as a chapter leader, I helped with some basic decision, but very little).  I’m excited that it’s continuing to thrive.  Says a lot about the infrastructure that PASS puts behind these events; they just need a little help from the local chapters, but they don’t rely on the same person getting burned out year after year.

#SQLSatATL SQLSaturday Atlanta 220–May 18, 2013

Just a quick note; SQLSaturday is coming back to Atlanta on May 18, 2013.  This free (lunch is optional) event usually sells out way in advance, so you may want to go ahead and sign up.  Also, the speaker lineup tends to fill up pretty quickly (call for speakers closes 2/24), so if you’re a SQL Rock Star or just want to share your knowledge, go ahead and submit your session as well!

post #summit12 write-up

Everyone does one, so I figured I should Smile  Unfortunately, I barely recovered from my trip to Seattle, and am now rushing out the door to Dallas for company training, so I’m afraid this will be brief.  I do intend to blog more in the future (promises, promises), but for now, here’s the highlights:

  1. Day 1 Keynote rocked; it looks like SQL Server vNext will be an awesome release for DBA’s.   2008 and 2012 brought a lot of cool things to the table development/BI – wise, but less love for administrators (IMHO).  Hekaton will change that.
  2. I was really burning out on the whole chapter leader/SQL Saturday/community activist thing; three days in Seattle changed that.  We’re now planning Atlanta SQLSaturday 2013  (woo-hoo!!)
  3. The Chapter Leader meeting format was very effective, and a big difference from previous years.
  4. Session planning seemed a little “off” this year; too many people trying to cram into too small a space.  I missed some good sessions because the rooms were too full.
  5. I got bit by the cert bug, and passed exam 70-461 Querying Microsoft SQL Server 2012 on the second try.  I flunked it on Wednesday, got pissed off about it, crammed that night, and passed it on Thursday.  More on that later.

All in all, it was awesome.  I had a great time reconnecting with people, and I’m looking forward to the year to come.  Gotta run;  the Atlanta airport is an hour away.  I have no clue how you road warriors do this….