SideBar    Logical Puzzle
DOWNLOAD THE CODE:
Download the Code 93907.zip

Server's pivoting capabilities (also known as crosstab queries) have many practical applications. Given an input table expression, pivoting—which is essentially rotation of data—turns one column's values from multiple rows into multiple columns in a single result row. You can use pivoting in different ways, such as in reporting, custom aggregates, and relational division. Although SQL Server 2005's pivoting feature is useful, the PIVOT operator has some limitations. Here I'll show you some workarounds for these limitations, and next month I'll provide a flexible and more complete solution to dynamic-pivoting needs in SQL Server 2005. Before continuing, though, if you aren't familiar with the new PIVOT operator in SQL Server 2005, I suggest you read the Web-exclusive articles "Dynamic Pivoting," July 2004, InstantDoc ID 43140 and "Pivot (or Unpivot) Your Data," June 2004, InstantDoc ID 42901, which explain PIVOT; and "Dynamic Crosstab Queries," November 2000, InstantDoc ID 15608, which discusses dynamic pivoting in SQL Server 2000.

Implicit Grouping
Pivoting involves three phases: grouping, rotation, and aggregation.To understand these phases, let's consider an example. Suppose you're asked to pivot data from the Orders table in the Northwind database. You need to return the sum of freight for each employee and shipper, such that employee IDs (grouping column) would appear on rows, shipper IDs (rotation column) on columns, and the sum of freight (aggregation column) in the intersection of each employee and shipper, as Table 1 shows.

The grouping phase involves grouping the data by the grouping column EmployeeID. A row will be generated in the result for each employee. The rotation phase involves generating a result column for each element of the rotation column ShipVia. The aggregation phase involves aggregating the Freight column for each combination of grouping element (employee) and rotation element (shipper).

The PIVOT operator has a confusing aspect. Although you specify the rotation and aggregation columns explicitly in the PIVOT clause, SQL Server determines the grouping columns implicitly. The grouping columns are all columns from the table provided as input to the PIVOT operator, which aren't mentioned in the PIVOT clause as either aggregation or rotation columns. Thus, the following PIVOT query doesn't return the result you'd expect:

SELECT EmployeeID, [1], [2], [3] FROM dbo.Orders
  PIVOT(SUM(Freight)
  FOR ShipVia IN([1], [2],
  [3])) AS P; SQL 

(Some code in this article wraps to multiple lines because of page constraints.) Instead of getting nine rows in the result (one row for each employee) as Table 1 shows,you get 830 rows in the result: a row for each order instead of a row for each employee. Since the columns Freight and ShipVia were specified in the PIVOT clause as aggregation and rotation columns, respectively, SQL Server implicitly used all other columns in the table for grouping.

The workaround for the implicit-grouping limitation is simple. Instead of querying the base table (e.g., Orders) directly, provide the PIVOT operator a table expression (derived table or common table expression—CTE) as the input table, as Listing 1 shows. The table expression contains only the columns required for pivoting: grouping, rotation, and aggregation.

PIVOT Isn't Dynamic
You must specify a static list of rotation elements in the parentheses following the rotation column and the IN keyword. For example, the previous PIVOT query specifies the static list of shipper IDs [1],[2],[3]. You can't specify a subquery or ask SQL Server to determine the distinct rotation elements. This means that with a static query, you must know in advance which elements you want to rotate.

The only workaround for this limitation is to use dynamic SQL.You query the distinct elements that you need to rotate, construct the PIVOT query string, and execute the query string by using sp_executesql or EXEC.The code in Listing 2 demonstrates how to construct the list of shipper IDs with a FOR XML PATH query. This code returns the string [1],[2],[3].

There are other techniques you can use to concatenate strings (e.g., by using a cursor), but the technique that uses the FOR XML PATH option is by far simpler and faster than the alternatives. (I describe this technique in "Custom Aggregations: Specialized Solutions," July 2006, InstantDoc ID 50269.)

If you don't have a table like Shippers with a row for each distinct rotation element, you can query the distinct rotation elements from the table used as the input to the PIVOT operator (Orders, in our case), as the code in Listing 3 shows. You can then construct the full query string and execute it as Listing 4 shows.

   Prev. page   [1] 2     next page



You must log on before posting a comment.

If you don't have a username & password, please register now.