Tar*_*ryn 29 sql-server unpivot
将该UNPIVOT函数应用于未规范化的数据时,SQL Server 要求数据类型和长度相同。我明白为什么数据类型必须相同,但为什么 UNPIVOT 要求长度相同?
假设我有以下需要逆透视的示例数据:
CREATE TABLE People
(
PersonId int,
Firstname varchar(50),
Lastname varchar(25)
)
INSERT INTO People VALUES (1, 'Jim', 'Smith');
INSERT INTO People VALUES (2, 'Jane', 'Jones');
INSERT INTO People VALUES (3, 'Bob', 'Unicorn');
Run Code Online (Sandbox Code Playgroud)
如果我尝试 UNPIVOTFirstname和Lastname列类似于:
select PersonId, ColumnName, Value
from People
unpivot
(
Value
FOR ColumnName in (FirstName, LastName)
) unpiv;
Run Code Online (Sandbox Code Playgroud)
SQL Server 生成错误:
Msg 8167, Level 16, State 1, Line 6
“姓氏”列的类型与 UNPIVOT 列表中指定的其他列的类型冲突。
为了解决错误,我们必须使用子查询首先将Lastname列强制转换为与以下相同的长度Firstname:
select PersonId, ColumnName, Value
from
(
select personid,
firstname,
cast(lastname as varchar(50)) lastname
from People
) d
unpivot
(
Value FOR
ColumnName in (FirstName, LastName)
) unpiv;
Run Code Online (Sandbox Code Playgroud)
在 SQL Server 2005 中引入 UNPIVOT 之前,我将使用SELECTwithUNION ALL来取消旋转firstname/lastname列,并且查询将运行而无需将列转换为相同长度:
select personid, 'firstname' ColumnName, firstname value
from People
union all
select personid, 'LastName', LastName
from People;
Run Code Online (Sandbox Code Playgroud)
我们还可以在CROSS APPLY不使用相同长度的数据类型的情况下成功地对数据进行逆透视:
select PersonId, columnname, value
from People
cross apply
(
select 'firstname', firstname union all
select 'lastname', lastname
) c (columnname, value);
Run Code Online (Sandbox Code Playgroud)
我已经通读了 MSDN,但我没有找到任何解释强制数据类型长度相同的原因。
使用 UNPIVOT 时要求相同长度背后的逻辑是什么?
Pau*_*ite 26
使用 UNPIVOT 时要求相同长度背后的逻辑是什么?
这个问题可能只有致力于实施的人才能真正回答UNPIVOT。您可以通过联系他们寻求支持来获得此信息。以下是我对推理的理解,不一定100%准确:
T-SQL 包含任意数量的奇怪语义和其他反直觉行为的实例。其中一些最终将作为弃用周期的一部分消失,但其他可能永远不会“改进”或“修复”。除此之外,存在依赖于这些行为的应用程序,因此必须保留向后兼容性。
隐式转换和表达式类型推导的规则占了上述奇怪之处的很大一部分。我并不羡慕那些必须确保SET为新版本保留奇怪(通常是未记录的)行为(在会话值等的所有组合下)的测试人员。
也就是说,在引入新的语言功能(显然没有向后兼容性包袱)时,没有充分的理由不进行改进并避免过去的错误。新功能,如递归公用表表达式(如Andriy M在评论中提到的),并且UNPIVOT可以自由拥有相对合理的语义和明确定义的规则。
关于在类型中包含长度是否使显式键入太过分,会有一系列观点,但我个人对此表示欢迎。在我看来,类型varchar(25)和varchar(50)是不一样的,无非是decimal(8)和decimal(10)是。在我看来,特殊的套管串类型转换使事情不必要地复杂化并且没有增加真正的价值。
有人可能会争辩说,只有可能会丢失数据的隐式转换才需要明确说明,但也存在边缘情况。最终,将需要进行转换,因此我们不妨将其明确化。
如果允许从varchar(25)to的隐式转换varchar(50),它只是另一个(很可能是隐藏的)隐式转换,具有所有常见的奇怪边缘情况和SET设置敏感性。为什么不使实现尽可能简单和明确?(没有什么是完美的,但是,这是一种耻辱,隐藏varchar(25)和varchar(50)内sql_variant是允许的。)
重写UNPIVOTwithAPPLY并UNION ALL避免(更好的)类型行为,因为 的规则UNION受向后兼容性的约束,并且在联机丛书中记录为允许不同的类型,只要它们使用隐式转换进行比较(数据类型优先级的神秘规则)使用等)。
解决方法包括明确数据类型并在必要时添加显式转换。这对我来说似乎是进步:)
一种编写显式类型解决方法的方法:
SELECT
U.PersonId,
U.ColumnName,
U.Value
FROM dbo.People AS P
CROSS APPLY
(
VALUES (CONVERT(varchar(50), Lastname))
) AS CA (Lastname)
UNPIVOT
(
Value FOR
ColumnName IN (P.Firstname, CA.Lastname)
) AS U;
Run Code Online (Sandbox Code Playgroud)
递归 CTE 示例:
-- Fails
WITH R AS
(
SELECT Dummy = 'A row'
UNION ALL
SELECT 'Another row'
FROM R
WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;
-- Succeeds
WITH R AS
(
SELECT Dummy = CONVERT(varchar(11), 'A row')
UNION ALL
SELECT CONVERT(varchar(11), 'Another row')
FROM R
WHERE Dummy = 'A row'
)
SELECT Dummy
FROM R;
Run Code Online (Sandbox Code Playgroud)
最后,请注意CROSS APPLY问题中使用的重写与 并不完全相同UNPIVOT,因为它不拒绝NULL属性。
| 归档时间: |
|
| 查看次数: |
17887 次 |
| 最近记录: |