ปัญหากับแบบสอบถามย่อย MySQL


16

ทำไมแบบสอบถามนี้

DELETE FROM test 
WHERE id = ( SELECT id 
             FROM (SELECT * FROM test) temp 
             ORDER BY RAND() 
             LIMIT 1
           );

บางครั้งลบ 1 แถวบางครั้ง 2 แถวและบางครั้งไม่มีอะไร

ถ้าฉันเขียนมันในรูปแบบนี้:

SET @var = ( SELECT id 
             FROM (SELECT * FROM test) temp 
             ORDER BY RAND() 
             LIMIT 1
           ); 
DELETE FROM test 
WHERE id=@var;

จากนั้นทำงานอย่างถูกต้อง - มีปัญหาในข้อความค้นหาย่อยหรือไม่

คำตอบ:


13

เหตุผลที่คิวรีแรกไม่ทำงานอย่างต่อเนื่องเกี่ยวข้องกับวิธีที่ MySQL ประมวลผลคิวรีย่อย ในความเป็นจริงsubqueries จะได้สัมผัสกับการปรับเปลี่ยนและการแปลง

มีองค์ประกอบสี่ (4) อธิบายที่นี่:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

จากตัวอย่างที่โพสต์มันเป็นไปไม่ได้ที่จะอนุญาตให้ item_ref กลายเป็นการอ้างอิงตนเอง ในแง่ของแบบสอบถาม DELETE เดียวของคุณตารางทดสอบโดยรวมไม่สามารถอ้างอิงตัวเองได้อย่างเต็มที่เนื่องจากมีบางคีย์ที่สามารถใช้ได้ระหว่างการแปลงและบางอันไม่ ดังนั้นเมื่อเคียวรีทำการอ้างอิงตนเองคีย์ (ในกรณีนี้ id) สามารถหายไปในการแปลงแม้ว่าตารางการอ้างอิงตนเองที่แท้จริงมีคีย์

แบบสอบถามย่อย Mysql นั้นยอดเยี่ยมสำหรับการเลือกย่อยเท่านั้นแม้จะอ้างถึงตารางตัวเองหลาย ๆ ครั้ง ไม่สามารถพูดแบบเดียวกันสำหรับคำสั่งที่ไม่ได้เลือก

ฉันหวังว่าคำอธิบายนี้จะช่วยได้


7

ฉันคิดว่าเหตุผลที่มันไม่ทำงานอย่างที่คาดไว้ไม่ใช่วิธีที่ MySQL ประมวลผลคิวรีย่อย แต่วิธีที่ MySQL ประมวลผลUPDATEคำสั่ง คำสั่ง:

DELETE 
FROM test 
WHERE id = 
      ( SELECT id 
        FROM 
            ( SELECT * 
              FROM test
            ) temp 
        ORDER BY RAND() 
        LIMIT 1
      ) 

จะประมวลผลWHEREเงื่อนไขทีละแถว ความหมายสำหรับทุกแถวมันจะเรียกใช้เคียวรีย่อยและทดสอบผลลัพธ์กับid:

  ( SELECT id 
    FROM 
        ( SELECT * 
          FROM test
        ) temp 
    ORDER BY RAND() 
    LIMIT 1
  ) 

ดังนั้นมันจะจับคู่ (และลบ) เป็นครั้งคราว 0, 1, 2 หรือมากกว่านั้น!


คุณสามารถเขียนซ้ำได้เช่นนี้และแบบสอบถามย่อยจะถูกประมวลผลครั้งเดียว:

DELETE t
FROM 
      test t
  JOIN 
      ( SELECT id 
        FROM test  
        ORDER BY RAND() 
        LIMIT 1
      ) tmp
    ON tmp.id = t.id

1

จากกระสุนเป็นครั้งแรกในหน้านี้ , LIMITไม่ได้รับการสนับสนุนใน subqueries MySQL ฉันไม่แน่ใจว่าทำไมมันไม่ทำให้เกิดข้อผิดพลาดสำหรับคุณ


2
LIMITไม่รองรับเฉพาะการใช้IN (<code> แทนที่ด้วย backticks ~ drachenstern)
tomas.lang

ดี ... ฉันได้เรียนรู้บางสิ่งบางอย่างซึ่งอธิบายว่าทำไมมันไม่ผิดพลาด!
Derek Downey

@ tomas.lang คุณสามารถใช้ `(เครื่องหมายถูก) โดยรอบคำแทนที่จะเป็น <code> บล็อก
Derek Downey
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.