Mysql
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Mysql

Tại sao điều kiện IN lại chậm hơn =trong sql?

Tóm tắt:Đây là một sự cố đã biết trong MySQL và đã được sửa trong MySQL 5.6.x. Vấn đề là do thiếu tối ưu hóa khi một truy vấn con sử dụng IN được xác định không chính xác là truy vấn con phụ thuộc thay vì một truy vấn con độc lập.

Khi bạn chạy EXPLAIN trên truy vấn ban đầu, nó sẽ trả về kết quả này:

1  'PRIMARY'             'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
2  'DEPENDENT SUBQUERY'  'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
3  'DEPENDENT SUBQUERY'  'question_law'          'ALL'  ''  ''  ''  ''  10040  'Using where'

Khi bạn thay đổi IN thành = bạn nhận được cái này:

1  'PRIMARY'   'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
2  'SUBQUERY'  'question_law_version'  'ALL'  ''  ''  ''  ''  10148  'Using where'
3  'SUBQUERY'  'question_law'          'ALL'  ''  ''  ''  ''  10040  'Using where'

Mỗi truy vấn con phụ thuộc được chạy một lần trên mỗi hàng trong truy vấn mà nó chứa trong đó, trong khi truy vấn con chỉ được chạy một lần. MySQL đôi khi có thể tối ưu hóa các truy vấn phụ phụ thuộc khi có một điều kiện có thể được chuyển đổi thành một phép nối nhưng ở đây không phải vậy.

Tất nhiên, điều này khiến câu hỏi tại sao MySQL tin rằng phiên bản IN cần phải là một truy vấn con phụ thuộc. Tôi đã tạo một phiên bản đơn giản của truy vấn để giúp điều tra vấn đề này. Tôi đã tạo hai bảng 'foo' và 'bar' trong đó bảng trước chỉ chứa một cột id và bảng sau chứa cả id và id foo (mặc dù tôi không tạo ràng buộc khóa ngoại). Sau đó, tôi điền cả hai bảng với 1000 hàng:

CREATE TABLE foo (id INT PRIMARY KEY NOT NULL);
CREATE TABLE bar (id INT PRIMARY KEY, foo_id INT NOT NULL);

-- populate tables with 1000 rows in each

SELECT id
FROM foo
WHERE id IN
(
    SELECT MAX(foo_id)
    FROM bar
);

Truy vấn đơn giản hóa này có cùng một vấn đề như trước - lựa chọn bên trong được coi như một truy vấn phụ phụ thuộc và không có tối ưu hóa nào được thực hiện, khiến truy vấn bên trong được chạy một lần trên mỗi hàng. Truy vấn mất gần một giây để chạy. Thay đổi IN thành = lại cho phép truy vấn chạy gần như ngay lập tức.

Dưới đây là đoạn mã tôi đã sử dụng để điền các bảng, phòng trường hợp bất kỳ ai muốn tạo lại kết quả.

CREATE TABLE filler (
        id INT NOT NULL PRIMARY KEY AUTO_INCREMENT
) ENGINE=Memory;

DELIMITER $$

CREATE PROCEDURE prc_filler(cnt INT)
BEGIN
        DECLARE _cnt INT;
        SET _cnt = 1;
        WHILE _cnt <= cnt DO
                INSERT
                INTO    filler
                SELECT  _cnt;
                SET _cnt = _cnt + 1;
        END WHILE;
END
$$

DELIMITER ;

CALL prc_filler(1000);

INSERT foo SELECT id FROM filler;
INSERT bar SELECT id, id FROM filler;


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kích thước bộ nhớ tối đa TINYTEXT, TEXT, MEDIUMTEXT và LONGTEXT

  2. Truy vấn SQL trả về dữ liệu từ nhiều bảng

  3. MySQL Thay đổi mật khẩu người dùng

  4. JSON_MERGE_PRESERVE () - Hợp nhất nhiều tài liệu JSON trong MySQL

  5. Lưu ý:Chuyển đổi mảng thành chuỗi trong