【问题标题】:Counting and grouping challenge in a pivot table with T-SQL使用 T-SQL 在数据透视表中计数和分组挑战
【发布时间】:2016-01-05 17:33:19
【问题描述】:

我有一个将垂直数据库设计转换为水平数据库设计的数据透视表:

源表:

Id  ParentId    Property    Value
---------------------------------
1   1           Date        01-09-2015
2   1           CountValue  2
3   1           TypeA       Value1
4   1           TypeB       Value2
5   1           TypeC       Value2
6   2           Date        15-10-2015
7   2           CountValue  3
8   2           TypeA       Value3
9   2           TypeB       Value22
10  2           TypeC       Value99

旋转后看起来像:

ParentId    Date        CountValue  TypeA   TypeB   TypeC
----------------------------------------------------------
1           01-09-2015  2           Value1  Value2  Value2
2           15-10-2015  3           Value3  Value22 Value99

然后,在TypeATypeBTypeC 列中有一个有效值的查找表:

Id  Name    Value
-----------------
1   TypeA   Value1
2   TypeA   Value2
3   TypeA   Value3
4   TypeB   Value20
5   TypeB   Value21
6   TypeB   Value22
7   TypeC   Value1
8   TypeC   Value2

因此,鉴于上述结构,我正在寻找一种方法来查询数据透视表,以便我可以计算 TypeATypeBTypeC 中的所有无效值,其中 @987654330 @ 是有效日期,CountValue 不为空且大于 0。

我怎样才能获得如下预期和输出的结果:

Count   Column
--------------
0       TypeA
1       TypeB
1       TypeC

我通过创建三个查询并使用 UNION 粘合结果来完成结果,但我认为也应该可以使用数据透视表,但我不确定如何。使用数据透视表可以实现想要的结果吗?

注意:使用的数据库是 SQL Server 2005 数据库。

【问题讨论】:

    标签: sql tsql sql-server-2005


    【解决方案1】:

    我不会使用 PIVOT,否则您必须先旋转数据,然后再旋转它以获得所需的输出。逐步分解它,您可以使用以下方法获取有效的父 ID:

    SELECT  t.ParentID
    FROM    #T AS t
    GROUP BY t.ParentID
    HAVING  ISDATE(MAX(CASE WHEN t.Property = 'Date' THEN t.Value END)) = 1 
    AND     MAX(CASE WHEN t.Property = 'CountValue' THEN CONVERT(INT, t.Value) END) > 0;
    

    这两个有子句将其限制为您的标准,即具有有效日期和大于 0 的 CountValue

    下一步是查找您的无效属性:

    SELECT  t.*
    FROM    #T AS t
    WHERE   NOT EXISTS
            (   SELECT  1
                FROM    #V AS v
                WHERE   v.Name = t.Property
                AND     v.Value = t.Value
            );
    

    这将包括 Date 和 CountValue,也不包括 TypeA,因为所有属性都是有效的,所以需要做更多的工作,我们必须找到我们感兴趣的不同属性:

    SELECT  DISTINCT Name 
    FROM    #V
    

    现在我们可以将其与无效属性结合以获取计数,并与有效的父 ID 结合以获得所需的结果:

    WITH ValidParents AS
    (   SELECT  t.ParentID
        FROM    #T AS t
        GROUP BY t.ParentID
        HAVING  ISDATE(MAX(CASE WHEN t.Property = 'Date' THEN t.Value END)) = 1 
        AND     MAX(CASE WHEN t.Property = 'CountValue' THEN CONVERT(INT, t.Value) END) > 0
    ), InvalidProperties AS
    (   SELECT  t.Property
        FROM    #T AS t
        WHERE   t.ParentID IN (SELECT vp.ParentID FROM ValidParents AS vp)
        AND     NOT EXISTS
                (   SELECT  1
                    FROM    #V AS v
                    WHERE   v.Name = t.Property
                    AND     v.Value = t.Value
                )
    )
    SELECT  [Count] =  COUNT(t.Property), 
            [Column] = v.Name
    FROM    (SELECT DISTINCT Name FROM #V) AS V
            LEFT JOIN InvalidProperties AS t
                ON t.Property = v.Name
    GROUP BY v.Name;        
    

    这给出了:

    Count   Column
    --------------
    0       TypeA
    1       TypeB
    1       TypeC           
    

    以上查询的架构

    适用于 SQL Server 2008+。抱歉,我没有 SQL Server 2005 了,忘了它不支持表值构造函数。

    CREATE TABLE #T (Id INT, ParentId INT, Property VARCHAR(10), Value VARCHAR(10));
    INSERT #T (Id, ParentId, Property, Value)
    VALUES
        (1, 1, 'Date', '01-09-2015'), (2, 1, 'CountValue', '2'), (3, 1, 'TypeA', 'Value1'),
        (4, 1, 'TypeB', 'Value2'), (5, 1, 'TypeC', 'Value2'), (6, 2, 'Date', '15-10-2015'),
        (7, 2, 'CountValue', '3'), (8, 2, 'TypeA', 'Value3'), (9, 2, 'TypeB', 'Value22'),
        (10, 2, 'TypeC', 'Value99');
    
    CREATE TABLE #V (ID INT, Name VARCHAR(5), Value VARCHAR(7));    
    INSERT #V (Id, Name, Value)
    VALUES
        (1, 'TypeA', 'Value1'), (2, 'TypeA', 'Value2'), (3, 'TypeA', 'Value3'), 
        (4, 'TypeB', 'Value20'), (5, 'TypeB', 'Value21'), (6, 'TypeB', 'Value22'), 
        (7, 'TypeC', 'Value1'), (8, 'TypeC', 'Value2');
    

    【讨论】:

    • 这需要我一些时间来弄清楚它是如何工作的。感谢您的全面回答。我已将示例名称转换为我的实际情况(垂直表包含超过 1.300 个独特的“属性”项)。我确实相信 PIVOT 表最终不是解决它的正确方法,但我从那里开始以使表旋转。谢谢,+1 并被接受,因为它还考虑了 Date 和 CountValue 标准。
    【解决方案2】:

    没有PIVOT的最终结果:

    SELECT [count] = SUM(CASE WHEN l.id IS NULL THEN 1 ELSE 0 END)
      ,t.Property
    FROM #lookup l
    RIGHT JOIN #tab t  
      ON t.Property = l.Name
      AND t.Value = l.Value
    WHERE t.Property LIKE 'Type%'
    GROUP BY t.Property;
    

    LiveDemo

    数据:

    CREATE TABLE #tab(
       Id       INTEGER  NOT NULL PRIMARY KEY 
      ,ParentId INTEGER  NOT NULL
      ,Property VARCHAR(10) NOT NULL
      ,Value    VARCHAR(10) NOT NULL
    );
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (1,1,'Date','01-09-2015');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (2,1,'CountValue','2');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (3,1,'TypeA','Value1');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (4,1,'TypeB','Value2');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (5,1,'TypeC','Value2');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (6,2,'Date','15-10-2015');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (7,2,'CountValue','3');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (8,2,'TypeA','Value3');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (9,2,'TypeB','Value22');
    INSERT INTO #tab(Id,ParentId,Property,Value) VALUES (10,2,'TypeC','Value99');
    
    CREATE TABLE #lookup(
       Id    INTEGER  NOT NULL PRIMARY KEY 
      ,Name  VARCHAR(5) NOT NULL
      ,Value VARCHAR(7) NOT NULL
    );
    INSERT INTO #lookup(Id,Name,Value) VALUES (1,'TypeA','Value1');
    INSERT INTO #lookup(Id,Name,Value) VALUES (2,'TypeA','Value2');
    INSERT INTO #lookup(Id,Name,Value) VALUES (3,'TypeA','Value3');
    INSERT INTO #lookup(Id,Name,Value) VALUES (4,'TypeB','Value20');
    INSERT INTO #lookup(Id,Name,Value) VALUES (5,'TypeB','Value21');
    INSERT INTO #lookup(Id,Name,Value) VALUES (6,'TypeB','Value22');
    INSERT INTO #lookup(Id,Name,Value) VALUES (7,'TypeC','Value1');
    INSERT INTO #lookup(Id,Name,Value) VALUES (8,'TypeC','Value2');
    

    编辑:

    添加更多条件:

    LiveDemo2

    SELECT [count] = SUM(CASE WHEN l.id IS NULL THEN 1 ELSE 0 END)
          ,t.Property
    FROM #lookup l
    RIGHT JOIN #tab t  
      ON t.Property = l.Name
      AND t.Value = l.Value
    WHERE t.Property LIKE 'Type%'
      AND t.ParentId IN (SELECT ParentId FROM #tab WHERE Property = 'Date' AND ISDATE(VALUE) = 1)
      AND t.ParentID IN (SELECT ParentId FROM #tab WHERE Property = 'CountValue' AND Value > 0)
    GROUP BY t.Property;
    

    【讨论】:

    • 我喜欢解决这个问题的简单方法。只有两件事没有考虑到;具有有效日期并确保 CountValue 大于 0。+1 为您的答案。
    • @Ben 你能提供破坏我代码的数据吗?我会努力改进它。关于DATE,您应该在插入数据库之前对其进行验证。不幸的是,对于 SQL Server 2005,没有像 TRY_PARSE 这样的花哨方法。
    • 我不确定它是否会损坏;但是为了获得正确的结果,这两个标准是强制性的。 TRY_PARSE 支持会很好,因为我经常遇到在查询执行中引发错误的无效数据。例如;日期存储为文本(由于垂直设计),但并不总是有效的日期......我知道;不是一个完美的设计,但它是存在的设计,我必须与之合作。
    • 这也是一个很好的答案。不幸的是,我不能接受多个答案或给出更多 +1。感谢您分享您的知识 :)
    • @Ben 这没问题。很高兴我能提供帮助。
    猜你喜欢
    • 2015-02-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-12-19
    • 2014-08-07
    • 2011-11-06
    • 2012-12-28
    • 1970-01-01
    相关资源
    最近更新 更多