简体   繁体   English

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

[英]Problem with SQL query involving XOR-like condition

Let's say we have a table (EnsembleMembers) in an SQL database with the following data. 假设我们在SQL数据库中有一个包含以下数据的表(EnsembleMembers)。 It lists the musicians which are part of various ensembles, along with their instruments. 它列出了属于各种乐团的音乐家及其乐器。

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'

I want to select the ensemble IDs where the ensemble has one or more saxophone or one or more clarinet players, but not both. 我想选择一个具有一个或多个萨克斯风或一个或多个单簧管播放器,但不能同时具有两者的集成ID。 I have tried the following SQL statement, but it is returning 1,2,2,99 , rather than the expected 2,99 . 我已经尝试了以下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));

What am I doing wrong? 我究竟做错了什么?

PS - I don't want to use DISTINCT for performance reasons. PS-由于性能原因,我不想使用DISTINCT。

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

You can also use a FULL OUTER JOIN for this, but this type of join is not supported by MySQL and a few other minor databases. 您也可以为此使用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

If you need this to work without FULL OUTER JOIN, try this: 如果您需要在没有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;

In the future, please tag your question with the brand of RDBMS you use. 将来,请用您使用的RDBMS品牌标记您的问题。

Here is the most stupid solution (but I like it somehow): 这是最愚蠢的解决方案(但我以某种方式喜欢它):

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

Here's how you could do the query using XOR: 使用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

I assume you have a table called ENSEMBLES: 我假设您有一个名为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'
  )
)

You want to avoid using DISTINCT, so one way to do it is by using the main ENSEMBLES table. 您要避免使用DISTINCT,所以一种方法是使用主ENSEMBLES表。 From there, pick ensemble rows that have 'clarinet' OR 'saxophone'. 从那里,选择具有“ clarinet”或“ saxophone”的合奏行。 Then the third step is to remove all ensemble rows that have 'clarinet' AND 'saxophone'. 然后,第三步是删除所有具有“ clarinet”和“ saxophone”的合奏行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM