【问题标题】:Use one select query instead of query + subquery使用一个选择查询而不是查询+子查询
【发布时间】:2019-10-29 04:47:44
【问题描述】:

SQL Server 2014,已更改数据以保护数据。我希望以下内容有意义。

我必须在一个表中搜索所有具有中等风险的类别。然后我需要进入另一个表(测试)并检索只有托比测试失败的那些中等类别。类别在第一个表中是唯一的。

所以在下面的示例数据中,类别 4 和 5 都是中等风险,并且在测试表中都有 Toby 的结果为 Fail。

但是我想从我的最终输出中排除类别 4,因为该类别的 Bill 测试也失败了。

我的目标只是将类别 5 显示为输出。我可以使用查询和子查询来做到这一点,其中子查询返回类别 4 和 5,主查询过滤器。但是我能以某种方式通过单个查询实现相同的目标吗?

更新:

我当前的查询如下。为了这篇文章,我不得不稍微整理一下,我希望它已经足够了。基本上,子查询会提取所有对中等类别进行任何失败测试的类别,而主查询会过滤掉任何其他失败的类别。

select tt.category, tt.[name]
from test_table mt
where tt.category in (select mt.category
                      from main_table mt
                      inner join test_table tt on tt.category = mt.category
                      where mt.risk= 'Moderate' and tt.result = 'Fail')
  and tt.[name] <> 'Toby'
  and tt.[result] = 'Fail'

输出:

category    risk
----------------------
1           Minimal
2           Critical
3           Elevated
4           Moderate
5           Moderate


category    name    result
-------------------------------
1           Mark    Pass
1           Bill    No Result
1           John    Pass
1           Toby    Pass

2           Mark    Pass
2           Bill    No Result
2           John    Fail
2           Toby    Pass

3           Mark    Pass
3           Bill    No Result
3           John    Pass
3           Toby    Pass

4           Mark    Pass
4           Bill    Fail
4           John    Pass
4           Toby    Fail

5           Mark    Pass
5           Bill    Pass
5           John    Pass
5           Toby    Fail

【问题讨论】:

  • 你好@THedge 请写下你的查询。那会有很大帮助。谢谢!
  • “我可以通过查询和子查询来做到这一点” 向您展示如何在没有子查询的情况下做到这一点,使用您已经拥有的查询会容易得多。如果您不向我们展示您已有的内容,我们无法向您展示如何删除所述子查询,
  • @THedge 您的示例数据没有您帖子中所述的类别 5 下的 Toby 失败。可能想要编辑您的示例数据。
  • @Ryan,对不起,我已经更正了数据。对于其他人,我会尽快将我现有的查询放在这里。
  • 当其他人想帮助你时,也许这会对他们有所帮助:dbfiddle.uk/…

标签: sql sql-server


【解决方案1】:

加入表 group by category 并在 HAVING 子句中设置条件:

select c.category
from categories c inner join test t
on c.category = t.category
where c.risk = 'Moderate'
group by c.category
having 
  sum(case when t.name = 'Toby' and t.result = 'Fail' then 1 else 0 end) > 0
  and 
  sum(case when t.name <> 'Toby' and t.result = 'Fail' then 1 else 0 end) = 0

或:

select c.category
from categories c inner join test t
on c.category = t.category
where c.risk = 'Moderate' and t.result = 'Fail'
group by c.category
having count(distinct t.name) = 1 and max(t.name) = 'Toby'

请参阅demo
结果:

> | category |
> | -------: |
> |        5 |

【讨论】:

  • 没问题,我的已经删了:)
  • 非常感谢您!我将使用第一个,因为它也记录了业务规则。如果/将来必须对其进行修改,这将有助于其他开发人员。
【解决方案2】:

对您的问题的直接阅读建议exists/not exists

select c.*
from categories c
where c.risk = 'Moderate' and
      exists (select 1
              from tests t
              where t.category = c.category and
                    t.name = 'Toby' and
                    t.result = 'Fail'
             ) and
      not exists (select 1
                  from tests t
                  where t.category = c.category and
                        t.name <> 'Toby' and
                        t.result = 'Fail'
                 );

条件聚合也是一种可行的解决方案——当过滤器仅在一个表上时,我更喜欢这种解决方案。但是,这里的过滤是在两个表上进行的,这将使用test(category, result, name) 上的索引并避免外部聚合。

【讨论】:

  • 所以你建议查询+2子查询而不是查询+子查询?
  • @forpas 。 . .是的,我愿意。答案不是很清楚吗?每个子查询都经过优化以使用索引,所以这应该很快。
  • 阅读题名:使用单选查询代替查询+子查询
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多