2017-03-07 63 views
0

我有一个很难搞清楚如何做一个优化的查询做下面列出,尽管这听起来很简单。查询排除行的ID在另一个表

假设我有一个名为promo的表(一列:ID),另一个表叫做promo_has_been_displayed_to_user(两列:promo_id和user_id,而promo_id是引用promo.ID的外键)。我想要一个查询,它将返回Promo中所有行,其中的ID字段在Promo_has_been_displayed_to_user中的任何行中都未提及,其中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)对于促销中的每一行,取出ID并在索引的promo_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) 

该查询的麻烦是,一旦你必须返回ID列表“选择从promo_has_been_displayed_to_user其中USER_ID = 45" PROMO_ID,我认为这只是一个列表(没有索引它身上),并认为‘没有’检查是通过只检查该列表一次一个实现。如果其中user_id = 45的promo_has_been_displayed_to_user的子集结果很大,那么对于促销中的每一行,我们都必须搜索一个没有索引的巨大列表。

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_id上进行索引查找以查看是否子集中有匹配的行吗?

(这是MySQL的5.0.95,虽然这听起来像的东西是不是数据库服务器特定的。)

+0

尝试左连接。可能会在MySQL上执行得更快。 – jarlh

+0

这是一个简单的排除加入 – Strawberry

回答

1

你不能用一个内部联接做到这一点。你需要做的是一个反加入。通常,这是用这样的查询最容易实现:

SELECT * FROM A WHERE id NOT IN (SELECT id FROM B); 

这是SQL的antojoin的基本语法。

的另一种方法然而这样做是把左连接到反连接和在某些数据库这个性能更好:

SELECT A.* 
    FROM A 
    LEFT JOIN B ON A.id = B.id 
WHERE A.id IS NOT NULL AND B.id IS NULL; 

这原来是等效和一些数据库可以更好地优化它。

+0

EXCEPT关键字也是一个选项。像这样:'SELECT ID FROM EXCEPT SELECT ID FROM B'然而,你必须在两个查询中选择相同的列类型。 – iPhantomGuy

+0

@iPhantomGuy,MySQL支持EXCEPT吗?此外,OP选择A中的所有列,而B可能不完全兼容。 – jarlh

+0

刚发现MySQL不支持EXCEPT。 – iPhantomGuy

相关问题