 Friday, February 29, 2008
Yesterday we discussed the changes to XQuery by allowing LET statements. Today the last XML enhancement is an "enhancement" to XML DML. XML Data Manipulation Language (DML) is specific to MS SQL Server 2005 and 2008. It is a very useful feature that allows you to insert nodes to an existing XML instance, delete an element, or replace the value of (sort of an edit.) It works like this: First let's create a table with an XML data type and insert a piece of XML into it: Create table XMLTest1 ( Speaker_ID int primary key, SpeakerXML xml not null ) GO Insert into XMLTest1 Values ( 1, ' <classes> <class name="Writing Secure Code for ASP .NET " /> <class name="Using XQuery in SQL Server 2008" /> <class name="SQL Server and Oracle Working Together" /> </classes> ' ) Now to insert an element into this table it is pretty easy, just use the XML modify method of the XML data type. Since our XML instance is in a SQL Server table we have to use the standard SQL UPDATE and SET syntax with a WHERE clause to get to the piece of XML in our table we are concerned with. You can see how easy it is to add an element to the table here: Update XMLTest1 Set SpeakerXML.modify( 'insert <class name="Using Linq to SQL" /> into /classes[1]' ) Where Speaker_ID=1 If we select * from XMLTest1 WHERE Speaker_ID=1 the XML column looks like this now: <classes> <class name="Writing Secure Code for ASP .NET " /> <class name="Using XQuery in SQL Server 2008" /> <class name="SQL Server and Oracle Working Together" /> <class name="Using Linq to SQL" /> </classes> SQL Server 2008 has an enhancement to this. You can substitute the XML element with a variable. For example let's create a variable here: DECLARE @newElement xml Set @newElement='<class name="ASP.NET Scalability"/>' Now let's do the same insert as before, however, we will use the @newElement variable. This will enable you to select XML from other areas and stick it into a variable and then insert it into another XML instance. Here is the final piece: Update XMLTest1 Set SpeakerXML.modify( 'insert sql:variable("@newElement") into /classes[1]' ) Where Speaker_ID=1 If we select * from XMLTest1 WHERE Speaker_ID=1 the XML column looks like this now: <classes> <class name="Writing Secure Code for ASP .NET " /> <class name="Using XQuery in SQL Server 2008" /> <class name="SQL Server and Oracle Working Together" /> <class name="Using Linq to SQL" /> <class name="ASP.NET Scalability" /> </classes> Not the greatest of new features, but a handy dandy feature to say the least.
 Thursday, February 28, 2008
Continuing our discussion of XML in SQL Server 2008, SQL Server 2005 alowed us to use XQuery to query XML data in an XML data type. You can use the FLWOR statements of for, where, order by, and retun, but not LET. SQL Server 2008 now has support for LET, which is used to assign values to variables in an XQuery expression. For example we are going to take the XML below and count the number of sessions per speaker using the count XQuery expression and assign it to a variable via LET. declare @xml xml set @xml= ' <Speakers> <Speaker name="Stephen Forte"> <classes> <class name="Writing Secure Code for ASP .NET "/> <class name="Using XQuery in SQL Server 2008"/> <class name="SQL Server and Oracle Working Together"/> </classes> </Speaker> <Speaker name="Richard Campbell"> <classes> <class name="SQL Server Profiler"/> <class name="Advanced SQL Querying Techniques"/> <class name="SQL Server and Oracle Working Together"/> <class name="From 1 Server to 2"/> </classes> </Speaker> </Speakers> ' SELECT @xml.query( '<Speakers> { for $Speaker in /Speakers/Speaker let $count :=count($Speaker/classes/class) order by $count descending return <Speaker> {$Speaker/@name} {$Speaker/count} <SessionCount>{$count}</SessionCount> </Speaker> } </Speakers>') The result is shown here, notice that Richard sorts on top since we used the descending order to our order by and ordered by the expression we created via the LET statement. This is very helpful if you are manipulating simple XML and need to do a mini-transform via XQuery. I used to use the RETURN statement all the time to make slight manipulations to my XML, so I was waiting for LET. While this is the only addition to XQuery, it is a much needed and welcome one. <Speakers> <Speaker name="Richard Campbell"> <SessionCount>4</SessionCount> </Speaker> <Speaker name="Stephen Forte"> <SessionCount>3</SessionCount> </Speaker> </Speakers>
 Wednesday, February 27, 2008
Continuing our discussion from yesterday on SQL Server 2008 XML Enhancements, let's take a look at the XSD enhancement of Union and List types. In SQL Server 2005 you could define a simple type of xsd:list with an enumeration to require items in a list for any element that inherits that type. For example our XSD below will allow FedEx, DHL, and UPS for a shipping. create XML SCHEMA COLLECTION dbo.order_xsd AS '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="orderAmt" > <xsd:restriction base="xsd:int" > <xsd:maxInclusive value="5000" /> <xsd:minExclusive value="1" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="shiptypeList"> <xsd:list> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="FexEx"/> <xsd:enumeration value="DHL"/> <xsd:enumeration value="UPS"/> </xsd:restriction> </xsd:simpleType> </xsd:list> </xsd:simpleType> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="CustomerName" type="xsd:string" /> <xsd:element name="OrderDate" type="xsd:dateTime"/> <xsd:element name="OrderAmt" type="orderAmt"/> <xsd:element name="ShipType" type="shiptypeList"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>' GO DECLARE @xml XML(dbo.order_xsd) SET @xml = '<Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>100</OrderAmt> <ShipType>DHL</ShipType> </Order>' GO So this is cool, gives you some database lookup functionality, kind of third normal form Clemens Vasters style. With the new support for union of lists in SQL Server 2008 with xsd:union, you can combine multiple lists to one simple type. For example in our XSD below we allow the same strings as FedEx, etc, but also numerical values. My example below is pretty basic, but this is useful if you have more than one way to describe something and need two lists to do so. One thing that comes to mind is units of measurement, metric and English measurements, so shoe sizes, US and Italian, etc. This is very useful when you need to restrict items and are writing them from a database, etc. Take a look at the demo for SQL 2008 below, notice that I changed the data to 1 from FedEx: create XML SCHEMA COLLECTION dbo.order_xsd AS '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="orderAmt" > <xsd:restriction base="xsd:int" > <xsd:maxInclusive value="5000" /> <xsd:minExclusive value="1" /> </xsd:restriction> </xsd:simpleType> <xsd:simpleType name="shiptypeList"> <xsd:union> <xsd:simpleType> <xsd:list> <xsd:simpleType> <xsd:restriction base="xsd:integer"> <xsd:enumeration value="1"/> <xsd:enumeration value="2"/> <xsd:enumeration value="3"/> </xsd:restriction> </xsd:simpleType> </xsd:list> </xsd:simpleType> <xsd:simpleType> <xsd:list> <xsd:simpleType> <xsd:restriction base="xsd:string"> <xsd:enumeration value="FedEx"/> <xsd:enumeration value="DHL"/> <xsd:enumeration value="UPS"/> </xsd:restriction> </xsd:simpleType> </xsd:list> </xsd:simpleType> </xsd:union> </xsd:simpleType> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="CustomerName" type="xsd:string" /> <xsd:element name="OrderDate" type="xsd:dateTime"/> <xsd:element name="OrderAmt" type="orderAmt"/> <xsd:element name="ShipType" type="shiptypeList"/> <xsd:any namespace="##other" processContents="lax"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>' GO DECLARE @xml XML(dbo.order_xsd) SET @xml = '<Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>100</OrderAmt> <ShipType>1</ShipType> <Notes xmlns="sf">Steve Test 1</Notes> </Order>' GO
 Tuesday, February 26, 2008
The SQL Server 2008 February CTP is now installed. I did it on my daily machine to force me to use it in a more realistic environment. It installed a-ok with my default SQL 2005 instance. As you can see below you can use the SQL 2008 IDE to manage and work with both SQL Server instances on your computer at the same time. I am playing with the new XML features of SQL Server. For those of you that know my old company, Corzen, used the XML data type to do some really cool stuff. I'm now investigating the XML features for the update to the Programming SQL Server 2008 book from MS Press. I guess that XLINQ is pretty cool and well accepted Microsoft did not change all that much in the XML world in SQL Server 2008. The new XML features of SQL Server 2008 are: - Lax validation support in XSD-giving you some more flexibility with your required areas of the XSD
- Full support of the XSD: xs:dateTime, basically the preservation of time zones
- Union and List types in XSDs (I have not played with this yet so can't speak to how useful it will be yet, give me a day)
- Support of the LET FLOWR expression in XQuery (yay!)
- XML DML "enhancements." I put that in quotes for two reasons, there is only one true enhancement, allowing you to insert data from a SQL column or a SQL variable as part of your insert. This is a very minor enhancement, I was hoping for a new syntax for specifying position, etc. Also XML DML is SQL Server specific, so it is hard to get very excited about this feature.
Now let's take a look at some of the XSD enhancements. SQL Server 2005 added the ability put XML data into an intrinsic XML data type. This was a vast improvement over SQL Server 2000 where we had to put data in text fields. In addition, SQL Server 2005 allowed us to restrict this column with an XSD. Consider this XSD: <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="orderAmt" > <xsd:restriction base="xsd:int" > <xsd:maxInclusive value="5000" /> <xsd:minExclusive value="1" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="CustomerName" type="xsd:string" /> <xsd:element name="OrderDate" type="xsd:dateTime"/> <xsd:element name="OrderAmt" type="orderAmt"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema> We have defined a few elements, given them a sequence and data types. We also say that you can only have an order amount between 1 and 5000 (work with me, I like simple demos) since the OrderAmt element inherits the simple type orderAmt. This works well, for example if we create an XSD and a table with an XML column bound to that XSD in SQL 2005: --Valid SQL Server 2005 Schema CREATE XML SCHEMA COLLECTION dbo.order_xsd AS '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="orderAmt" > <xsd:restriction base="xsd:int" > <xsd:maxInclusive value="5000" /> <xsd:minExclusive value="1" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="CustomerName" type="xsd:string" /> <xsd:element name="OrderDate" type="xsd:dateTime"/> <xsd:element name="OrderAmt" type="orderAmt"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>' GO CREATE TABLE xml_schematest ( Order_ID int primary key, Order_XML XML(order_xsd) --XML Schema Name )
Now let's insert some XML in there, the XML will be like this: Insert into xml_schematest VALUES (1, '<Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>100</OrderAmt> </Order> ') Here is the result, notice the Z for UTC as well as the new time (more on that below): <Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T19:22:27.250Z</OrderDate> <OrderAmt>100</OrderAmt> </Order> This XML will fail since it has an order over 5000. Insert into xml_schematest VALUES (2, '<Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>10000</OrderAmt> </Order> ') So life is good. We have a nice XSD and it restricts our content. SQL Server 2008 has added two new features of an XSD that we will look at today. The first is something called Lax validation. Let's say that you wanted to add an additional element after <OrderAmt> that is not part of the same schema. In SQL Server 2005, schemas can use processContents values of skip and strict for any and anyAttribute declarations as a wildcard. If it was set to skip, SQL will skip completely the validation of the additional element, if it is set to strict, SQL will require that it has an element or namespace defined in the current schema. SQL Server 2008 adds support for an additional validation option: lax. By setting the processContents attribute for this wildcard section to lax, you can enforce validation for any elements that have a schema associated with them, but ignore any elements that are not defined in the schema. Pretty cool, no? Also the xsd:dateTime XML data type is now timezone aware. SQL Server 2005 you had to provide a time zone for dateTime, time and date data (did you really ever do that? If you needed the flexibility, trust me, this was a pain). What was not cool was that SQL Server 2005 did not preserve the time zone information for your data for dateTime or time, it normalizes it to UTC (so for example 2008-10-10T08:00:00:000-5:00 is normalized to 2008-10-10T13:00:00:000Z. Notice the Z for UTC. Quite annoying.) In SQL Server 2008, this has been removed! Take a look at this code: CREATE XML SCHEMA COLLECTION dbo.order_xsd AS '<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <xsd:simpleType name="orderAmt" > <xsd:restriction base="xsd:int" > <xsd:maxInclusive value="5000" /> <xsd:minExclusive value="1" /> </xsd:restriction> </xsd:simpleType> <xsd:element name="Order"> <xsd:complexType> <xsd:sequence> <xsd:element name="CustomerName" type="xsd:string" /> <xsd:element name="OrderDate" type="xsd:dateTime"/> <xsd:element name="OrderAmt" type="orderAmt"/> <xsd:any namespace="##other" processContents="lax"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>' GO CREATE TABLE xml_schematest ( Order_ID int primary key, Order_XML XML(order_xsd) --XML Schema Name ) GO Now this will work and even preserve the time zone of -5 (New York) and notice the random <Notes> element due to lax validation. Here is how to insert the XML: Insert into xml_schematest VALUES (1, '<Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>100</OrderAmt> <Notes xmlns="sf">Steve Test 1</Notes> </Order> ') Here are the results in the database with the original time zone: <Order> <CustomerName>Bill Gates</CustomerName> <OrderDate>2008-10-10T14:22:27.25-05:00</OrderDate> <OrderAmt>100</OrderAmt> <Notes xmlns="sf">Steve Test 1</Notes> </Order> Tomorrow I will look at Union and List types.
 Monday, February 14, 2005
I know this. (Ask me if I care.) Anyway, all of those folks who dislike me usually say "is there a way to shut him up." Well there is a way!!!! Have me train for a marathon. Ran 18 miles on Saturday and then a 9.3 mile race on Sunday inside the “gates“ in Central Park. I was very quiet all weekend. (Marathon in 12 days)
Another thing people thought was impossible was referencing a RANK()ing function in a WHERE clause in SQL Server 2005 or using an aggregate with the Ranking and Windowing functions. A simple solution that I did not see in any of the docs, use a Common Table Expression. Take this simple aggregate, rounding to the nearest 100th the sales figure from AdventureWorks;
Select CustomerID,round(convert(int, sum(totaldue))/100,8) *100 as totalamt
From Sales.SalesOrderHeader
Group by CustomerID
Gives you results like:
CustomerID totalamt
----------- -----------
22814 0
11407 0
28387 600
15675 7900
18546 0
(and so on)
What if you want to rank them? Easy, make the aggregate a CTE and rank over the new field:
--rank by totaldue, summed and rounded (nearest 100)
--need a CTE to do a sum & rounding
--so this example will have a
--customerID summed with all of
--their orders
With CustomerSum
As
(
Select CustomerID,round(convert(int, sum(totaldue))/100,8) *100 as totalamt
From Sales.SalesOrderHeader
Group by CustomerID
)
Select *,
Rank() Over (Order By totalamt Desc) as Rank
From CustomerSum
Results:
CustomerID totalamt Rank
----------- ----------- --------------------
678 1179800 1
697 1179400 2
170 1134700 3
328 1084400 4
514 1074100 5
(and so on)
Ditto if you want to filter a query by the results of a ranking function. Just move the ranking function up to the CTE:
--use a common table expression if you want
--to filter by one of the rows that contain a
--ranking function since ranking functions
--are not allowed in where or having clauses
With NumberRows
As
(
Select SalesOrderID, CustomerID,
Row_Number() Over (Order By SalesOrderID) as RowNumber
From Sales.SalesOrderHeader
)
Select * from NumberRows
where RowNumber between 100 and 200
Order By SalesOrderID
Resutls:
SalesOrderID CustomerID RowNumber
------------ ----------- --------------------
43758 27646 100
43759 13257 101
43760 16352 102
43761 16493 103
43762 27578 104
(and so on)
 Monday, January 17, 2005
Yukon has enhanced the SQL Server 2000 FOR XML function. If you want to create element based XML (yay!), FOR XML PATH allows you to specify column aliases that contain valid XPath expressions that will shape your XML output.
--XML FOR PATH
Select
ContactID as [@Contact_ID],
FirstName as [ContactName/First],
LastName as [ContactName/Last],
Phone as [ContactPhone/Phone1]
From Person.Contact For XML PATH
Produces output like:
<Customer>
<CustomerID>1</CustomerID>
<OrderDetail>
<SalesOrderID>43860</SalesOrderID>
<OrderDate>2001-08-01T00:00:00</OrderDate>
</OrderDetail>
<OrderDetail>
<SalesOrderID>44501</SalesOrderID>
<OrderDate>2001-11-01T00:00:00</OrderDate>
</OrderDetail>
</Customer>
<Customer>
<CustomerID>2</CustomerID>
<OrderDetail>
<SalesOrderID>46976</SalesOrderID>
<OrderDate>2002-08-01T00:00:00</OrderDate>
</OrderDetail>
<OrderDetail>
<SalesOrderID>49054</SalesOrderID>
<OrderDate>2003-02-01T00:00:00</OrderDate>
</OrderDetail>
<OrderDetail>
</Customer>
If you are familiar and comfortable with XPath, there are some additional features to XML PATH you may like. You can use the following XPath node test functions to further control the shape of your XML output:
· node()
· text()
· data()
· comment()
· processing-instruction()
 Tuesday, January 11, 2005
The XML Data Type can, just like other data types, conform to Nullability, Defaults and Constraints. If you want to make the field required (NOT NULL) and provide a default value on your XML column, just specify like you would any other column.
CREATE TABLE OrdersXML
(OrderDocID INT PRIMARY KEY,
xOrders XML NOT NULL Default '</Orders>'
This insert will work because it is relying on the default:
Insert Into OrdersXML (OrderDocID, xOrders) Values (2, DEFAULT)
The following insert will work, even if we add non <Orders> in our table because we have not declared a constraint on the column:
Insert Into OrdersXML (OrderDocID, xOrders) Values (3, 'steve')
SQL Server 2005 gives you the opportunity to add a constraint. An interesting feature of an XML column is that the CHECK constraint is based on the XML Infoset model using the methods of the XML column. You can use the exist method, which is specific to the XML type, as part of our CHECK constraint to force a particular type of XML element to exist. In the following example we will drop and recreate the table from scratch (you can use ALTER TABLE as well) adding a default and CHECK CONSTRAINT.
Drop Table OrdersXML
--create the table with the XML Datatype
--using a default value
--also has the check constraints
CREATE TABLE OrdersXML
(OrderDocID INT PRIMARY KEY,
xOrders XML NOT NULL Default '<Orders>'
CONSTRAINT xml_orderconstraint
CHECK(xOrders.exist('/Orders')=1))
Now this example from before will fail.
Insert Into OrdersXML (OrderDocID, xOrders) Values (3, 'steve')
When you attempt to insert data that will violate the xml_orderconstraint you get the following error message from SQL Server:
Msg 547, Level 16, State 0, Line 1
INSERT statement conflicted with CHECK constraint
'xml_orderconstraint'. The conflict occurred in database
'AdventureWorks', table 'OrdersXML', column 'xOrders'.
The statement has been terminated.
 Wednesday, December 22, 2004
SQL Server 2005 introduces an enhancement to the OPENROWSET function, the new BULK rowset OLE DB provider. Via OPENROWSET, it lets you access data in files in a relational fashion. In the past, you used BULK INSERT to pull data from the file into a temp table and then ran your relational operations, but with SQL Server 2005, you can use the data contained in the file as part of your SQL statement rowset results. Keep in mind that you must specify a format file, which is the same format file you use with bcp.exe or the BULK INSERT statement. The following code shows how to access a file named c:\bulk.txt using the format file c:\bulk.fmt:
SELECT customerid, customername, totalorders
FROM OPENROWSET(BULK 'c:\bulk.txt',
FORMATFILE = 'c:\bulk.fmt') AS (customerid, customername, totalorders)
 Tuesday, December 21, 2004
SQL Server 2005 adds a new feature called Common Table Expressions (CTE). The true power of CTEs emerges when you use them recursively to perform hierarchical queries on tree structured data. In fact, besides SQL-92 compliance, this was the main reason Microsoft built CTEs. A recursive CTE is constructed from a minimum of two queries, the first, or anchor member (AM), is a nonrecursive query, and the second, or recursive member (RM), is the recursive query. Within your CTE’s parentheses (after the AS clause), you define queries that are either independent or refer back to the same CTE. The AM and RM are separated by a UNION ALL statement. Anchor members and are invoked only once and are invoked repeatedly until the query returns no rows. Multiple AMs can be appended to each other using either a UNION or a UNION ALL operator, depending on whether you want to eliminate duplicates. (You must append recursive members using a UNION ALL operator.) Here is the syntax:
With SimpleRecursive( field names)
As
(
<Select Statement for the Anchor Member>
Union All
<Select Statement for the Recursive Member>
)
Select * From SimpleRecursive
To demonstrate this feature, I will create an example here. We create a table with employees and a self referencing field back to Employee_ID called ReportsTo (I call this a Domestic Key in lue of a Foreign Key). We are going to write a query that returns all the employees who report to Stephen (Employee_ID=2) and all the employees who report to Stephen’s subordinates:
--create a table with tree data
--Reportsto is a "domestic key" back to Employee_id
create table Employee_Tree (Employee_NM nvarchar(50), Employee_ID int Primary Key, ReportsTo int)
--insert some data, build a reporting tree
insert into employee_tree values('Richard', 1, 1)
insert into employee_tree values('Stephen', 2, 1)
insert into employee_tree values('Clemens', 3, 2)
insert into employee_tree values('Malek', 4, 2)
insert into employee_tree values('Goksin', 5, 4)
insert into employee_tree values('Kimberly', 6, 1)
insert into employee_tree values('Ramesh', 7, 5)
Our table looks like this:
|
Employee_NM |
Employee_ID |
ReportsTo |
|
Richard |
1 |
null |
|
Stephen |
2 |
1 |
|
Clemens |
3 |
2 |
|
Malek |
4 |
2 |
|
Goksin |
5 |
4 |
|
Kimberly |
6 |
1 |
|
Ramesh |
7 |
5 |
Now the recursive query to determine all the employees who will report to Stephen:
--Recursive Query
WITH SimpleRecurvice(Employee_NM, Employee_ID, ReportsTO)
AS
(SELECT Employee_NM, Employee_ID, ReportsTO
FROM Employee_Tree WHERE Employee_ID = 2
UNION ALL
SELECT p.Employee_NM, p.Employee_ID, p.ReportsTO
FROM Employee_Tree P INNER JOIN
SimpleRecurvice A ON A.Employee_ID = P.ReportsTO
)
SELECT Employee_NM FROM SimpleRecurvice
Employee_NM
--------------------------------------------------
Stephen
Clemens
Malek
Goksin
Ramesh
(5 row(s) affected)
This recursion starts where Employee_ID= 2 (the ANCHOR MEMBER or the first SELECT). It picks up that record and then, via the RECURSIVE MEMBER (the SELECT after the UNION ALL), picks up all of the records that report to Stephen and that record’s children (Goksin reports to Malek and Malek reports to Stephen). Each subsequent recursion tries to find more children that have as parents the employees found by the previous recursion. Eventually the recursion returns no results and that is what causes the recursion to stop (the reason why Kimberly is not returned).
 Friday, December 10, 2004
The ranking functions can also combine with windowing functions. A windowing function will divide a resultset into equal partitions based on the values of your PARTITION BY statement in conjunction with the OVER clause in your ranking function. It is like applying a GROUP BY to your ranking function-you get a separate ranking for each partition. The example below uses ROW_NUMBER with PARTITION BY to count the number of orders by order date by salesperson. We do this with a PARTITION BY SalesPersonID OVER OrderDate. This can be used with any of the four ranking functions.
Select SalesOrderID, SalesPersonID, OrderDate,
Row_NUMBER() Over (Partition By SalesPersonID Order By OrderDate) as OrderRank
From Sales.SalesOrderHeader
Where SalesPersonID is not null
SalesOrderID SalesPersonID OrderDate OrderRank
------------ ------------- ----------------------- ---
43659 279 2001-07-01 00:00:00.000 1
43660 279 2001-07-01 00:00:00.000 2
|