2010-10-27 65 views
0

假設我們在SQL數據庫中有一個表(EnsembleMembers),其中包含以下數據。它列出了各種樂團的音樂家以及他們的樂器。涉及類似異或情況的SQL查詢的問題

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。

回答

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表。從那裏,選擇有「單簧管」或「薩克斯管」的合奏排。然後第三步是刪除所有具有「單簧管」和「薩克斯管」的合奏行。

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來標記你的問題。

+0

沒有使用DISTINCT? – 2010-10-27 02:37:45

+1

在GROUP BY解決方案中,如果在一個合奏中可以有多個薩克斯或多個單簧管,則必須使用DISTINCT。另請參閱我剛剛添加的第二個解決方案,這可能取決於您使用的數據庫品牌。 – 2010-10-27 02:39:52

+0

您確定完整的外部連接示例是正確的嗎?我知道MySQL不支持它們,但我重複了你的例子,將其中一個改爲左外連接,另一個改爲右外連接,並將兩者聯合(這是模仿AFAIK功能的方式),但我仍然檢索同時擁有薩克斯管和單簧管演奏員的合奏編號。 – 2010-10-27 03:24:37

1

這是最愚蠢的解決方案(但我喜歡它在某種程度上):

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

將'MINUS'改爲'EXCEPT'並且它變成了SQL-92語法並且在SQL Server上工作...所以我也喜歡它:)出於興趣,'MINUS'似乎不在任何SQL標準,是專用於特定的SQL產品的? – onedaywhen 2010-10-27 07:49:26

+0

@onedaywhen:是的,我在甲骨文。沒有想過它不是ANSI,thanx! – 2010-10-27 09:09:59

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