2012-07-20 50 views
3

我最後的作品之一是基於Yii的硬件目錄。每個項目都可以鏈接到很多組。在MySQL中實現SQL INTERSECT時的嵌套級別太高

CREATE TABLE item_group (
id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
itemId INT(10) UNSIGNED NOT NULL, 
groupId INT(10) UNSIGNED NOT NULL, 
PRIMARY KEY (id) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

只有該itemIds必須顯示,其中所有的groupIds都由用戶選擇。這裏有我的越野車解決方案:

$groups = isset($_GET['groups']) ? array_merge(array_diff($_GET['groups'], array('0'=>'-')),array()) : array(); 
$sql = ''; 
$brackets = ''; 
$groupMaxKey = count($groups) - 1; 
//some code here 
for($i=0;$i<=$groupMaxKey;$i++){ 
    $sql .= "SELECT itemId FROM item_group WHERE groupId='".$groups[$i]."' "; 
    if($i != $groupMaxKey){ 
     $sql .= "AND itemId IN ("; 
     $brackets .= ")"; 
    } else { 
     $sql .= $brackets; 
    } 
} 

我發現最大的嵌套級別是32.更多的組給出了一個錯誤。什麼是最乾淨的解決方案?

在此澄清查詢的例子:

SELECT itemId FROM item_group 
WHERE groupId='31' AND itemId IN (
    SELECT itemId FROM item_group 
    WHERE groupId='24' AND itemId IN (
     SELECT itemId FROM item_group 
     WHERE groupId='35' 
    ) 
) 

//// 答案確實有效:

SELECT g1.itemId 
FROM (((((((((((((((((((((((((((((((((((((((item_group g1 
INNER JOIN item_group g2 ON g1.itemId = g2.itemId) 
INNER JOIN item_group g3 ON g1.itemId = g3.itemId) 
INNER JOIN item_group g4 ON g1.itemId = g4.itemId) 
INNER JOIN item_group g5 ON g1.itemId = g5.itemId) 
INNER JOIN item_group g6 ON g1.itemId = g6.itemId) 
INNER JOIN item_group g7 ON g1.itemId = g7.itemId) 
INNER JOIN item_group g8 ON g1.itemId = g8.itemId) 
INNER JOIN item_group g9 ON g1.itemId = g9.itemId) 
INNER JOIN item_group g10 ON g1.itemId = g10.itemId) 
INNER JOIN item_group g11 ON g1.itemId = g11.itemId) 
INNER JOIN item_group g12 ON g1.itemId = g12.itemId) 
INNER JOIN item_group g13 ON g1.itemId = g13.itemId) 
INNER JOIN item_group g14 ON g1.itemId = g14.itemId) 
INNER JOIN item_group g15 ON g1.itemId = g15.itemId) 
INNER JOIN item_group g16 ON g1.itemId = g16.itemId) 
INNER JOIN item_group g17 ON g1.itemId = g17.itemId) 
INNER JOIN item_group g18 ON g1.itemId = g18.itemId) 
INNER JOIN item_group g19 ON g1.itemId = g19.itemId) 
INNER JOIN item_group g20 ON g1.itemId = g20.itemId) 
INNER JOIN item_group g21 ON g1.itemId = g21.itemId) 
INNER JOIN item_group g22 ON g1.itemId = g22.itemId) 
INNER JOIN item_group g23 ON g1.itemId = g23.itemId) 
INNER JOIN item_group g24 ON g1.itemId = g24.itemId) 
INNER JOIN item_group g25 ON g1.itemId = g25.itemId) 
INNER JOIN item_group g26 ON g1.itemId = g26.itemId) 
INNER JOIN item_group g27 ON g1.itemId = g27.itemId) 
INNER JOIN item_group g28 ON g1.itemId = g28.itemId) 
INNER JOIN item_group g29 ON g1.itemId = g29.itemId) 
INNER JOIN item_group g30 ON g1.itemId = g30.itemId) 
INNER JOIN item_group g31 ON g1.itemId = g31.itemId) 
INNER JOIN item_group g32 ON g1.itemId = g32.itemId) 
INNER JOIN item_group g33 ON g1.itemId = g33.itemId) 
INNER JOIN item_group g34 ON g1.itemId = g34.itemId) 
INNER JOIN item_group g35 ON g1.itemId = g35.itemId) 
INNER JOIN item_group g36 ON g1.itemId = g36.itemId) 
INNER JOIN item_group g37 ON g1.itemId = g37.itemId) 
INNER JOIN item_group g38 ON g1.itemId = g38.itemId) 
INNER JOIN item_group g39 ON g1.itemId = g39.itemId) 
INNER JOIN item_group g40 ON g1.itemId = g40.itemId) 
WHERE g1.groupId='1' AND g2.groupId='2' AND g3.groupId='3' AND g4.groupId='4' AND g5.groupId='5' AND g6.groupId='6' AND g7.groupId='7' AND g8.groupId='8' AND g9.groupId='9' AND g10.groupId='10' AND g11.groupId='11' AND g12.groupId='12' AND g13.groupId='13' AND g14.groupId='14' AND g15.groupId='15' AND g16.groupId='16' AND g17.groupId='17' AND g18.groupId='18' AND g19.groupId='19' AND g20.groupId='20' AND g21.groupId='21' AND g22.groupId='22' AND g23.groupId='23' AND g24.groupId='24' AND g25.groupId='25' AND g26.groupId='26' AND g27.groupId='27' AND g28.groupId='28' AND g29.groupId='29' AND g30.groupId='30' AND g31.groupId='31' AND g32.groupId='32' AND g33.groupId='33' AND g34.groupId='34' AND g35.groupId='35' AND g36.groupId='36' AND g37.groupId='37' AND g38.groupId='38' AND g39.groupId='39' AND g40.groupId='40' 
+0

你怎麼使用Drewch的解決方案,但接受hackattack的? :P – 2012-07-20 17:42:42

回答

4

您可以使用INNER JOIN實現此目的。沒有理由嵌套這些陳述。

您例如情況下適當的任何查詢的例子是:

SELECT g1.itemId 
FROM ((item_group g1 
     INNER JOIN item_group g2 ON g1.itemId = g2.itemId) 
     INNER JOIN item_group g3 ON g1.itemId = g3.itemId) 
WHERE g1.groupId='31' AND g2.groupId='24' AND g3.groupId='35' 

我已經與三列(id, itemId, groupId)測試這一個簡單的表和它的作品。將這種語句放在一個循環中是非常容易的,並且連接數沒有最大值。

要使其運行速度更快,您應該在item_group表中索引itemId列。

你可以用下面的SQL語句執行此操作:

ALTER TABLE item_group ADD INDEX (itemId) 
+0

謝謝。我的MySQL不太好找到這個解決方案。=) – 2012-07-20 14:25:33

+0

很高興它爲你工作,如果你添加索引,它會非常快。我建議也爲groupId添加一個索引。 – Drewch 2012-07-20 17:43:33

+0

我會盡力回答在我的網站充滿產品時兩種解決方案的速度。認爲這有助於很多人做出正確的選擇 – 2012-07-20 21:19:16

1

如果我沒有理解這個問題吧,看來你正在努力尋找的itemIds在不同組中的交集?

我們所能做的就是計數凡在我們正在尋找的羣體itemIds ...

SELECT itemId, COUNT(groupId) as CNT 
FROM item_group 
WHERE groupId IN (*GROUP_IDS*) 
GROUP BY itemId 

現在,只要一個項目不能在同一組的兩倍,我們可剛拉出來的行此查詢的衛生組織CNT等於號,我們正在尋找羣體 ...

SELECT * FROM (
    SELECT itemId, COUNT(groupId) as CNT 
    FROM item_group 
    WHERE groupId IN (*GROUP_IDS*) 
    GROUP BY itemId 
) as TMP WHERE CNT = *NUMBER_OF_GROUP_IDS* 

而且應該這樣做。

+0

你的答案也有效,現在看起來更好,如果在創建關係時防止重複行。我對嗎? – 2012-07-20 14:47:53

+0

我不知道什麼性能更好,我的解決方案或內部聯接。這本身就是一個有趣的問題。在我看來,SQL越簡潔,就越容易理解。有時可讀性比性能更有價值。 – hackattack 2012-07-20 16:45:09