如何改进此SQL查询?

如何改进此SQL查询?,sql,tsql,aggregate,sql-server-2008-r2,Sql,Tsql,Aggregate,Sql Server 2008 R2,今天我遇到了一个有趣的SQL问题,虽然我提出了一个有效的解决方案,但我怀疑这是最好或最有效的答案。我听从这里的专家-帮助我学习一些东西并改进我的查询!RDBMS是SQL Server 2008 R2,查询是SSRS报告的一部分,该报告将针对大约100000行运行 本质上,我有一个ID列表,可以有多个与之关联的值,这些值是Yes、No或其他字符串。对于ID x,如果任何值为是,则x应为是;如果它们都为否,则应为否;如果它们包含除是和否以外的任何其他值,则显示该值。我只希望每个ID返回1行,不重复

今天我遇到了一个有趣的SQL问题,虽然我提出了一个有效的解决方案,但我怀疑这是最好或最有效的答案。我听从这里的专家-帮助我学习一些东西并改进我的查询!RDBMS是SQL Server 2008 R2,查询是SSRS报告的一部分,该报告将针对大约100000行运行

本质上,我有一个ID列表,可以有多个与之关联的值,这些值是Yes、No或其他字符串。对于ID x,如果任何值为是,则x应为是;如果它们都为否,则应为否;如果它们包含除是和否以外的任何其他值,则显示该值。我只希望每个ID返回1行,不重复

简化版本和测试用例:

DECLARE @tempTable table ( ID int, Val varchar(1) )

INSERT INTO @tempTable ( ID, Val ) VALUES ( 10, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 13, 'N')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'N')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 15, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 16, 'Y')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 17, 'F')
INSERT INTO @tempTable ( ID, Val ) VALUES ( 18, 'P')


SELECT DISTINCT t.ID, COALESCE(t2.Val, t3.Val, t4.Val)
FROM @tempTable t
LEFT JOIN
(
    SELECT ID, Val
    FROM @tempTable
    WHERE Val = 'Y'
) t2 ON t.ID = t2.ID
LEFT JOIN
(
    SELECT 
    ID, Val FROM @tempTable
    WHERE Val = 'N'
) t3 ON t.ID = t3.ID
LEFT JOIN
(
    SELECT ID, Val
    FROM @tempTable
    WHERE Val <> 'Y' AND Val <> 'N'
) t4 ON t.ID = t4.ID
DECLARE@tentable(ID int,Val varchar(1))
插入到@tentable(ID,Val)值中(10,“Y”)
插入到@tentable(ID,Val)值中(11,'N')
插入到@tentable(ID,Val)值中(11,'N')
插入@fentable(ID,Val)值(12,“Y”)
插入@fentable(ID,Val)值(12,“Y”)
插入@fentable(ID,Val)值(12,“Y”)
插入到@tentable(ID,Val)值中(13,'N')
插入@fentable(ID,Val)值(14,“Y”)
插入到@fentable(ID,Val)值中(14,'N')
插入@fentable(ID,Val)值(15,“Y”)
插入@fentable(ID,Val)值(16,“Y”)
插入@fentable(ID,Val)值(17,'F')
插入@fentable(ID,Val)值(18,'P')
选择不同的t.ID,合并(t2.Val、t3.Val、t4.Val)
来自@attreable t
左连接
(
选择ID,Val
来自“诱惑”
其中Val='Y'
)t.ID上的t2=t2.ID
左连接
(
挑选
ID,Val来自@tentable
其中Val='N'
)t.ID上的t3=t3.ID
左连接
(
选择ID,Val
来自“诱惑”
其中Val'Y'和Val'N'
)t.ID上的t4=t4.ID

提前谢谢。

我想把它改成这样,以便更容易阅读:

SELECT DISTINCT t.ID, COALESCE(t2.Val, t3.Val, t4.Val)
FROM @tempTable t
LEFT JOIN @tempTable t2 ON t.ID = t2.ID and t2.Val = 'Y'
LEFT JOIN @tempTable t3 ON t.ID = t3.ID and  t3.Val = 'N'
LEFT JOIN @tempTable t4 ON t.ID = t4.ID and t4.Val <> 'Y' AND t4.Val <> 'N'
选择不同的t.ID,合并(t2.Val,t3.Val,t4.Val)
来自@attreable t
t.ID=t2.ID和t2.Val='Y'上的左连接@tentable t2
在t.ID=t3.ID和t3.Val='N'上左连接@tentable t3
t.ID=t4.ID和t4.Val'Y'和t4.Val'N'上的左连接@TENTABLE t4
给出与示例相同的结果


我还查看了两者的执行计划,它们看起来完全相同,我怀疑您是否会看到任何性能差异。

让我们回答一个更简单的问题:对于每个id,获取字母表中最后一个Val。如果Y和N是唯一的值,这将起作用。而且查询要简单得多:

SELECT t.ID, MAX(t.Val) FROM t GROUP BY t.ID;
所以,把你的情况简化为简单的情况。使用枚举(如果您的DB支持),或将值代码拆分为另一个具有排序规则列的表(在这种情况下,可以使用1表示Y,2表示N,999表示所有其他可能的值,并且您希望最小值)。然后

这里代码有两列,Val和Collation

您还可以通过CTE类型查询执行此操作,只要您按需要对值进行排序。这种方法有一个到小查找表的连接,应该比3个自连接快得多

WITH q AS (SELECT t.id, t.Val, ROW_NUMBER() AS r FROM t JOIN codes ON t.Val=codes.Val 
    PARTITION BY t.id ORDER BY codes.collation)
SELECT q.id, q.Val WHERE r=1;            

我是这样读你的说明书的:

  • 如果任何ID为Y,则为Y
  • 如果所有ID均为N,则为N
  • else显示值(Y或N除外)
  • 消除每个(1)的行

    选择distinct以消除每个(2)的多个N

    将各种“其他”值分组,以获得每个ID的一行

    SELECT A.Id, AllVals = 
        SubString(
            (SELECT ', ' + B.Val 
             FROM C as B 
             WHERE A.Id = B.Id 
             FOR XML PATH ( '' ) ), 3, 1000) 
    FROM C as A 
    GROUP BY Id
    
    整个可运行查询:

    declare @tempTable table (ID int, Val char(1))
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 10, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 13, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 15, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 16, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 17, 'F') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 18, 'P')
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 18, 'F')
    delete from @tempTable
    where not Val='Y' and ID in (
        select distinct ID
        from @tempTable
        where Val='Y'
    );
    WITH C as (select distinct * from @tempTable)
    SELECT A.Id, AllVals = 
        SubString(
            (SELECT ', ' + B.Val 
             FROM C as B 
             WHERE A.Id = B.Id 
             FOR XML PATH ( '' ) ), 3, 1000) 
    FROM C as A 
    GROUP BY Id
    
    输出:

    Id  AllVals
    10  Y
    11  N
    12  Y
    13  N
    14  Y
    15  Y
    16  Y
    17  F
    18  F, P
    
    试试这个:

    ;WITH a AS ( 
    SELECT
      ID,
      SUM(CASE Val WHEN 'Y' THEN 1 ELSE 0 END) AS y,
      SUM(CASE Val WHEN 'N' THEN 0 ELSE 1 END) AS n,
      MIN(CASE WHEN Val IN ('Y','N') THEN NULL ELSE Val END) AS first_other
    FROM @tempTable
    GROUP BY ID
    ) 
    SELECT
      ID,
      CASE WHEN y > 0 THEN 'Y' WHEN n = 0 THEN 'N' ELSE first_other END AS Val
    FROM a 
    
    • 如果存在任何“Y”值,则Y之和将大于0
    • 如果所有值均为“N”,则N的和为零
    • 如果需要,获取第一个非“Y”或“N”字符
    • 在这种情况下,只需一次通过即可确定结果 桌子

    如果有这样的行,会发生什么:
    10=>Y;10=>N;10=>F;10=>S;10=>P
    。在这种情况下应该显示什么?如果有任何“是”值,答案应该是“是”,因此10=Y。如果同一个id可以显示除Y或N以外的两个不同值,该怎么办?这不符合你的“无重复”吗?@Gabriel这正是我想知道的。最近我不得不解决类似的问题,我向一个需要报告的用户提问。他们花了很长时间才回复,最后他们取消了任务-)因此需要仔细考虑在OP的代码中,如果ID有“N”和“P”,那么将返回“N”。这似乎违反了“为了成为N而所有N”规则。尝试将这一行添加到示例中以验证:插入@tentable(ID,Val)值(18,'N')ahh,我喜欢;在…之前。。。我总是犯这样的错误:)
    declare @tempTable table (ID int, Val char(1))
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 10, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 11, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 12, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 13, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 14, 'N') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 15, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 16, 'Y') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 17, 'F') 
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 18, 'P')
    INSERT INTO @tempTable ( ID, Val ) VALUES ( 18, 'F')
    delete from @tempTable
    where not Val='Y' and ID in (
        select distinct ID
        from @tempTable
        where Val='Y'
    );
    WITH C as (select distinct * from @tempTable)
    SELECT A.Id, AllVals = 
        SubString(
            (SELECT ', ' + B.Val 
             FROM C as B 
             WHERE A.Id = B.Id 
             FOR XML PATH ( '' ) ), 3, 1000) 
    FROM C as A 
    GROUP BY Id
    
    Id  AllVals
    10  Y
    11  N
    12  Y
    13  N
    14  Y
    15  Y
    16  Y
    17  F
    18  F, P
    
    ;WITH a AS ( 
    SELECT
      ID,
      SUM(CASE Val WHEN 'Y' THEN 1 ELSE 0 END) AS y,
      SUM(CASE Val WHEN 'N' THEN 0 ELSE 1 END) AS n,
      MIN(CASE WHEN Val IN ('Y','N') THEN NULL ELSE Val END) AS first_other
    FROM @tempTable
    GROUP BY ID
    ) 
    SELECT
      ID,
      CASE WHEN y > 0 THEN 'Y' WHEN n = 0 THEN 'N' ELSE first_other END AS Val
    FROM a