2008-09-05 594 views
444

Tengo una tabla story_category en mi base de datos con entradas dañadas. La siguiente consulta devuelve las entradas corruptas:MySQL Error 1093 - No se puede especificar la tabla de destino para la actualización en la cláusula FROM

SELECT * 
FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
     story_category ON category_id=category.id); 

Me trataron de eliminarlos ejecución:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category 
     INNER JOIN story_category ON category_id=category.id); 

pero me da el siguiente error:

#1093 - You can't specify target table 'story_category' for update in FROM clause

¿Cómo puedo superar esto?

+2

relacionadas: http://stackoverflow.com/a/14302701/238419 –

+0

parece como la solicitud de función en el rastreador de errores de MySQL está aquí: [no se puede actualizar una tabla y seleccionar de la misma tabla en una subconsulta] (https: //bugs.mys ql.com/bug.php?id=23353) –

Respuesta

578

Actualización: Esta respuesta cubre la clasificación de error general. Para obtener una respuesta más específica sobre cómo manejar mejor la consulta exacta del OP, consulte otras respuestas a esta pregunta

En MySQL, no puede modificar la misma tabla que utiliza en la parte SELECCIONAR.
Este comportamiento se documenta en: http://dev.mysql.com/doc/refman/5.6/en/update.html

Tal vez usted puede simplemente unirse a la tabla consigo misma

Si la lógica es suficiente para volver a dar forma a la consulta, perderá la subconsulta y unirse a la mesa a la simple sí mismo, empleando los criterios de selección apropiados. Esto hará que MySQL vea la tabla como dos cosas diferentes, permitiendo que los cambios destructivos continúen.

UPDATE tbl AS a 
INNER JOIN tbl AS b ON .... 
SET a.col = b.col 

otra parte, trate de anidación de la subconsulta más profundamente en una cláusula FROM ...

Si necesita absolutamente la subconsulta, hay una solución, pero es fea por varias razones, incluyendo la actuación:

UPDATE tbl SET col = (
    SELECT ... FROM (SELECT.... FROM) AS x); 

la subconsulta anidada en la cláusula FROM crea una tabla temporal implícita , por lo que no cuenta como la misma tabla que está actualizando.

... pero cuidado con el optimizador de consultas

Sin embargo, ten en cuenta que a partir MySQL 5.7.6 y en adelante, el optimizador puede optimizar la subconsulta, y todavía le dará el error. Afortunadamente, la variable optimizer_switch se puede usar para desactivar este comportamiento; aunque no podría recomendar hacer esto como algo más que una solución a corto plazo, o para pequeñas tareas únicas.

SET optimizer_switch = 'derived_merge=off'; 

Gracias a Peter V. Mørch para este consejo en los comentarios.

La técnica de ejemplo fue de Baron Schwartz, originally published at Nabble, parafraseada y extendida aquí.

+1

upvoted esta respuesta porque tenía que eliminar elementos y no podía obtener información de otra tabla, tuvieron que subconsulta de una misma mesa. Como esto es lo que aparece en la parte superior mientras busco en Google el error que obtuve, esta sería la respuesta más adecuada para mí y para mucha gente que intenta actualizar mientras subcuenta desde la misma tabla. – HMR

+2

@Cheekysoft, ¿por qué no guardar los valores en variables en su lugar? – Pacerier

+15

Cuidado, que a partir de [MySQL 5.7.6 on] (http://dev.mysql.com/doc/relnotes/mysql/5.7/en/news-5-7-6.html), el optimizador puede optimizar la sub -query de distancia y todavía le dan el error, a menos que 'set 'optimizer_switch derived_merge = off';' :-( –

4

Puede insertar los identificadores de las filas deseadas en una tabla temporal y luego eliminar todas las filas que se encuentran en esa tabla.

que puede ser lo que @Cheekysoft significó hacerlo en dos pasos.

1

Si algo no funciona, al venir a través de la puerta principal, luego tomar la puerta de atrás:

drop table if exists apples; 
create table if not exists apples(variety char(10) primary key, price int); 

insert into apples values('fuji', 5), ('gala', 6); 

drop table if exists apples_new; 
create table if not exists apples_new like apples; 
insert into apples_new select * from apples; 

update apples_new 
    set price = (select price from apples where variety = 'gala') 
    where variety = 'fuji'; 
rename table apples to apples_orig; 
rename table apples_new to apples; 
drop table apples_orig; 

Es rápido. Mientras más grande sea la información, mejor.

+7

y usted acaba de perder todas sus claves externas, y tal vez haya alguna elimina en cascada, también. Esto debería – Walf

98

El inner join en su subconsulta no es necesario. Parece que desea eliminar las entradas en story_category donde category_id no está en la tabla category.

Haga lo siguiente:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category); 

En lugar de eso:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
     story_category ON category_id=category.id); 
+5

¡Sé la mejor respuesta! Quizás elimines e primero "en lugar de". – hoyhoy

+1

y fijar el bloque de código – hoyhoy

+1

Creo 'DISTINCT' es innecesaria aquí - para un mejor rendimiento;). –

11

Esto es lo que hice para la actualización de un valor de columna Prioridad por 1 si es> = 1 en una mesa y en su Cláusula WHERE utilizando una subconsulta en la misma tabla para asegurarse de que al menos una fila contiene Prioridad = 1 (porque esa era la condición que debe verificarse al realizar la actualización):


UPDATE My_Table 
SET Priority=Priority + 1 
WHERE Priority >= 1 
AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t); 

Sé que es un poco feo pero funciona bien.

+1

@anonymous_reviewer: En caso de dar [-1] o incluso [+1] a un comentario de alguien, también mencione por qué lo ha otorgado. ¡¡¡Gracias!!! – sactiw

+1

-1 porque esto es incorrecto. No puede modificar la misma tabla que usa en la instrucción SELECT. – Chris

+1

@Chris Lo he verificado en MySQL y funciona muy bien para mí, así que le pediría que por favor lo verifique al final y luego lo reclame como correcto o incorrecto. ¡¡¡Gracias!!! – sactiw

28
DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT cid FROM (
     SELECT DISTINCT category.id AS cid FROM category INNER JOIN story_category ON category_id=category.id 
    ) AS c 
) 
+3

¿Puedes explicar por qué funciona esto, por qué solo anidar un nivel más funciona? Esta pregunta ya se ha formulado como un comentario a la pregunta de @EkoNoval, pero nadie respondió. Quizás puedas ayudar. –

+0

@AkshayArora, repase la parte bajo el encabezado 'Quizás pueda unirse a la mesa' en la respuesta de @ Cheekysoft. Dijo 'UPDATE tbl AS INNER JOIN tbl AS b ON .... SET a.col = b.col' - esto funcionaría como un alias diferente para la misma tabla que se utiliza aquí. De forma similar, en la respuesta de @NexusRex, la primera consulta 'SELECT' actúa como una tabla derivada en la que' story_category' se utiliza por segunda vez. Entonces, el error mencionado en el PO no debería tener lugar aquí, ¿verdad? –

192

NexusRex proporciona un very good solution para borrar con unirse de la misma tabla.

Si hace esto:

DELETE FROM story_category 
WHERE category_id NOT IN (
     SELECT DISTINCT category.id AS cid FROM category 
     INNER JOIN story_category ON category_id=category.id 
) 

que una va a conseguir un error.

Pero si se coloca la condición en una más seleccione

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT cid FROM (
     SELECT DISTINCT category.id AS cid FROM category 
     INNER JOIN story_category ON category_id=category.id 
    ) AS c 
) 

sería hacer lo correcto !!

Explicación: El optimizador de consultas hace un derived merge optimization para la primera consulta (que hace que se producirá el error), pero la segunda consulta no califica para la optimización de combinación de derivados por lo tanto, el optimizador se ve obligado a ejecuta la subconsulta primero.

+1

Tal vez sea porque estoy en un aprieto hoy, pero esta fue la respuesta más fácil, incluso si tal vez no es la "mejor". –

+4

Esto funcionó muy bien, ¡gracias! Entonces, ¿cuál es la lógica aquí? Si está anidado un nivel más, ¿se ejecutará antes de la parte externa? Y si no está anidado, ¿mySQL intenta ejecutarlo después de que la eliminación tenga un bloqueo en la tabla? – Ben

+16

Este error y solución no tienen sentido lógico ... pero funciona. A veces me pregunto en qué drogas están los desarrolladores de MySQL ... – Cerin

77

Recientemente tuve que actualizar los registros en la misma mesa lo hice, como a continuación:

UPDATE skills AS s, (SELECT id FROM skills WHERE type = 'Programming') AS p 
SET s.type = 'Development' 
WHERE s.id = p.id; 
+4

La respuesta más fácil de entender aquí. ¡Gracias! –

+3

¡Asombroso! ¡Entonces, leer todas las respuestas es una buena práctica! Gracias. –

+2

No estoy seguro de por qué esto no tiene más votos, gracias! – MrB

3

Según the Mysql UPDATE Syntax unidos por @CheekySoft, se dice en la parte inferior derecha.

Currently, you cannot update a table and select from the same table in a subquery.

Supongo que está eliminando de store_category mientras selecciona desde allí en la unión.

1

Intenta guardar el resultado de la instrucción Select en una variable separada y luego usarla para eliminar la consulta.

10

Si no se puede hacer

UPDATE table SET a=value WHERE x IN 
    (SELECT x FROM table WHERE condition); 

porque es la misma tabla, puede engañar y hacer:

UPDATE table SET a=value WHERE x IN 
    (SELECT * FROM (SELECT x FROM table WHERE condition) as t) 

[actualizar o borrar o lo que sea]

0

¿qué tal esta consulta espero que ayude

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL 
+0

El resultado muestra: '# 1064 - Usted tiene un error en su sintaxis de SQL; revise el manual que corresponde a su versión del servidor MariaDB para la sintaxis correcta para usar cerca de 'LEFT JOIN (SELECCIONAR categories.id FROM categories) cat ON story_category.id = cat.' en la línea 1 ' –

0

La forma más sencilla de hacerlo es utilizar un alias de tabla cuando refiera la tabla de consulta principal dentro de la consulta secundaria.

Ejemplo:

insert into xxx_tab (trans_id) values ((select max(trans_id)+1 from xxx_tab)); 

Cambiar a:

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P)); 
0

probar esto

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc; 
Cuestiones relacionadas