【问题标题】:Select from table where in other table most efficient way从表中选择其他表中最有效的方式
【发布时间】:2015-03-21 19:58:36
【问题描述】:

我的 mySQL 数据库中有一个 ID 号列表作为一张表;我有第二个表,其中包含 From_IDTo_IDFrequency 列。

我想创建第三个表,它与第二个表具有相同的结构,但只包含第一个表中的“来自”和“到”ID 的那些行。

第一个表有 ≈ 80k 行,第二个有 ≈ 4500 万行。这个过程花费的时间太长,似乎无法在合理的时间内(不少于一天)结束。

我目前的查询如下:

CREATE table3 AS (SELECT * FROM table2 
                  WHERE from_id IN (SELECT id FROM table1) 
                  AND to_id IN (SELECT id FROM table1);

如果有人能告诉我一个更有效的方法来解决这个问题,我将不胜感激!

【问题讨论】:

    标签: mysql sequelpro


    【解决方案1】:

    首先,使用exists 而不是in

    SELECT t2.*
    FROM table2 t2
    WHERE EXISTS (SELECT 1 FROM table1 t1 WHERE t1.id = t2.from_id) AND
          EXISTS (SELECT 1 FROM table1 t1 WHERE t1.id = t2.to_id);
    

    然后确保您在table1(id) 上有一个索引。后者非常重要。

    请注意:您可以在用户界面中通过将limit 100limit 1000 等输入到查询中来测试查询。这将让您看到随着数据增长的性能如何。

    【讨论】:

    • 这很好 - 谢谢!从 > 24 小时到 175 秒。
    • 99.8% 改进 ;) 实际上,该指数得到了认可。
    【解决方案2】:

    我想创建第三个表,它的结构与第二个表相同,但只包含第一个表中的“来自”和“到”ID 的行。

    这被称为"denormalization",虽然这样做有正当理由,但它不被认为是好的数据库设计,应该避免。

    您想这样做可能是因为您的查询太慢了。那么让我们看看您的查询。

    SELECT *
    FROM  table2 
    WHERE from_id IN (SELECT id FROM table1) 
      AND to_id   IN (SELECT id FROM table1)
    

    如果 MySQL 必须对 table1 进行全表扫描,这可能会很慢,但它似乎足够聪明,可以识别出它可以使用索引。

    mysql> explain SELECT * FROM table2                    WHERE from_id IN (SELECT id FROM table1)                    AND to_id IN (SELECT id FROM table1);
    +----+-------------+--------+--------+---------------+---------+---------+---------------------+------+-------------+
    | id | select_type | table  | type   | possible_keys | key     | key_len | ref                 | rows | Extra       |
    +----+-------------+--------+--------+---------------+---------+---------+---------------------+------+-------------+
    |  1 | SIMPLE      | table2 | ALL    | NULL          | NULL    | NULL    | NULL                |    4 | Using where |
    |  1 | SIMPLE      | table1 | eq_ref | PRIMARY       | PRIMARY | 4       | test.table2.from_id |    1 | Using index |
    |  1 | SIMPLE      | table1 | eq_ref | PRIMARY       | PRIMARY | 4       | test.table2.to_id   |    1 | Using index |
    +----+-------------+--------+--------+---------------+---------+---------+---------------------+------+-------------+
    3 rows in set (0.00 sec)
    

    我认为通过在子查询中明确询问确切的 ID 可以更好地表达。

    SELECT t2.*
    FROM   table2 t2
    WHERE  (SELECT 1 FROM table1 t1 WHERE t1.id = t2.from_id)
      AND  (SELECT 1 FROM table1 t1 WHERE t1.id = t2.to_id)
    
    mysql> explain SELECT t2.*     FROM   table2 t2     WHERE  (SELECT 1 FROM table1 t1 WHERE t1.id = t2.from_id)       AND  (SELECT 1 FROM table1 t1 WHERE t1.id = t2.to_id);
    +----+--------------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    | id | select_type        | table | type   | possible_keys | key     | key_len | ref             | rows | Extra       |
    +----+--------------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    |  1 | PRIMARY            | t2    | ALL    | NULL          | NULL    | NULL    | NULL            |    4 | Using where |
    |  3 | DEPENDENT SUBQUERY | t1    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t2.to_id   |    1 | Using index |
    |  2 | DEPENDENT SUBQUERY | t1    | eq_ref | PRIMARY       | PRIMARY | 4       | test.t2.from_id |    1 | Using index |
    +----+--------------------+-------+--------+---------------+---------+---------+-----------------+------+-------------+
    3 rows in set (0.00 sec)
    

    很难说哪个会更快,我没有你的数据集。只要 table2.from_id、table2.to_id 和 t1.id 被索引,只要它们被正确声明为外键和主键,就可以了。

    如果它仍然不够快,我建议您使用create a view 或临时表或query cache,而不是非规范化。这些可以有效地缓存查询,而不必进行非规范化。您选择哪一种取决于您的数据更新频率以及您的应用程序对更改的敏感程度。

    【讨论】:

    • 感谢您提供如此详细的答案。我要进行非规范化,因为a)我必须经常使用子集b)我需要导出子集并且c)数据不会改变......我猜b)不是真的有正当理由,但(我认为?) a) 是。 Sequel Pro 也不允许您创建视图...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 2022-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多