【问题标题】:t-sql most efficient row to column? crosstab for xml path, pivott-sql 最有效的行到列? xml 路径的交叉表,枢轴
【发布时间】:2010-04-12 14:33:31
【问题描述】:

我正在寻找将行转换为列的最高效方法。我需要以固定宽度和分隔格式输出数据库的内容(不是下面的实际模式,但概念相似)。下面的 FOR XML PATH 查询给出了我想要的结果,但是在处理除少量数据之外的任何内容时,可能需要一段时间。

 select orderid
   ,REPLACE((  SELECT '  ' + CAST(ProductId as varchar)
       FROM _details d
       WHERE d.OrderId = o.OrderId
       ORDER BY d.OrderId,d.DetailId
       FOR XML PATH('')
   ),' ','') as Products
 from _orders o

我查看了数据透视表,但我发现的大多数示例都是汇总信息。我只想合并子行并将它们附加到父行上。

我还应该指出,我也不需要处理列名,因为子行的输出要么是固定宽度的字符串,要么是分隔的字符串。

例如,给定以下表格:

OrderId     CustomerId
----------- -----------
1           1
2           2
3           3

DetailId    OrderId     ProductId
----------- ----------- -----------
1           1           100
2           1           158
3           1           234
4           2           125
5           3           101
6           3           105
7           3           212
8           3           250

对于我需要输出的订单:

orderid     Products
----------- -----------------------
1             100  158  234
2             125
3             101  105  212  250

orderid     Products
----------- -----------------------
1           100|158|234
2           125
3           101|105|212|250

想法或建议?我正在使用 SQL Server 2k5。

示例设置:

   create table _orders (
  OrderId int identity(1,1) primary key nonclustered
  ,CustomerId int
 )

 create table _details (
  DetailId int identity(1,1) primary key nonclustered
  ,OrderId int 
  ,ProductId int
 )

 insert into _orders (CustomerId)
 select 1
 union select 2
 union select 3

 insert into _details (OrderId,ProductId)
 select 1,100
 union select 1,158
 union select 1,234
 union select 2,125
 union select 3,105
 union select 3,101
 union select 3,212
 union select 3,250

 CREATE CLUSTERED INDEX IX_CL__orders on _orders(OrderId)
 CREATE NONCLUSTERED INDEX IX_NCL__orders on _orders(OrderId)
 INCLUDE (CustomerId)

 CREATE CLUSTERED INDEX IX_CL_details on _details(OrderId)
 CREATE NONCLUSTERED INDEX IX_NCL_details on _details(OrderId)
 INCLUDE (DetailId,ProductId)

使用 FOR XML 路径:

 select orderid
   ,REPLACE((  SELECT '  ' + CAST(ProductId as varchar)
       FROM _details d
       WHERE d.OrderId = o.OrderId
       ORDER BY d.OrderId,d.DetailId
       FOR XML PATH('')
   ),' ','') as Products
 from _orders o

它输出我想要的东西,但是对于大量数据来说非常慢。其中一个子表的行数超过 200 万行,将处理时间推迟到 ~ 4 小时。

orderid     Products
----------- -----------------------
1             100  158  234
2             125
3             101  105  212  250

【问题讨论】:

    标签: sql-server tsql crosstab unpivot for-xml


    【解决方案1】:

    根据定义,PIVOT 将不得不以某种方式聚合,因为您可以拥有多行具有相同的枢轴键列。如果您没有多行,那很好 - 但您仍然需要选择聚合运算符(MIN、MAX、SUM)。

    FOR XML PATH 构造更适合多行值到单字符串列的“枢轴”操作。

    我不确定为什么你的表现不佳。你在表上有什么索引?你的执行计划是什么样的?

    【讨论】:

    • 在主键列上有一个聚集索引,在主键列上有一个非聚集索引,表中的每一列都有包含
    • @ajberry - 在详细信息表上,您是否有关于 OrderId、DetailId 的键,其中包括 ProductId?
    • 我在上面的示例设置下添加了通常在表格上的索引。我需要重新检查实际表上的索引,那里可能会有一些调整。
    • @ajberry - 执行计划是什么样的,您可能希望将 DetailId 移动到该非聚集索引中而不是包含在内(因为您有该顺序)。此外,您的聚集索引在详细信息上不是唯一的(我会在 detailId 或 orderid、detailid 上聚集),因此它将向聚集索引添加唯一数据。
    • 运行它的 sp 还运行了一些索引例程,结果证明是导致速度变慢的那些例程。我按照上面的建议更改了表格上的索引,并且确实看到了改进。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    • 1970-01-01
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多