2011-03-29 110 views
3

如果存在一組項目,我想查詢關係數據庫。查詢關係數據庫中的集合

我建模數據有以下形式:

key1 = [ item1, item3, item5 ] 
key2 = [ item2, item7 ] 
key3 = [ item2, item3, item4, item5 ] 
... 

我將它們存儲在一個表下面的模式

CREATE TABLE sets (key INTEGER, item INTEGER); 

因此,舉例來說,下面的INSERT語句會插入以上三套。

INSERT INTO sets VALUES (key1, item1); 
INSERT INTO sets VALUES (key1, item3); 
INSERT INTO sets VALUES (key1, item5); 
INSERT INTO sets VALUES (key2, item2); 
INSERT INTO sets VALUES (key2, item7); 
INSERT INTO sets VALUES (key3, item2); 
INSERT INTO sets VALUES (key3, item3); 
INSERT INTO sets VALUES (key3, item4); 
INSERT INTO sets VALUES (key3, item5); 

給定一組項目,我想用一組相關聯的密鑰,如果它被存儲在表和NULL,如果事實並非如此。是否有可能用sql查詢做到這一點?如果是這樣,請提供詳細信息。

詳細信息,可能是相關的:

  • 我在數據庫設計/查詢策略主要有興趣,但我最終會在MySQL實現這一點,並使用mysql-Python包在Python從瓶坯查詢。
  • 如果不同的佈局對於這種類型的查詢更方便,我可以自由重構數據庫模式。
  • 每個集合,如果它存在應該是唯一的。
  • 我對部分比賽不感興趣。
  • 數據庫規模大約爲< 1000套,其中每套包含< 10個項目,因此此時的性能不是優先級。

在此先感謝。

+0

Iam -1:從現在開始,所有「預先感謝」。所以禁止我! – stefan 2011-03-29 23:08:42

+0

問一個真正的問題。這是值得您的SQL的教授應該教給你(雖然他可能會教你全錯,所以使用互聯網資源,而不是) – stefan 2011-03-29 23:09:28

+1

@stefan,何必那麼認真? – epaps 2011-03-30 00:05:42

回答

2

我不會評論是否有更適合的模式來完成此操作(這很可能),但對於具有列nameitem的模式,以下查詢應該可以工作。 (MySQL的句法)

SELECT k.name 
FROM (SELECT DISTINCT name FROM sets) AS k 
INNER JOIN sets i1 ON (k.name = i1.name AND i1.item = 1) 
INNER JOIN sets i2 ON (k.name = i2.name AND i2.item = 3) 
INNER JOIN sets i3 ON (k.name = i3.name AND i3.item = 5) 
LEFT JOIN sets ix ON (k.name = ix.name AND ix.item NOT IN (1, 3, 5)) 
WHERE ix.name IS NULL; 

的想法是,我們在k所有設置鍵,我們則在sets與設定項數據加入一次組中的每個組項目,我們在尋找,三這個案例。表格別名i1i2i3的三個內部聯接中的每個都會過濾掉所有不包含使用該聯接搜索的項目的名稱。最後,我們還有一個sets的左連接,其表別名爲ix,它帶來了集合中的所有額外項目,也就是我們沒有搜索的每個項目。 ix.nameNULL在沒有找到額外項目的情況下,這正是我們想要的,因此WHERE條款。如果找到該集合,則查詢返回包含set key的行,否則不返回行。


編輯: collapsars答案背後的想法似乎是比我好很多,所以這裏有與解釋有點較短的版本。

SELECT sets.name 
FROM sets 
LEFT JOIN (
    SELECT DISTINCT name 
    FROM sets 
    WHERE item NOT IN (1, 3, 5) 
) s1 
ON (sets.name = s1.name) 
WHERE s1.name IS NULL 
GROUP BY sets.name 
HAVING COUNT(sets.item) = 3; 

的這裏的想法是,子查詢s1選擇包含我們正在尋找的那些其他項目組的所有的鑰匙。因此,當我們離開加入setss1時,s1.nameNULL當集合只包含我們正在搜索的項目。然後,我們按設置鍵進行分組,並過濾​​出任何具有錯誤項目數的組。然後,我們只留下只包含我們正在搜索並且長度正確的項目的集合。由於集合只能包含一個項目,因此只能有一個滿足該條件的集合,這就是我們正在尋找的集合。


編輯:這只是我恍然大悟如何做到這一點,而不排除。

SELECT totals.name 
FROM (
    SELECT name, COUNT(*) count 
    FROM sets 
    GROUP BY name 
) totals 
INNER JOIN (
    SELECT name, COUNT(*) count 
    FROM sets 
    WHERE item IN (1, 3, 5) 
    GROUP BY name 
) matches 
ON (totals.name = matches.name) 
WHERE totals.count = 3 AND matches.count = 3; 

第一個子查詢查找每個集合中項目的總數,第二個查找每個集合中匹配項目的數量。當matches.count是3時,該集合包含我們正在尋找的所有項目,並且如果totals.count也是3,則該集合沒有任何額外項目。

+0

我不認爲有必要排除: – momeara 2011-03-31 13:10:33

+0

@momeara如果我們不排除具有我們正在尋找的項目以外的項目的集合,則查詢將返回所有具有相同項目數量的集合甚至一個匹配項目。也就是說,搜索集合(1,3,5)也可以返回(1,4,7),因爲它具有相同的長度,1是搜索到的項目之一,並且我們不排除包含非搜索項目。 – 2011-03-31 21:27:34

1

aleksis解決方案需要對每個可能的項目集進行特定的查詢。以下建議提供了一種通用的解決方案,即將要查詢的項目集可以作爲另一個查詢的結果集進行分解 - 只需使用適當的子查詢替換集合包含操作符即可。

 SELECT CASE COUNT(ddd.key) WHEN 0 THEN NULL ELSE MIN(ddd.key) END 
     FROM (
       SELECT s4.key 
         , COUNT(*) icount 
        FROM sets s4 
        JOIN (
          SELECT DISTINCT d.key 
          FROM (
            SELECT s1.key 
            FROM sets s1 
            WHERE s1.item IN ('item1', 'item3', 'item5') 
            MINUS 
            SELECT s2.key 
            FROM sets s2 
            WHERE s2.item NOT IN ('item1', 'item3', 'item5') 
           ) d  
         ) dd ON (dd.key = s4.key) 
       GROUP BY s4.key 
      ) ddd 
     WHERE ddd.icount = (
          SELECT COUNT(*) 
           FROM (
             SELECT DISTINCT s3.item 
             FROM sets s3 
             WHERE s3.item IN ('item1', 'item3', 'item5') 
            ) 
         ) 
      ;     

結果集DD提供一組候選誰不與其他項目比那些從一組被測試asscociate鍵。唯一不明確的地方可能來自引用測試項目集的適當子集的鍵。因此我們計算與dd關鍵字相關的項目數量,並選擇該數字與測試項目集合的基數相匹配的關鍵字。如果這樣的密鑰存在,它是唯一的(因爲我們知道該項目集是唯一的)。 最外層select中的case表達式只是一種保證它們不會爲空結果集的奇特方式,即如果項集不由關係表示,則返回null值。

也許這個解決方案將是對你有用,

問候

卡斯滕

+0

這很聰明。謝謝! – momeara 2011-03-30 13:12:19

0

爲了簡化黑洞的解決方案,這是已經被阿列克西Torhamo簡化:

這是沒有必要爲了得到所有不匹配的密鑰,可能很大,只要找到匹配的密鑰並將它們稱爲部分匹配即可。

-- get all partial matches 
CREATE TEMPORARY VIEW partial_matches AS 
SELECT DISTINCT key FROM sets WHERE item IN (1,3,5); 

-- filter for full matches 
SELECT sets.key 
FROM sets, partial_matches 
WHERE sets.key = partial_matches.key 
GROUP BY sets.key HAVING COUNT(sets.key) = 3; 
+0

這不起作用。我想你的意思是'COUNT(sets.item)'。如果你改變它,並在數據庫中設置(1,3,5)和(1,4,7),搜索(1,3,5)將返回兩者,因爲兩者都包含部分匹配(單獨1個就足夠了對於另一組被認爲是部分匹配)並且都具有正確數量的項目。 – 2011-03-31 21:18:49

+0

是的,我認爲你是對的! – momeara 2011-04-01 18:27:41

1

此查詢有一個衆所周知的名稱。谷歌「關係部門」,「一套遏制加入」,「一套平等參加」。

+0

我從來沒有聽說過這個名字。 – Marcin 2012-02-04 09:03:11