This SELECT query takes 180 seconds to finish

后端 未结 3 1629
[愿得一人]
[愿得一人] 2020-12-06 02:08

UPDATE:

Just to mention it on a more visible place. When I changed IN for =, the query execution time went from 180 down to 0.00008 seconds. Ridiculous speed differe

相关标签:
3条回答
  • 2020-12-06 02:31

    (Posting my comment as an answer as apparently it did make a difference!)

    Any difference if you change the IN to =?

    If anyone wants to investigate this further I've just done a test and found it very easy to reproduce.

    Create Table

    CREATE TABLE `filler` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      PRIMARY KEY (`id`)
    ) 
    

    Create Procedure

    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
    

    Populate Table

      call prc_filler(5000)
    

    Query 1

    SELECT id 
    FROM filler 
    WHERE id =  (SELECT MAX(id) FROM filler  WHERE id =   
     ( SELECT MIN(id) 
        FROM filler
        WHERE id between 2000 and 3000
        )
      )
    

    Equals Explain Output http://img689.imageshack.us/img689/5592/equals.png

    Query 2 (same problem)

    SELECT id 
    FROM filler 
    WHERE id in  (SELECT MAX(id) FROM filler  WHERE id in   
     ( SELECT MIN(id) 
        FROM filler
        WHERE id between 2000 and 3000
        )
      )
    

    In Explain Output http://img291.imageshack.us/img291/8129/52037513.png

    0 讨论(0)
  • 2020-12-06 02:37

    You can use EXPLAIN to find out how is it possible for a query to execute so slow.

    MySQL does not really like nested subselects so probably what happens is that it goes and does sorts on disk to get min and max and fail to reuse results.

    Rewriting as joins would probably help it.

    If just looking for a quick fix try:

    SET @temp1 =     
      (
      SELECT MIN(IdQuestionLaw) 
      FROM question_law 
      WHERE IdQuestion = 236 AND IdQuestionLaw > 63
      )
    
    SET @temp2 = 
      (
      SELECT MAX(IdQuestionLawVersion) 
      FROM question_law_version 
      WHERE IdQuestionLaw = @temp1
      )
    
    SELECT IdLawVersionValidFrom 
    FROM question_law_version 
    WHERE IdQuestionLawVersion = @temp2
    
    0 讨论(0)
  • 2020-12-06 02:38

    Here is a good explanation why = is better than IN

    Mysql has problems with inner queries - not well using indexes (if at all).

    1. Make sure you have indexes on all the fields in the join/where/order etc.
    2. get those Max and MIN values in a separate query (use stored procedure for this entire thing if you want to skip the multiple requests overhead Or just do a request with multiple queries.

    Anyway:

    SELECT
             IdLawVersionValidFrom 
    FROM 
             question_law_version 
        JOIN 
             question_law
          ON 
             question_law_version.IdQuestionLaw = question_law.IdQuestionLaw
    WHERE 
             question_law.IdQuestion=236 
         AND 
             question_law.IdQuestionLaw>63
    
    ORDER BY 
             IdQuestionLawVersion DESC, 
             question_law.IdQuestionLaw ASC
    LIMIT 1
    
    0 讨论(0)
提交回复
热议问题