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

说我有一个employee表和一个review表。每个员工可以有一个以上的评论。我想查找所有employee至少具有一个“好”评论但没有“坏”评论。

我还没有弄清楚如何使子选择在不知道员工ID的情况下起作用,也没有弄清楚联接的正确组合以实现此目的。

有没有存储过程,函数或不带数据服务器端的方法吗?我已经将其与这些对象一起使用,但是我敢肯定还有另一种方法。

评论

确实有可能,但是请在您的表格中提供相关结构! (即,与员工进行审核有关的列以及您如何确定审核的好坏(是否有相应的列?))

我希望对此有一个更好的问题标题;除非这是一个相当知名的词组,否则我认为在搜索中查找可能具有挑战性。也许“仅获取在另一个表上的聚合联接具有一个条件但没有另一个条件的记录”?

您要查找A和B之间的交叉联接与A和C之间的半反联接的交集。“ A”代表员工,“ B”代表良好评价,而“ C”代表不良评价。

我没有提供整个架构,因为这是确定“好”或“坏”所涉及的逻辑的简化版本。鉴于以下所有答案,我绝对可以改进当前模型! :)非常感谢!

@JaredFarrish如果您能想到的话,我很高兴。您的建议似乎很先进。就像这样,如果我知道如何用这样的方式表达问题,我可能已经知道答案了! ;)

#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(这是具有自己的不符合标准SQL的SQL的DBMS)或Rel(这是RDBMS而不是SQL产品)上运行。

–有一天
2012年9月9日13:58



@onedaywhen,当我更新我的答案使之成为真实的陈述时。但是我有一个观点(这可能会使某些人感到不安):我几乎不考虑MS Access RDBMS

–阿德里亚诺·卡内罗(Adriano Carneiro)
2012年3月9日14:12

@onedaywhen,我现在真的很好奇。哪种解决方案在Access中不起作用? Access不支持哪种SQL构造?

–阿德里亚诺·卡内罗(Adriano Carneiro)
2012年3月9日14:19

访问不支持CASE表达式。而是具有专有的SWITCH()函数。没有任何支持SQL标准的产品是关系型的(空,重复行,列顺序等)。当您指的是SQL时,请勿使用“关系”或其派生词。谢谢收听 :)

–有一天
2012年3月9日16:12

@onedaywhen感谢您的输入。这意味着第一个解决方案对Access有利,但第二个解决方案则不然。这个社区的好处是我们一直在倾听。 :)如果您认为解决方案可以解决OP的问题,请不要忘记投票!

–阿德里亚诺·卡内罗(Adriano Carneiro)
2012年3月9日17:14

#2 楼

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

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

注意, 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'
    )


评论


格里芬,我重新分配了该SQL的位置,因为它伤害了我的眼睛,我发现使用自己的审美方法将它分配出去有点挑战。 (请注意,我确实在最左边的关键字后面留下了阵容,所以我没有完全做我通常会自己做的所有事情……)如果这样会冒犯您,我会事先道歉,如果您选择还原它。但是,我确实认为这是一个富有成效的改进,FWIW。 :)

–杰瑞·法里什(Jared Farrish)
2012年3月9日在2:17



#3 楼

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

假设您在classification中有一​​个review列,其值是“好”和“差”。

那么您可以这样做:

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.


#4 楼

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