【问题标题】:How to Use Aggregate Case Statements Instead of PIVOT When You Don't Know the Upper Bound?当您不知道上限时,如何使用聚合案例语句而不是 PIVOT?
【发布时间】:2017-02-09 16:11:53
【问题描述】:

解决方案:如果不进行简单查询,我就无法解决这个问题,因此我采用了硬编码 case 语句,直到表的数字标识符的限制。

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

这是我的餐桌设计:

[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

【问题讨论】:

  • 您所指的是动态交叉表。这是有关该主题的文章。 sqlservercentral.com/articles/Crosstab/65048如果您在用一些示例数据找出回发时遇到问题,我可以为您提供帮助。
  • 好的,我已经阅读了您链接的文章,但它确实让我有点不知所措。这绝对需要创建存储过程吗?至于样本数据,我猜我会更新主帖。
  • 它当然不需要存储过程,但你为什么不想创建一个呢?在临时查询中执行此操作将是一场语法噩梦。在 t-sql 中已经够难了,但是尝试在代码中构建动态 sql 简直是疯了,而且极易出现 sql 注入漏洞。
  • 好吧,坦率地说,我对你提出的问题一点也不熟悉。我是一名应用程序开发人员,我需要一个可用于构建数据集的简单视图。我不知道什么是临时查询,而且我对 t-sql 的经验也很少。但是我不想做疯狂的工作,所以我愿意学习。
  • OK没问题。视图中不能有动态 sql。如果您可以发布一些示例数据,当然不是真实信息,我可以提供帮助。我不需要很多数据,但可能需要 3-5 行左右?即席查询是指将一堆 sql 语句插入应用程序的字符串中。这是一种不好的查询方法,所以希望这不是您构建应用程序的方式。 :D

标签: sql sql-server pivot case aggregates


【解决方案1】:

您可以通过以下方式使用动态交叉表来完成此操作。如果您使用它,请确保您了解它在做什么。您可以在最后取消注释 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-24
    • 2021-07-21
    • 2011-09-14
    • 1970-01-01
    • 2021-02-16
    • 2015-08-04
    • 2013-08-22
    相关资源
    最近更新 更多