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