在MySQL中实现SQL INTERSECT时的嵌套级别太高
我最后的作品之一是基于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'
您可以使用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)
谢谢。我的MySQL不太好找到这个解决方案。=) – 2012-07-20 14:25:33
很高兴它为你工作,如果你添加索引,它会非常快。我建议也为groupId添加一个索引。 – Drewch 2012-07-20 17:43:33
我会尽力回答在我的网站充满产品时两种解决方案的速度。认为这有助于很多人做出正确的选择 – 2012-07-20 21:19:16
如果我没有理解这个问题吧,看来你正在努力寻找的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*
而且应该这样做。
你的答案也有效,现在看起来更好,如果在创建关系时防止重复行。我对吗? – 2012-07-20 14:47:53
我不知道什么性能更好,我的解决方案或内部联接。这本身就是一个有趣的问题。在我看来,SQL越简洁,就越容易理解。有时可读性比性能更有价值。 – hackattack 2012-07-20 16:45:09
你怎么使用Drewch的解决方案,但接受hackattack的? :P – 2012-07-20 17:42:42