Select 来自 table 其他 table 最有效的方式

Select from table where in other table most efficient way

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

我想制作第三个 table,它与第二个具有相同的结构,但只有 'from' 和 'to' ID 在首先table。

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

我目前的查询如下:

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

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

首先,使用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 100,然后 limit 1000 等放在查询上来在用户界面中测试查询。这将使您了解随着数据的增长性能如何。

I'd like to make a third table that has the same structure as the second, but with only those rows for which the 'from' and 'to' IDs are in the first table.

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

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

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

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

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 or a temporary table or a query cache 而不是反规范化。这些可以有效地缓存查询而无需非规范化。您选择哪个取决于您的数据更新频率以及您的应用程序对更改的敏感程度。