我很难弄清楚如何进行优化查询以执行以下操作,即使听起来很简单。
假设我有一个名为promo的表(一列:ID),另一个名为promo_has_been_displayed_to_user的表(两列:promo_id和user_id,promo_id是引用promo.ID的外键)。我想要一个查询,它将返回promo中的所有行,其中promo_has_been_displayed_to_user中的任何行中都没有提到ID字段,其中promo_has_been_displayed_to_user.user_id字段设置为45.假设我在所有字段上都有索引。
(我的想法是我有一个促销广告数据库和一个用户数据库,每次我向用户展示广告时,我都会在promo_has_been_displayed_to_user中存储它已经向他们展示。现在我想找到一个尚未向用户45显示的新广告。)
理论上最佳的方法似乎如下:
1)获取promo_has_been_displayed_to_user的子集,其中user_id = 45,并在该子集中,在user_id字段上维护索引。 2)对于促销中的每一行,在索引的promo_id字段上获取ID并在步骤1中生成的子集中查找。 3)返回促销中您在步骤2中找不到匹配项的所有行。
但是,如何构建反映该查询的查询?
现在,我至少有两个会返回正确答案的查询(我已经验证了测试数据);问题是我不认为他们会以最佳状态运行,原因如下:
1)
select * from promo
where ID not in (select promo_id from promo_has_been_displayed_to_user
where user_id=45)
此查询的问题在于,一旦您获得&#34返回的ID列表;从promo_has_been_displayed_to_user选择promo_id,其中user_id = 45",我认为它只是列表(没有索引onit),并且"不在"通过一次只检查一个列表来实现检查。如果promo_has_been_displayed_to_user的子集,其中user_id = 45,那么对于促销中的每一行,我们必须搜索一个没有索引的巨大列表。
2)
select * from promo p
where not exists (select * from promo_has_been_displayed_to_user
where promo_id = p.ID and user_id=45)
这一次,我们正在对索引的promo_id字段进行查找。但是,对于促销中的每一行,我都要查询整个promo_has_been_displayed_to_user表。如果只有一小部分promo_has_been_displayed_to_user,其中user_id = 45,那就太浪费了。
是否只有一个查询可以结合两者的优点 - 我首先将promo_has_been_displayed_to_user减少到user_id = 45的子集,然后对于promo中的每一行,我在promo_id上进行索引查找以查看是否存在& #39;子集中的匹配行?
(这是MySQL 5.0.95,虽然这听起来像是不是数据库服务器特定的。)
答案 0 :(得分:2)
您无法使用内部联接执行此操作。你需要做的是反连接。通常使用这样的查询最容易完成:
SELECT * FROM A WHERE id NOT IN (SELECT id FROM B);
这是SQL中antojoin的基本语法。
另一种方法是将LEFT JOIN转换为反连接,而在某些数据库上则表现更好:
SELECT A.*
FROM A
LEFT JOIN B ON A.id = B.id
WHERE A.id IS NOT NULL AND B.id IS NULL;
这相当于一些数据库可以更好地优化它。