Problema con la subconsulta MySQL


16

¿Por qué esta consulta

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

a veces eliminar 1 fila, a veces 2 filas y a veces nada?

Si lo escribo de esta forma:

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

entonces funciona correctamente: ¿hay algún problema en la subconsulta?

Respuestas:


13

La razón por la que la primera consulta no funciona consistentemente tiene que ver con cómo MySQL procesa las subconsultas. De hecho, las subconsultas experimentarán reescrituras y transformaciones .

Aquí se explican cuatro (4) componentes:

  • Item_in_optimizer
  • Item_in_subselect
  • Item_ref
  • Left_expression_Cache

De los ejemplos publicados, sería imposible permitir que un item_ref se convierta en una auto referencia. En términos de su única consulta DELETE, la tabla de prueba en su conjunto no puede autorreferenciarse completamente porque algunas claves están disponibles durante la transformación y otras no. Por lo tanto, cuando una consulta realiza una autorreferencia, una clave (en este caso, id) puede desaparecer en una transformación aunque la tabla real autorreferenciada tenga la clave.

Las subconsultas de Mysql solo son excelentes para sub-SELECCIONAR, incluso para auto-referenciar una tabla varias veces. No se puede decir lo mismo de las consultas que no son SELECT.

Espero que esta explicación ayude.


7

Creo que la razón por la que no funciona como se esperaba no es cómo MySQL procesa las subconsultas sino cómo MySQL procesa las UPDATEdeclaraciones. La declaración:

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

procesará la WHEREcondición fila por fila. Es decir, para cada fila, ejecutará la subconsulta y probará el resultado con id:

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

Por lo tanto, ocasionalmente coincidirá (y eliminará) 0, 1, 2 o incluso más filas.


Puede reescribirlo así y la subconsulta se procesará una vez:

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

1

Desde la primera viñeta de esta página , LIMITno se admite en las subconsultas de mysql. Sin embargo, no estoy seguro de por qué no arroja un error.


2
LIMITno es compatible solo con el uso IN (<code> reemplazado con backticks ~ drachenstern)
tomas.lang

bueno ... aprendí algo, lo que explica por qué no arrojó un error.
Derek Downey

@ tomas.lang puede usar `(marcas de verificación) que rodean la palabra, en lugar de bloques <code>.
Derek Downey
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.