【问题标题】:At least one X but no Ys Query至少有一个 X 但没有 Ys 查询
【发布时间】:2012-03-26 11:38:49
【问题描述】:

我偶尔会遇到这种模式,但我还没有找到非常令人满意的解决方法。

假设我有一个employee 表和一个review 表。每个员工可以有多个评论。我想找到所有至少有一个“好”评论但没有“坏”评论的employees。

我还没有弄清楚如何在事先不知道员工 ID 的情况下使子选择工作,而且我还没有想出正确的联接组合来实现这一点。

有没有办法做到这一点没有存储过程、函数或带来数据服务器端?我已经让它与那些一起工作,但我确信还有另一种方法。

【问题讨论】:

  • 当然可以,但请从您的表格中给出相关结构! (即与employeereview 相关的列以及您如何确定评论的好坏(是否有一些列?))
  • 我希望有一个更好的问题标题;除非这是一个相当知名的短语,否则我认为在搜索中找到它可能具有挑战性。也许“只获取另一个表上的聚合连接具有一个条件但没有另一个条件的记录”?
  • 你寻找 A & B 之间的交叉连接和 A & C 之间的半反连接的交集。“A”代表员工,“B”代表好评,“C”代表差评。
  • 我没有提供整个模式,因为这是确定“好”或“坏”所涉及的逻辑的简化版本。鉴于以下所有答案,我绝对认为我可以改进我目前的模型! :) 非常感谢!
  • @JaredFarrish 如果你能想到一个,我会全力以赴。你的建议似乎很先进。比如,如果我知道如何表达这样的问题,我可能已经知道答案了! ;)

标签: mysql sql join subquery


【解决方案1】:

由于您尚未发布您的数据库结构,我做了一些假设和简化(关于rating 列,它可能是数字而不是字符字段)。相应调整。

解决方案 1:使用联接

select distinct e.EmployeeId, e.Name
from employee e
left join reviews r1 on e.EmployeeId = r1.EmployeeId and r1.rating = 'good'
left join reviews r2 on e.EmployeeId = r2.EmployeeId and r1.rating = 'bad'
where r1.ReviewId is not null --meaning there's at least one
and r2.ReviewId is null --meaning there's no bad review

解决方案 2:分组依据和条件计数过滤

select e.EmployeeId, max(e.Name) Name
from employee e
left join reviews r on e.EmployeeId = r.EmployeeId
group by e.EmployeeId
having count(case r.rating when 'good' then 1 else null end) > 0
and  count(case r.rating when 'bad' then 1 else null end) = 0

这两种解决方案都与 SQL ANSI 兼容,这意味着它们都可以与任何完全支持 SQL ANSI 标准的 RDBMS 风格一起使用(大多数 RDBMS 都是如此)。

正如@onedaywhen 所指出的,该代码在 MS Access 中不起作用(尚未测试,我相信他在该主题上的专业知识)。

但我对此有一个说法(这可能会让一些人感到不安):我几乎不认为 MS Access 是一个 RDBMS。我过去曾使用过它。一旦你继续前进(甲骨文、SQL Server、Firebird、PostGreSQL、MySQL 等等),你就再也不想回来了。认真的。

【讨论】:

  • “SQL ANSI 兼容,这意味着两者都适用于任何 RDBMS 风格”——这不是一个真实的陈述。例如,您的代码不适用于 Access(它是一个 DBMS,它有自己的不符合标准 SQL 的 SQL)和 Rel(它是一个 RDBMS,但不是一个 SQL 产品)。
  • @onedaywhen 我更新了我的答案以做出正确的陈述。但我有一点(这可能会让一些人不高兴):我几乎不认为 MS Access 是 RDBMS
  • @onedaywhen 我现在真的很好奇。哪些解决方案在 Access 中不起作用? Access 不支持哪种 SQL 构造?
  • Access 不支持 CASE 表达式;而是具有专有的SWITCH() 功能。没有支持 SQL 标准的产品是关系型的(空值、重复行、列排序等)。当您指的是 SQL 时,请不要使用关系一词或其派生词。感谢收听:)
  • @onedaywhen 感谢您的意见。这意味着第一个解决方案适用于 Access,但不适用于第二个解决方案。这个社区的好处是我们一直在倾听。 :) 如果您认为解决方案对 OP 的问题有好处,请不要忘记投票!
【解决方案2】:

问题——基于 B 中不存在匹配项返回 A 方的行——(没有“差”评论的员工)描述了“反半联接”。有很多方法可以完成这种查询,我在 MS Sql 2005 及更高版本中发现了至少 5 种。

我知道此解决方案适用于 MSSQL 2000 及更高版本,并且是我在 MS Sql 2005 和 2008 中尝试过的 5 种方法中最有效的。我不确定它是否有效在 MySQL 中,但它应该,因为它反映了一个相当常见的集合操作。

注意,IN 子句允许子查询访问外部范围内的employee 表。

SELECT EE.*
FROM   employee EE
WHERE
    EE.EmpKey IN (
      SELECT RR.EmpKey
      FROM   review RR
      WHERE  RR.EmpKey = EE.EmpKey
        AND  RR.ScoreCategory = 'good'
    ) 
  AND
    EE.EmpKey NOT IN (
      SELECT  RR.EmpKey 
      FROM    review RR
      WHERE   RR.EmpKey = EE.EmpKey
        AND   RR.ScoreCategory = 'bad'
    )

【讨论】:

  • Griffin,我重新间隔了那个 SQL,因为它让我的眼睛受伤,我发现使用我自己的审美方法来间隔它有点挑战。 (注意,我确实按照最左边的关键词离开了阵容,所以我没有完全做我自己通常会做的所有事情......)如果冒犯了你,我提前道歉,并获胜不介意您是否选择还原它。但是,我确实认为这是一项富有成效的改进,FWIW。 :)
【解决方案3】:
SELECT ???? FROM employee,review
WHERE employees.id = review.id
GROUP BY employees.id
HAVING SUM(IF(review='good',1,0)) > 1 AND SUM(IF(review='bad',1,0)) = 0

【讨论】:

    【解决方案4】:

    这是可能的。特定语法取决于您存储“好”和“差”评论的方式。

    假设您在 review 中有一个 classification 列,其值为“好”和“坏”。

    那么你可以这样做:

    SELECT employee.*
    FROM employee
    JOIN review 
     ON employee.id=review.employee_id
    GROUP BY employee.id
    HAVING SUM(IF(classification='good',1,0))>0 -- count up #good reviews, > 0
       AND SUM(IF(classification='bad',1,0))=0  -- count up #bad  reviews, = 0.
    

    【讨论】:

      猜你喜欢
      • 2022-01-13
      • 2019-05-07
      • 2014-06-12
      • 2015-02-19
      • 2011-12-02
      • 2015-05-27
      • 1970-01-01
      • 2011-11-23
      相关资源
      最近更新 更多