【问题标题】:Which MySQL Query is faster?哪个 MySQL 查询更快?
【发布时间】:2012-05-27 21:32:54
【问题描述】:

哪个查询执行得更快,哪个是完美查询?

SELECT
    COUNT(*) AS count
FROM
    students
WHERE
    status = 1
AND
    classes_id IN(
                    SELECT
                        id
                    FROM
                        classes
                    WHERE
                        departments_id = 1
                );

或者

SELECT
    COUNT(*) AS count
FROM
    students s
LEFT JOIN
    classes c
ON
    c.id = s.classes_id
WHERE
    status = 1
AND
    c.departments_id = 1

我已经放置了两个查询都将输出相同的结果。现在我想知道哪种方法执行得更快,哪种方法是正确的?

【问题讨论】:

  • 您测试时哪个更快?
  • 使用EXPLAIN 运行两者并亲自查看。示例:explain select count(*) ...
  • 解释一下你会得到答案
  • @eggyal:有趣,但主题不同。
  • 你的表结构很奇怪。为什么学生有“classes_id”?您将如何代表一名学生参加多个课程?

标签: mysql database


【解决方案1】:

您应该始终使用EXPLAIN 来确定您的查询将如何运行。

不幸的是,MySQL 会将您的子查询作为 DEPENDENT QUERY 执行,这意味着子查询将针对外部查询中的每一行运行。你会认为 MySQL 足够聪明,可以检测到子查询不是相关子查询,并且只会运行一次,唉,它还没有那么聪明。

因此,MySQL 将扫描学生中的所有行,为每一行运行子查询,并且不使用外部查询的任何索引。

将查询编写为 JOIN 将允许 MySQL 使用索引,以下查询将是编写它的最佳方式:

SELECT COUNT(*) AS count
FROMstudents s
JOIN classes c
  ON c.id = s.classes_id
  AND c.departments_id = 1
WHERE s.status = 1

这将使用以下索引:

students(`status`)
classes(`id`, `departements_id`) : multi-column index

【讨论】:

    【解决方案2】:

    从设计和清晰度的角度来看,我会避免像第一个那样选择内部选择。确实,要 100% 确定是否或如何优化每个查询以及哪个查询会“更好”运行,需要了解您使用的 SQL 服务器将如何解释它及其计划。在 Mysql 中,使用“解释”。

    但是....即使没有看到这一点,我的钱仍然在仅加入版本上...内部选择版本必须在确定要在“IN”子句中使用的值之前完整地执行内部选择--我知道当您将东西包装在函数中时这是正确的,并且在将选择作为 IN 争论插入时很确定这是正确的。我也知道这是完全抵消内部选择中的表上的索引可能带来的任何好处的好方法。

    我通常认为内部选择仅在非常罕见的查询情况下才真正需要。通常,使用它们的人经常像传统的迭代流程序员一样思考,而不是真正用关系数据库结果集术语思考......

    【讨论】:

      【解决方案3】:

      EXPLAIN两个查询单独

      两个查询之间的区别在于子查询与连接

      大多数情况下,连接比子查询快。 Join 创建执行计划并预测将要处理的数据,因此可以节省时间。另一方面,子查询运行所有查询,直到加载所有数据。大多数开发人员使用子查询,因为它们比 JOIN 更具可读性,但在性能很重要的地方,JOIN 是更好的解决方案。

      【讨论】:

        【解决方案4】:

        找出答案的最好方法是测量它:

        无索引

        • 查询1:0.9s
        • 查询 2:0.9s

        有索引

        • 查询1:0.4s
        • 查询 2:0.2s

        结论是:

        • 如果您没有索引,则使用哪个查询没有区别。
        • 如果您有正确的索引,连接会更快。
        • 添加正确索引的效果大于选择正确查询的效果。如果性能很重要,请确保您拥有正确的索引。

        当然,您的结果可能会因 MySQL 版本和您拥有的数据分布而异。

        这是我的测试方法:

        • 1,000,000 名学生(25% 处于状态 1)。
        • 50,000 门课程。
        • 10 个部门。

        这是我用来创建测试数据的 SQL:

        CREATE TABLE students
        (id INT PRIMARY KEY AUTO_INCREMENT,
        status int NOT NULL,
        classes_id int NOT NULL);
        
        CREATE TABLE classes
        (id INT PRIMARY KEY AUTO_INCREMENT,
        departments_id INT NOT NULL);
        
        CREATE TABLE numbers(id INT PRIMARY KEY AUTO_INCREMENT);
        
        INSERT INTO numbers VALUES (),(),(),(),(),(),(),(),(),();
        
        INSERT INTO numbers
        SELECT NULL
        FROM numbers AS n1
        CROSS JOIN numbers AS n2
        CROSS JOIN numbers AS n3
        CROSS JOIN numbers AS n4
        CROSS JOIN numbers AS n5
        CROSS JOIN numbers AS n6;
        
        INSERT INTO classes (departments_id)
        SELECT id % 10 FROM numbers WHERE id <= 50000;
        
        INSERT INTO students (status, classes_id)
        SELECT id % 4 = 0, id % 50000 + 1 FROM numbers WHERE id <= 1000000;
        
        SELECT COUNT(*) AS count
        FROM students
        WHERE status = 1
        AND classes_id IN (SELECT id FROM classes WHERE departments_id = 1);
        
        SELECT COUNT(*) AS count
        FROM students s
        LEFT JOIN classes c
        ON c.id = s.classes_id
        WHERE status = 1
        AND c.departments_id = 1;
        
        CREATE INDEX ix_students ON students(status, classes_id);
        

        【讨论】:

          【解决方案5】:

          这两个查询不会产生相同的结果:

          SELECT
              COUNT(*) AS count
          FROM
              students
          WHERE
              status = 1
          AND
              classes_id IN(
                              SELECT
                                  id
                              FROM
                                  classes
                              WHERE
                                  departments_id = 1
                          );
          

          ...将返回学生表中具有 classes_id 字段的行数,该字段也在 classes_id 为 1 的 classes 表中。

          SELECT
              COUNT(*) AS count
          FROM
              students s
          LEFT JOIN
              classes c
          ON
              c.id = s.classes_id
          WHERE
              status = 1
          AND
              c.departments_id = 1
          

          ...将返回学生表中状态字段为 1 的总行数,并且可能会更多,具体取决于数据的组织方式。

          如果您希望查询返回相同的内容,则需要将 LEFT JOIN 更改为 INNER JOIN,以便它只匹配同时满足这两个条件的行。

          【讨论】:

          • 这是正确的。碰巧这两个查询将在 OP 的情况下返回相同的结果,因为我假设所有 students.classes_id 引用 classes.id。但在一般情况下,这两个查询在语义上并不相同,因此 MySQL 无法以相同的方式优化它们。
          【解决方案6】:

          在两个查询上运行 EXPLAIN SELECT ... 并检查哪一个做了什么;)

          【讨论】:

            猜你喜欢
            • 2011-03-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2023-03-27
            • 1970-01-01
            • 2020-10-16
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多