Error 1093 de MySQL: no se puede especificar la tabla de destino para la actualización en la cláusula FROM

Tengo una mesa story_category en mi base de datos con entradas corruptas. La siguiente consulta devuelve las entradas corruptas:

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

Traté de eliminarlos ejecutando:

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 obtengo el siguiente error:

# 1093 - No se puede especificar la tabla de destino 'story_category' para la actualización en la cláusula FROM

¿Cómo puedo superar esto?

preguntado el 05 de septiembre de 08 a las 08:09

16 Respuestas

Actualización: esta respuesta cubre la clasificación general de errores. 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 usa en la parte SELECT.
Este comportamiento está documentado en: http://dev.mysql.com/doc/refman/5.6/en/update.html

Tal vez puedas unirte a la mesa para sí mismo

Si la lógica es lo suficientemente simple como para cambiar la forma de la consulta, pierda la subconsulta y una la tabla a sí misma, empleando los criterios de selección adecuados. Esto hará que MySQL vea la tabla como dos cosas diferentes, permitiendo que se produzcan cambios destructivos.

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

Alternativamente, intente anidar la subconsulta más profundamente en una cláusula from ...

Si es absolutamente necesario la subconsulta, hay una solución, pero es desagradable por varias razones, incluido el rendimiento:

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, tenga en cuenta que desde MySQL 5.7.6 y en adelante, el optimizador puede optimizar la subconsulta y aún así darle el error. Afortunadamente, el optimizer_switch La variable se puede utilizar para desactivar este comportamiento; aunque no puedo recomendar hacer esto como algo más que una solución a corto plazo, o para pequeñas tareas puntuales.

SET optimizer_switch = 'derived_merge=off';

Gracias a Peter V. Morch por este consejo en los comentarios.

La técnica de ejemplo fue del Baron Schwartz, publicado originalmente en Nabble, parafraseado y ampliado aquí.

contestado el 23 de mayo de 17 a las 13:05

Voté esta respuesta porque tuve que eliminar elementos y no pude obtener información de otra tabla, tuve que realizar una subconsulta de la misma tabla. Dado que esto es lo que aparece en la parte superior mientras busco en Google el error, obtuve esta sería la mejor respuesta para mí y para muchas personas que intentan actualizar mientras subconsultas desde la misma tabla. - HMR

@Cheekysoft, ¿por qué no guardar los valores en variables? - marcapasos

@ PeterV.Mørch Siguiendo mysqlserverteam.com/tablas-derivadas-en-mysql-5-7, en caso de que se realicen determinadas operaciones, la fusión no puede ocurrir. Por ejemplo, proporcione a la tabla ficticia derivada un LÍMITE (a inifity) y el error nunca ocurrirá. Sin embargo, eso es bastante complicado y todavía existe el riesgo de que las versiones futuras de MySQL admitan la fusión de consultas con LIMIT después de todo. - user2180613

¿Puede, por favor, proporcionar un ejemplo completo sobre esta solución? ACTUALIZAR tbl SET col = (SELECT ... FROM (SELECT .... FROM) AS x); sigo recibiendo errores - joelbonetr

INNER JOIN Trabaja para mí :) - hassanrazadev

NexoRex proporcionó un muy buena solucion para eliminar con unión de la misma tabla.

Si haces 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
)

obtendrá un error.

Pero si envuelve la condición en una selección más:

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
)

haría lo correcto !!

Explicación: El optimizador de consultas hace una optimización de fusión derivada para la primera consulta (lo que hace que falle con el error), pero la segunda consulta no califica para la optimización de fusión derivada. Por lo tanto, el optimizador se ve obligado a ejecutar la subconsulta primero.

Respondido el 09 de Septiembre de 18 a las 17:09

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

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

Este error y esta solución no tienen sentido lógico ... pero funciona. A veces me pregunto qué medicamentos están tomando los desarrolladores de MySQL ... - Cerín

De acuerdo con @Cerin ... esto es completamente absurdo, pero funciona. - FastTrack

@ekonoval Gracias por la solución pero no tiene el más mínimo sentido para mí, parece que estás engañando a MySQL y él la acepta, jejeje - defreitas

LA inner join en su subconsulta es innecesario. Parece que desea eliminar las entradas en story_category donde el category_id no está en el category mesa.

Hacer esto:

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);

contestado el 31 de mayo de 15 a las 10:05

¡Esta debería ser la mejor respuesta! Quizás elimine el primer "en lugar de". - hoyhoy

Creo que el DISTINCT aquí es innecesario - para un mejor rendimiento;). - mierda

Debo estar loco. La respuesta es la misma que se dijo en el original. - jeff lowery

Esta es la respuesta también para mí. No where in en la columna ID, por lo que no es necesario realizar una subconsulta en la tabla principal. - Richard

@JeffLowery - el la primera el bloque de código es la respuesta aquí; es el segundo bloque de código que proviene de la pregunta. - Fabricante de herramientasSteve

Recientemente tuve que actualizar los registros en la misma tabla, 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;

Respondido el 20 de diciembre de 13 a las 10:12

¿No se puede escribir esto como UPDATE skills SET type='Development' WHERE type='Programming'; ? Esto no parece responder a la pregunta original. - Lilbyrdie

Parece exagerado, @lilbyrdie tiene razón, solo podría ser UPDATE skills SET type='Development' WHERE type='Programming';. No entiendo por qué tanta gente no está pensando en lo que hace ...- sombreado

esta es la mejor respuesta aquí, en mi humilde opinión. Su sintaxis es fácil de entender, puede reutilizar su declaración anterior y no se limita a un caso súper específico. - Esteban Winkler

Incluso si el caso de ejemplo es cuestionable, el principio es el mejor para comprender e implementar en escenarios del mundo real. La consulta mostrada funciona porque la combinación implícita en la lista de tablas crea una tabla temporal para una subconsulta, proporcionando así resultados estables y evitando conflictos entre la recuperación y modificación de la misma tabla. - AnrDaemon

Independientemente de lo que alguien pueda decir acerca de que esto es una exageración, todavía responde a la pregunta en términos del título. El error que mencionó el OP también se encuentra cuando se intenta una actualización utilizando la misma tabla en una consulta anidada: smac89

Si no puedes hacer

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

porque es la misma mesa, puedes engañar y hacer:

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

[actualizar o eliminar o lo que sea]

Respondido 25 Abr '17, 12:04

La implementación más comprensible y lógica de todas las respuestas anteriores. Simple y al grano. - Clain Dsilva

Esto se ha convertido en parte de mi flujo de trabajo ahora. Atascarse en este error, diríjase a esta respuesta y rectifique la consulta. Gracias. - Vaibhav

Voy a usar este truco para todo ahora. Alucinado o_o - Justin

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
)

Respondido el 23 de diciembre de 11 a las 20:12

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

@AkshayArora, repase la parte bajo el encabezado 'Tal vez pueda unir la mesa consigo misma' en la respuesta de @ Cheekysoft. Él dijo UPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col - esto funcionaría como un alias diferente para la misma tabla que se usa aquí. De manera similar, en la respuesta de @ NexusRex, la primera SELECT La consulta actúa como una tabla derivada en la que story_category se utiliza por segunda vez. Entonces, el error mencionado en el OP no debería ocurrir aquí, ¿verdad? - istiaque ahmed

Esto es lo que hice para actualizar un valor de columna de Prioridad en 1 si es> = 1 en una tabla y en su cláusula WHERE usando una subconsulta en la misma tabla para asegurarme de que al menos una fila contiene Prioridad = 1 (porque esa era la condición que se debe verificar mientras se realiza 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.

Respondido el 02 de Septiembre de 10 a las 08:09

@anonymous_reviewer: En caso de dar [-1] o incluso [+1] al comentario de alguien, mencione también por qué lo ha dado. ¡¡¡Gracias!!! - sacrificar

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

@Chris Lo he verificado en MySQL y funciona bien para mí, así que le pido que lo verifique al final y luego afirme que es correcto o incorrecto. ¡¡¡Gracias!!! - sacrificar

En la parte inferior de esta página dice "Actualmente, no puede actualizar una tabla y seleccionar de la misma tabla en una subconsulta". - y he experimentado que esto es cierto en muchas ocasiones. dev.mysql.com/doc/refman/5.0/en/update.html - Chris

@Chris Lo sé, pero hay una solución para eso y es exactamente lo que he intentado mostrar con mi consulta 'ACTUALIZAR' y créanme que funciona bien. No creo que realmente haya intentado verificar mi consulta en absoluto. - sacrificar

La forma más sencilla de hacer esto es usar un alias de tabla cuando hace referencia a la tabla de consulta principal dentro de la subconsulta.

Ejemplo:

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

Cambiarlo a:

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

Respondido el 11 de enero de 18 a las 02:01

Para la consulta específica que el OP está tratando de lograr, la forma ideal y más eficiente de hacerlo es NO usar una subconsulta en absoluto.

Aquí están los LEFT JOIN versiones de las dos consultas del OP:

SELECT s.* 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

Nota: DELETE s restringe las operaciones de borrado al story_category mesa.
Documentación

DELETE s 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

Respondido 09 Abr '20, 20:04

Sorprendido, esto no tiene más votos a favor. También debe tenerse en cuenta que la sintaxis de varias tablas también funciona con UPDATE declaraciones y subconsultas unidas. Permitiéndote realizar LEFT JOIN ( SELECT ... ) en contraposición a WHERE IN( SELECT ... ), lo que hace que la implementación sea útil en muchos casos de uso. - Será.

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 quiso decir al hacerlo en dos pasos.

Respondido el 05 de Septiembre de 08 a las 11:09

Según la sintaxis de actualización de Mysql enlazado por @CheekySoft, dice justo en la parte inferior.

Actualmente, no puede actualizar una tabla y seleccionar de la misma tabla en una subconsulta.

Supongo que está eliminando de store_category mientras sigue seleccionando en la unión.

Respondido el 08 de enero de 15 a las 13:01

Si algo no funciona, al entrar por la puerta principal, tome la puerta trasera:

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. Cuanto mayores sean los datos, mejor.

respondido 29 mar '09, 03:03

Y acaba de perder todas sus claves externas, y tal vez también tenga algunas eliminaciones en cascada. - walf

Intente guardar el resultado de la instrucción Select en una variable separada y luego utilícelo para eliminar la consulta.

contestado el 31 de mayo de 16 a las 12:05

prueba este

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

Respondido el 23 de enero de 18 a las 01:01

¿Qué tal esta consulta? Espero que te ayude

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

contestado el 11 de mayo de 17 a las 02:05

El resultado muestra: '# 1064 - Tiene un error en su sintaxis SQL; consulte el manual que corresponde a la versión de su servidor MariaDB para conocer la sintaxis correcta para usar cerca de 'LEFT JOIN (SELECT categorías.id FROM categorías) cat ON story_category.id = cat.' en la línea 1 '- istiaque ahmed

Cuando utilice la eliminación de varias tablas, debe especificar las tablas afectadas. DELETE story_category FROM ... sin embargo, la subconsulta unida no es necesaria en este contexto y se puede realizar usando LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL, Tenga en cuenta que los criterios de combinación en la respuesta hacen referencia incorrectamente story_category.id = cat.id - Será.

Por lo que respecta, desea eliminar filas en story_category que no existen en category.

Aquí está su consulta original para identificar las filas a eliminar:

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

Combinando NOT IN con una subconsulta que JOINs la tabla original parece innecesariamente complicada. Esto se puede expresar de una manera más sencilla con not exists y una subconsulta correlacionada:

select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);

Ahora es fácil convertir esto en un delete declaración:

delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);    

Esta consulta se ejecutaría en cualquier versión de MySQL, así como en la mayoría de las otras bases de datos que conozco.

Demostración en DB Fiddle:

-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);

-- your original query to identify offending rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id | | ----------: | | 1 | | 2 | | 3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id | | ----------: | | 1 | | 2 | | 3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

-- outcome
select * from story_category;
| category_id | | ----------: | | 4 | | 5 |

Respondido 10 Abr '20, 11:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.