.Net SqlClient Data Provider:Msg 8167, Level 16, State 1, Line 1 Type of column 'whenmade' conflicts with the type of other columns specified in the UNPIVOT list.
目标 value 列包含源自多个不同源列(那些出现在<column_list> 中的列)的值。因为所有列值的目标是单个列,所以 UNPIVOT 要求<column_list> 中的所有列都具有相同的数据类型、长度和精度。要满足该限制,可以向 UNPIVOT 运算符提供一个表表达式,以便将这三个列转换为相同的数据类型。sql_variant 数据类型是一个良好的候选类型,因为您可以将不同的源列转换为相同的数据类型,并且仍然保留它们的原始数据类型。应用该限制,您可以按如下方式修改上一个查询并获得所需的结果:
SELECT itemid, attribute, value FROM (SELECT itemid, CAST(itemtype AS SQL_VARIANT) AS itemtype, CAST(whenmade AS SQL_VARIANT) AS whenmade, CAST(initialprice AS SQL_VARIANT) AS initialprice FROM AuctionItems) AS ITM UNPIVOT ( value FOR attribute IN([itemtype], [whenmade], [initialprice]) ) AS UPV
结果 attribute 列的数据类型为 sysname 。这是 SQL Server 用于存储对象名称的数据类型。
请注意,UNPIVOT 运算符从结果中消除了 value 列中的空值;因此,不能将其视为 PIVOT 运算符的严格逆操作。
在将 AuctionItems 中的列旋转为行之后,您现在可以将 UNPIVOT 操作的结果与 ItemAttributes 中的行合并,以提供统一的结果:
SELECT itemid, attribute, value FROM (SELECT itemid, CAST(itemtype AS SQL_VARIANT) AS itemtype, CAST(whenmade AS SQL_VARIANT) AS whenmade, CAST(initialprice AS SQL_VARIANT) AS initialprice FROM AuctionItems) AS ITM UNPIVOT ( value FOR attribute IN([itemtype], [whenmade], [initialprice]) ) AS UPV UNION ALL SELECT * FROM ItemAttributes ORDER BY itemid, attribute
以下为结果集:itemid attribute value ----------- --------------- ------------- 1 color Red 1 initialprice 3000.00 1 itemtype Wine 1 manufacturer ABC 1 type Pinot Noir 1 whenmade 1822 2 color Red 2 initialprice 500.00 2 itemtype Wine 2 manufacturer XYZ 2 type Porto 2 whenmade 1807 3 initialprice 800000.00 3 itemtype Chair 3 material Wood 3 padding Silk 3 whenmade 1753 4 initialprice 1000000.00 4 inscription One ring 4 itemtype Ring 4 material Gold 4 size 10 4 whenmade -501 5 height 19.625 5 initialprice 8000000.00 5 itemtype Painting 5 name Field of Poppies 5 artist Claude Monet 5 type Oil 5 whenmade 1873 5 width 25.625 6 height 28.750 6 initialprice 8000000.00 6 itemtype Painting 6 name The Starry Night 6 artist Vincent Van Gogh 6 type Oil 6 whenmade 1889 6 width 36.250
APPLY
APPLY 关系运算符使您可以针对外部表表达式的每个行调用指定的表值函数一次。您可以在查询的 FROM 子句中指定 APPLY,其方式与使用 JOIN 关系运算符类似。APPLY 具有两种形式:CROSS APPLY 和 OUTER APPLY。通过 APPLY 运算符,SQL Server 2005 Beta 2 使您可以在相关子查询中引用表值函数。
CROSS APPLYCROSS APPLY 为外部表表达式中的每个行调用表值函数。您可以引用外部表中的列作为该表值函数的参数。CROSS APPLY 从该表值函数的单个调用所返回的所有结果中返回统一的结果集。如果该表值函数对于给定的外部行返回空集,则不会在结果中返回该外部行。例如,以下表值函数接受两个整数作为参数,并且返回带有一个行的表 — 该表用最小值和最大值作为列:
CREATE FUNCTION dbo.fn_scalar_min_max(@p1 AS INT, @p2 AS INT) RETURNS TABLE AS RETURN SELECT CASE WHEN @p1 < @p2 THEN @p1 WHEN @p2 < @p1 THEN @p2 ELSE COALESCE(@p1, @p2) END AS mn, CASE WHEN @p1 > @p2 THEN @p1 WHEN @p2 > @p1 THEN @p2 ELSE COALESCE(@p1, @p2) END AS mx GO SELECT * FROM fn_scalar_min_max(10, 20)
以下为结果集:mn mx ----------- ----------- 10 20
给定下面的 T1 表:
CREATE TABLE T1 ( col1 INT NULL, col2 INT NULL ) INSERT INTO T1 VALUES(10, 20) INSERT INTO T1 VALUES(20, 10) INSERT INTO T1 VALUES(NULL, 30) INSERT INTO T1 VALUES(40, NULL) INSERT INTO T1 VALUES(50, 50)
您希望为 T1 中的每个行调用 fn_scalar_min_max。您可以按如下方式编写 CROSS APPLY 查询:
SELECT * FROM T1 CROSS APPLY fn_scalar_min_max(col1, col2) AS M
以下为结果集:col1 col2 mn mx ----------- ----------- ----------- ----------- 10 20 10 20 20 10 10 20 NULL 30 30 30 40 NULL 40 40 50 50 50 50
如果该表值函数为特定的外部行返回多个行,则该外部行被多次返回。考虑在本文前面的递归查询和常见表表达式一节中使用的 Employees
表(“雇员组织结构图”方案)。在同一数据库中,您还创建了以下 Departments
表:
CREATE TABLE Departments ( deptid INT NOT NULL PRIMARY KEY, deptname VARCHAR(25) NOT NULL, deptmgrid INT NULL REFERENCES Employees ) SET NOCOUNT ON INSERT INTO Departments VALUES(1, 'HR', 2) INSERT INTO Departments VALUES(2, 'Marketing', 7) INSERT INTO Departments VALUES(3, 'Finance', 8) INSERT INTO Departments VALUES(4, 'R&D', 9) INSERT INTO Departments VALUES(5, 'Training', 4) INSERT INTO Departments VALUES(6, 'Gardening', NULL)
大多数部门都具有一个与 Employees 表中的某个雇员相对应的经理 ID,但是像 Gardening 部门一样,有些部门可能没有经理。请注意,Employees 表中的经理必然管理某个部门。以下表值函数接受雇员 ID 作为参数,并且返回该雇员及其所有级别的所有下属:
CREATE FUNCTION dbo.fn_getsubtree(@empid AS INT) RETURNS @TREE TABLE ( empid INT NOT NULL, empname VARCHAR(25) NOT NULL, mgrid INT NULL, lvl INT NOT NULL ) AS BEGIN WITH Employees_Subtree(empid, empname, mgrid, lvl) AS ( --






