开发者

Problem with SQL query involving XOR-like condition

Let's say we have a table (EnsembleMembers) in an SQL database with the following data. 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. I have tried the following SQL statement, but it is returning 1,2,2,99, rather than the expected 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.


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.

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:

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.


I assume you have a table called 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. From there, pick ensemble rows that have 'clarinet' OR 'saxophone'. Then the third step is to remove all ensemble rows that have 'clarinet' AND 'saxophone'.


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:

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
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜