In a previous post I talked about performing "inner joins" in XQuery. The basic idea is that the inner join is simply a special case of a Cartesian product, or "cross join". In this post I'm going to briefly look at another common SQL-style join condition that can be duplicated to some degree in XQuery. Specifically I'm going to give an example of a retrieving nodes from a tuple stream that don't match the nodes in a second tuple stream.
In this post we'll duplicate about 50% of the SQL "left outer join" functionality. For those who aren't familiar with SQL left outer joins, they can be thought of as an inner join between two tables unioned together with the rows of the left-hand table that have no corresponding rows in the right-hand table.
For this example I'm going to borrow some slightly modified XML data from the previous post:
DECLARE @xml xml;-- Create sample XML documentSET @xml = N'<authors> <author id = "1">Fabio Claudio Ferracchiati</author> <author id = "2">Hugo Kornelis</author> <author id = "3">Rob Walters</author> <author id = "4">Lara Rubbelke</author> <author id = "5">Adam Machanic</author> <author id = "6">Michael Coles</author> <author id = "7">Robin Dewson</author> <author id = "8">Jan D. Narkiewicz</author> <author id = "9">Robert Rae</author></authors><books> <book title = "Pro T-SQL 2005 Programmer's Guide"> <isbn>159059794X</isbn> <author>6</author> </book> <book title = "Accelerated SQL Server 2008"> <isbn>1590599691</isbn> <author>3</author> <author>6</author> <author>7</author> <author>1</author> <author>8</author> <author>9</author> </book> <book title = "Pro SQL Server 2008 XML"> <isbn>1590599837</isbn> <author>6</author> </book></books>';-- Perform outer joinSELECT @xml.query('for $author in /authors/author where fn:empty($author[@id = /books/book/author]) return <author> { $author } </author>');
This sample is available as a download file here.
The XML data consists of a list of authors and a list of books. The query uses a FLWOR expression to retrieve all authors who don't have a corresponding book node. Here's how it works:
The results are shown below:
<author> <author id="2">Hugo Kornelis</author></author><author> <author id="4">Lara Rubbelke</author></author><author> <author id="5">Adam Machanic</author></author>
As you can see, the three authors with id's of (2, 4, 5) were returned because they have no corresponding book nodes in the XML data. This technique, combined with the previously discussed inner join technique, can be used to simulate SQL style outer joins in XQuery.
One of the more common uses of triggers is to create data manipulation language (DML) logging functionality. Essentially you can intercept and log inserts, updates, and deletes to tables. In the simplest case it's enough to know that one of these operations has occurred on a table, the date/time that it occurred, and some sort of identifying information for the user that performed the operation. At the other end of the requirements spectrum is the need to identify all information, including data indicating what exactly has changed.
What you usually find is that people who need to log DML operations will write one trigger and modify the same basic code for every other table that needs to be logged. I know one person who even wrote a client-side utility to automatically generate custom trigger code for hundreds of tables that he had to log. One problem with this method, apart from the sheer boredom of modifying the same trigger, again and again, is maintenance. If the underlying table structure changes your trigger is suddenly shot. In this post we're going to use FOR XML and SQL Server catalog views to create a dynamic trigger that will work on just about any table, and will automatically adjust it's output if the table structure changes.
CREATE
Next we’ll create a dynamic trigger. By dynamic I mean that you can run this script against any table and it will create a trigger that automatically detects its parent table and schema and logs all DML actions against the table properly, regardless of table structure. The example below creates the dynamic trigger on the AdventureWorks HumanResources.Shift table.
-- Change the schema and table name to match any-- existing table in your database
INSERT
Each entry has information like the schema name, table name, date/time, user name, etc. The Changes column is an XML data type column with the contents of the inserted and deleted virtual tables in XML format. The image below shows the results of the sample UPDATE statement above.
This type of dynamic logging is especially useful when you have to log DML activity for several tables in a transactional system. A couple of caveats: This trigger may require some changes if your table contains LOB data type columns. Also always take care when using triggers on high-DML activity tables, as triggers of any kind can affect performance.
SQL Server Standard magazine will be publishing more of these tips for getting the most out of SQL Server XML in a future issue.
Hi everyone, in this blog I plan to discuss the new SQL Server 2005 and SQL Server 2008 XML features. While I have several topics I plan to cover already, if you have specific SQL Server + XML related questions, feel free to contact me via this blog and I'll try to answer, or at least help you get a little closer to the answer.
I decided to kick this off with a simple example to demonstrate the utility of XML in SQL Server. SQL Server 2008 and 2005 store cached XML query plans that are accessible via the sys.dm_exec_query_plan dynamic management function. This function takes a query plan handle as an argument and returns the XML query plan, along with some other metadata.
While I was at the PASS Conference in Denver, I threw together a quick example to demonstrate shredding the cached XML query plans on SQL 2005. I expanded it a little bit to include some other information from the sys.dm_exec_sql_text dynamic management function and the sys.dm_exec_cached_plans dynamic management view.
This sample query uses a few of the new features available starting with SQL 2005: the CROSS APPLY operator, Common Table Expressions (CTEs), dynamic management views/functions, and the ROW_NUMBER() windowing function.
The most important feature we're using here is the xml data type and its nodes() and value() methods. Since the XML query plans are stored as xml data type data, we are using the nodes() method to shred the XML data nodes into rows. Then we apply the value() method to each of these new rows to extract individual query plan operators and operator information from the plan. In this case we pull the physical operator name ("Nested Loops", "Filter", etc.) and the estimated subtree cost for each operator. As a bonus we are grabbing the initial SQL statement that is the basis for the plan from the sys.dm_exec_sql_text dynamic management view. That way we can easily relate the query plan, and its individual operators, back to the source SQL statement.
This is just one example of the capabilities of the xml data type. In upcoming posts I'll describe more precisely how the xml data type methods and XQuery work.