SQL查询涉及类似XOR条件的问题

时间:2010-10-27 02:26:29

标签: sql mysql

假设我们在SQL数据库中有一个表(EnsembleMembers),其中包含以下数据。它列出了作为各种乐团一部分的音乐家及其乐器。

EnsembleID (FK)   MusicianID (FK)   Instrument
----------------------------------------------
'1'               '1'               'Clarinet'
'1'               '4'               'Clarinet'
'1'               '100'             'Saxophone'
'2'               '200'             'Saxophone'
'2'               '300'             'Saxophone'
'2'               '320'             'Flute'
'99'              '300'             'Clarinet'

我想选择整体有一个或多个萨克斯管或一个或多个单簧管演奏者的合奏ID,但不是两者。我尝试了以下SQL语句,但它返回的是1,2,2,99,而不是预期的2,99

SELECT e1.EnsembleID 
  FROM ensemblemembers e1 
 WHERE e1.Instrument = 'Saxophone' 
    OR e1.Instrument = 'Clarinet' 
  AND NOT EXISTS (SELECT * 
                    FROM ensemblemembers e2 
                   WHERE (    e1.Instrument = 'Saxophone' 
                          AND e2.Instrument = 'Clarinet' 
                          AND e1.EnsembleID = e2.EnsembleID) 
                      OR (    e1.Instrument = 'Clarinet' 
                          AND e2.Instrument = 'Saxophone' 
                          AND e1.EnsembleID = e2.EnsembleID));

我做错了什么?

PS - 出于性能原因,我不想使用DISTINCT。

4 个答案:

答案 0 :(得分:3)

SELECT EnsembleID
FROM EnsembleMembers
WHERE Instrument IN ('Saxophone', 'Clarinet')
GROUP BY EnsembleID
HAVING COUNT(DISTINCT Instrument) = 1

您也可以使用FULL OUTER JOIN,但MySQL和其他一些次要数据库不支持这种类型的连接。

SELECT COALESCE(e1.EnsembleID, e2.EnsembleID) AS EnsembleID
FROM EnsembleMembers e1 FULL OUTER JOIN EnsembleMembers e2
  ON e1.EnsembleID = e2.EnsembleID 
  AND e1.Instrument = 'Saxophone' 
  AND e2.Instrument = 'Clarinet'
WHERE e1.EnsembleID IS NULL OR e2.EnsembleID IS NULL

如果您需要在没有FULL OUTER JOIN的情况下工作,请尝试:

SELECT e1.EnsembleID, e1.Instrument
FROM EnsembleMembers e1 LEFT OUTER JOIN EnsembleMembers e2
  ON e1.EnsembleID = e2.EnsembleID 
  AND e2.Instrument = 'Clarinet'
WHERE e1.Instrument = 'Saxophone' AND e2.EnsembleID IS NULL
UNION
SELECT e1.EnsembleID, e1.Instrument, e2.EnsembleID, e2.Instrument
FROM EnsembleMembers e1 LEFT OUTER JOIN EnsembleMembers e2
  ON e1.EnsembleID = e2.EnsembleID 
  AND e2.Instrument = 'Saxophone'
WHERE e1.Instrument = 'Clarinet' AND e2.EnsembleID IS NULL;

将来,请使用您使用的RDBMS品牌标记您的问题。

答案 1 :(得分:1)

这是最愚蠢的解决方案(但我喜欢它):

   SELECT EnsembleID FROM EnsembleMembers
MINUS
( 
      SELECT EnsembleID
        FROM EnsembleMembers
       WHERE Instrument = 'Saxophone'
   INTERSECT
      SELECT EnsembleID
        FROM EnsembleMembers
       WHERE Instrument = 'Clarinet' );

答案 2 :(得分:1)

以下是使用XOR进行查询的方法:

select a.EnsembleID from 
(
    select max(EnsembleID) as EnsembleID,
    max(case when Instrument = 'Saxophone' then 1 else 0 end) as Saxophone,
    max(case when Instrument = 'Clarinet' then 1 else 0 end) as Clarinet
    from 
        EnsembleMembers
    group by EnsembleID
) a 
where 
    a.Saxophone ^ a.Clarinet = 1

答案 3 :(得分:1)

我假设您有一个名为ENSEMBLES的表:

select E.id from ensembles E where exists (
  select 1 from ensemblemembers M where E.id = M.ensembleid
  and M.instrument in ('clarinet', 'saxophone')
) and not (
  exists (
    select 1 from ensemblemembers M where E.id = M.ensembleid
    and M.instrument = 'clarinet'
  ) and exists (
    select 1 from ensemblemembers M where E.id = M.ensembleid
    and M.instrument = 'saxophone'
  )
)

您希望避免使用DISTINCT,因此一种方法是使用主要的ENSEMBLES表。从那里,选择具有'单簧管'或'萨克斯管'的合奏行。然后第三步是删除所有具有'单簧管'和'萨克斯管'的整体行。