加速select另一个表中存在列条件的位置,没有重复

时间:2016-07-31 07:14:52

标签: mysql sql database

如果我有以下两个表:

  1. 表“a”有2列:id(int)[主要索引],column1 [已索引]
  2. 表3“b”包含3列:id_table_a(int),condition1(int),condition2(int)[所有列作为主索引]
  3. 我可以运行以下查询来从表a中选择行,其中表b condition1为1

    SELECT a.id FROM a WHERE EXISTS (SELECT 1 FROM b WHERE b.id_table_a=a.id && condition1=1 LIMIT 1) ORDER BY a.column1 LIMIT 50
    

    在两个表中有几亿行,这个查询非常慢。如果我这样做:

    SELECT a.id FROM a INNER JOIN b ON a.id=b.id_table_a && b.condition1=1  ORDER BY a.column1 LIMIT 50
    

    这几乎是即时的,但如果表b中有多个匹配的行匹配id_table_a,则返回重复项。如果我执行SELECT DISTINCT或GROUP BY a.id来删除重复项,则查询变得非常慢。

    这是一个显示示例查询的SQLFiddle:http://sqlfiddle.com/#!9/35eb9e/10

    在这种情况下,有没有办法快速建立没有重复的连接?

    *编辑显示INNER而不是LEFT加入没有太大的区别

    *编辑显示加入的移动条件没有太大的区别

    *编辑添加LIMIT

    *已编辑添加ORDER BY

5 个答案:

答案 0 :(得分:1)

您可以尝试使用内部联接和不同的

SELECT distinct a.id 
FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1

但在select *上使用distinct,请确保在这种情况下不要使用返回错误结果的不同ID

SELECT distinct col1, col2, col3 .... 
FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1

您还可以使用condtition1添加复合索引,例如:key(id,condition1)

如果可以,你也可以执行

 ANALYZE TABLE table_name; 

在桌子上..

另一种技术是尝试恢复潜在客户表

SELECT distinct a.id 
FROM b INNER JOIN a ON a.id=b.id_table_a AND b.condition1=1

使用最具选择性的表来引导查询

使用这个似乎与使用索引http://sqlfiddle.com/#!9/35eb9e/15(最后添加使用where)

不同
# USING DISTINCT TO REMOVE DUPLICATES without col  and order 
 EXPLAIN 
 SELECT DISTINCT a.id 
 FROM a 
 INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1
;

答案 1 :(得分:1)

看起来我找到了答案。

SELECT a.id FROM a 
INNER JOIN b ON 
    b.id_table_a=a.id && 
    b.condition1=1 && 
    b.condition2=(select b.condition2 from b WHERE b.id_table_a=a.id && b.condition1=1 LIMIT 1)
ORDER BY a.column1
LIMIT 5;

我不知道这是否存在缺陷,如果有,请告诉我。如果有人有办法以某种方式压缩这个,我很乐意接受你的回答。

答案 2 :(得分:0)

SELECT id FROM a INNER JOIN b ON a.id=b.id_table_a AND b.condition1=1

将条件带入连接的ON子句,这样可以使用表b的索引进行过滤。同时使用INNER JOIN而不是LEFT JOIN

然后你应该将较少的结果分组。

答案 3 :(得分:0)

在处理重复数据删除和限制的查询中包装快速版本:

SELECT DISTINCT * FROM (
  SELECT a.id
  FROM a
  JOIN b ON a.id = b.id_table_a && b.condition1 = 1
) x
ORDER BY column1
LIMIT 50

我们知道内部查询很快。重复删除和排序必须在某处进行。这种情况发生在可能的最小行集上。

请参阅SQLFiddle

选项2:

尝试以下方法:

按如下方式创建索引:

create index a_id_column1 on a(id, column1)
create index b_id_table_a_condition1 on b(a_table_a, condition1)

这些是覆盖索引 - 包含查询所需的所有列的索引,这反过来意味着对数据的仅索引访问可以实现结果。

然后试试这个:

SELECT * FROM (
  SELECT a.id, MIN(a.column1) column1
  FROM a
  JOIN b ON a.id = b.id_table_a
    AND b.condition1 = 1
  GROUP BY a.id) x
ORDER BY column1
LIMIT 50

答案 4 :(得分:0)

在子选择中使用快速查询并删除外部选择中的重复项:

SELECT DISTINCT sub.id
FROM (
    SELECT a.id 
    FROM a 
    INNER JOIN b ON a.id=b.id_table_a && b.condition1=1
    WHERE b.id_table_a > :offset
    ORDER BY a.column1
    LIMIT 50
) sub

由于删除重复项,您可能会少于50行。只需重复查询,直到获得足够的行。从:offset = 0开始。在以下查询中,将最后一个结果中的最后一个ID用作:offset

如果您了解统计信息,则还可以使用两个限制。内部查询中的限制应足够高,以返回50个不同的行,其概率足够高。

SELECT DISTINCT sub.id
FROM (
    SELECT a.id 
    FROM a 
    INNER JOIN b ON a.id=b.id_table_a && b.condition1=1
    ORDER BY a.column1
    LIMIT 1000
) sub
LIMIT 50

例如:如果每个ID平均有10个重复项,则内部查询中的LIMIT 1000将返回平均100个不同的行。你不太可能得到少于50行。

如果condition2列是布尔值,您知道最多可以有两个重复项。在这种情况下,内部查询中的LIMIT 100就足够了。