Проблема з запитом 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 обробляє підзапити. Насправді, підзапити будуть зазнавати переписування та перетворення .

Тут пояснено чотири (4) компоненти:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

З опублікованих прикладів неможливо дозволити item_ref стати самовідправкою. Що стосується вашого єдиного DELETE-запиту, тестова таблиця в цілому не може повністю самостійно посилатися на себе, оскільки деякі клавіші доступні під час трансформації, а деякі - ні. Тому, коли запит виконує самопосилання, ключ (у цьому випадку ідентифікатор) може зникнути при перетворенні, навіть незважаючи на те, що у власній таблиці самопосилання є ключ.

Підзапроси Mysql чудово підходять лише для суб-SELECT, навіть самостійно посилаючись на таблицю кілька разів. Те саме не можна сказати для запитів, які не вибираються.

Сподіваюся, це пояснення допомагає.


7

Я думаю, що причина, по якій вона не працює, як очікувалося, полягає не в тому, як MySQL обробляє підзапити, а в тому, як UPDATEоператори MySQL обробляють . Заява:

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не підтримуються в MySQL підзапитів. Я не впевнений, чому це не робить помилку для вас.


2
LIMITне підтримується лише для використання IN (<code> замінено на задній план ~ drachenstern)
tomas.lang

ну ... я щось дізнався, що пояснює, чому це не помилка!
Дерек Дауні

@ tomas.lang ви можете використовувати `(галочки) навколо слова замість блоків <code>.
Дерек Дауні
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.