当您不知道上限时如何使用聚合案例语句而不是 PIVOT?

How to Use Aggregate Case Statements Instead of PIVOT When You Don't Know the Upper Bound?

解决方案:我无法在不超出简单查询的情况下解决这个问题,因此我采用了硬编码 case 语句,以达到 table 的数字标识符的限制。

我正在寻求帮助来编写一个查询来表示跨多个字段的未知数量的记录,每个主键只有一个记录。

这是我的 table 设计:

[Column Name] | [Data Type]  | [Allow Nulls]
-------------------------------------------
*ItemRef        nvarchar(48)    Unchecked
*AttributeID    numeric(2, 0)   Unchecked
 AttributeName  nvarchar(128)   Unchecked
 AttributeValue nvarchar(3072)  Nullable
 AttributeUOM   nvarchar(10)    Nullable

*编辑:这是一些示例数据:

    Product123 | 1 | Brand | MyBrandName
    Product123 | 2 | Product Line | MyProductLine
    Product123 | 3 | Color | MyColor
    Product456 | 1 | Brand | MySecondBrandName
    Product456 | 2 | Style | MyStyle

这是我想要的查询结果:

[ItemRef] | [AttributeName_01] | [AttributeValue_01] | [AttributeName_02] | [AttributeValue_02] | etc...

起初我想使用 PIVOT 查询,但 运行 此处建议我尝试使用 Aggregate Case 语句来代替多个线程,乍一看效率更高。

但是,我不知道一条记录有多少个属性。所以我的问题是,如何编写以下内容以更好地表示不确定数量的属性?

SELECT ItemRef
  , MIN(CASE AttributeID WHEN '1' THEN AttributeName END) AS AttrName01
  , MIN(CASE AttributeID WHEN '1' THEN AttributeValue END) AS AttrValue01
  , MIN(CASE AttributeID WHEN '1' THEN AttributeUOM END) AS AttrUom01
  , MIN(CASE AttributeID WHEN '2' THEN AttributeName END) AS AttrName02
  , MIN(CASE AttributeID WHEN '2' THEN AttributeValue END) AS AttrValue02
  , MIN(CASE AttributeID WHEN '2' THEN AttributeUOM END) AS AttrUom02
  , MIN(CASE AttributeID WHEN '3' THEN AttributeName END) AS AttrName03
  , MIN(CASE AttributeID WHEN '3' THEN AttributeValue END) AS AttrValue03
  , MIN(CASE AttributeID WHEN '3' THEN AttributeUOM END) AS AttrUom03
  , MIN(CASE AttributeID WHEN '4' THEN AttributeName END) AS AttrName04
  , MIN(CASE AttributeID WHEN '4' THEN AttributeValue END) AS AttrValue04
  , MIN(CASE AttributeID WHEN '4' THEN AttributeUOM END) AS AttrUom04
. . .
  , MIN(CASE AttributeID WHEN '99' THEN AttributeName END) AS AttrName05
  , MIN(CASE AttributeID WHEN '99' THEN AttributeValue END) AS AttrValue05
  , MIN(CASE AttributeID WHEN '99' THEN AttributeUOM END) AS AttrUom05
FROM dbo.ProductAttributes
GROUP BY ItemRef

下面介绍了如何使用动态交叉表来完成此操作。如果您使用它,请确保您了解它在做什么。您可以在末尾取消注释 exec 之前的行以查看已生成的动态 sql 。如果需要,您可以扩展它以包括其他属性特性的新列。

if OBJECT_ID('tempdb..#Something') is not null
    drop table #Something

create table #Something
(
    ItemRef nvarchar(48)
    , AttributeID numeric(2, 0)
    , AttributeName nvarchar(25)
    , AttributeValue nvarchar(25)
)

insert #Something
select 'Product123', 1, 'Brand', 'MyBrandName' union all
select 'Product123', 2, 'Product Line', 'MyProductLine' union all
select 'Product123', 3, 'Color', 'MyColor' union all
select 'Product456', 1, 'Brand', 'MySecondBrandName' union all
select 'Product456', 2, 'Style', 'MyStyle'

declare @StaticPortion nvarchar(2000) = 
    'with OrderedResults as
    (
        select *, ROW_NUMBER() over(partition by ItemRef order by ItemRef) as RowNum
        from #Something
    )
    select ItemRef';

declare @DynamicPortion nvarchar(max) = '';
declare @FinalStaticPortion nvarchar(2000) = ' from OrderedResults Group by ItemRef order by ItemRef';

with E1(N) AS (select 1 from (values (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))dt(n)),
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS 
(
    SELECT  ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
)

select @DynamicPortion = @DynamicPortion + 
    ', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then AttributeName end) as AttributeName' + CAST(N as varchar(6)) + CHAR(10) 
     + ', MAX(Case when RowNum = ' + CAST(N as varchar(6)) + ' then AttributeValue end) as AttributeValue' + CAST(N as varchar(6)) + CHAR(10)
from cteTally t
where t.N <= 
(
    select top 1 Count(*)
    from #Something
    group by ItemRef
    order by COUNT(*) desc
)

declare @SqlToExecute nvarchar(max) = @StaticPortion + @DynamicPortion + @FinalStaticPortion;

--select @SqlToExecute
exec sp_executesql @SqlToExecute