2011-05-27 9 views
5

Tengo problemas para ejecutar correctamente una consulta INSERT, y parece que no puedo encontrar nada en Google o Stack Overflow que resuelva este problema en particular.MySQL INSERT Usar la subconsulta con COUNT() en la misma tabla

Estoy tratando de crear una tabla simple para las entradas destacadas, donde el entry_id se guarda en la tabla junto con su orden actual.

Mi salida deseada es la siguiente:

Si la tabla featured actualmente con estos tres entradas:

featured_id entry_id featured_order 
1    27   0 
2    54   1 
4    23   2 

Quiero la siguiente entrada para guardar con featured_order = 3.

Estoy tratando de obtener la siguiente consulta para trabajar sin suerte:

INSERT INTO `featured` 
(
    `entry_id`, `featured_order` 
) 
VALUES 
(
    200, 
    (SELECT COUNT(*) AS `the_count` FROM `featured`) 
) 

El error que estoy recibiendo es: You can't specify target table 'featured' for update in FROM clause.

¿Alguien puede ayudar con una solución que obtiene el conteo sin causar un error?

¡Gracias de antemano!

+0

posible duplicado http://stackoverflow.com/questions/45494/sql-delete- cant-especifica-target-table-for-update-in-from-clause – Tim

+2

No es un duplicado: esa pregunta se eliminó; esto es para inserción. No aplica en absoluto aquí. – Bohemian

Respuesta

13

Aquí es una cosa genial: MySQL de INSERT . . . SELECT:

INSERT INTO `featured` 
(
    `entry_id`, `featured_order` 
) 
SELECT 200, COUNT(*) + 1 
FROM `featured` 

No se requiere subconsulta.


@Bohemian tiene un buen punto:

Better to use max(featured_order) + 1 if you use this approach

Así que una consulta mejor probablemente sería:

INSERT INTO `featured` 
(
    `entry_id`, `featured_order` 
) 
SELECT 200, MAX(`featured_order`) + 1 
FROM `featured` 

Su método de disparo describe en su respuesta es también una buena manera de lograr Lo que quieras.


El problema potencial con la consulta 1 es si alguna vez eliminar una fila de la fila será echado fuera, y que tendrá un duplicado en featured_order. Con la segunda consulta, esto no es un problema, pero tendrá lagunas, como si estuviera usando una columna de incremento automático.

Si es absolutamente necesario tener un orden y sin espacios la mejor solución que conozco es ejecutar esta serie de consultas:

SET @pos:=0; 

DROP TABLE IF EXISTS temp1; 

CREATE TEMPORARY TABLE temp1 LIKE featured; 

ALTER TABLE featured ORDER BY featured_order ASC; 

INSERT INTO temp1 (featured_id, entry_id, featured_order) 
SELECT featured_id, entry_id, @pos:[email protected]+1 FROM words; 

UPDATE featured 
JOIN temp1 ON featured.featured_id = temp1.featured_id 
SET featured.rank = temp1.rank; 

DROP TABLE temp1; 

Cada vez que se elimina una fila

+0

No creo que esto funcione. De los documentos para ['INSERT ... SELECT'] (http://dev.mysql.com/doc/refman/5.5/en/insert-select.html):" Sin embargo, no puede insertar en una tabla y seleccionar de la misma tabla en una subconsulta ". –

+1

Problemas con esto: a) querrá agregar uno para contar (*), b) qué sucede si la numeración no se alinea con el recuento (*), por ejemplo, si las filas se eliminan. Es mejor usar max (featured_order) + 1 si usa este enfoque – Bohemian

+0

@Ted - Probado en un db local - funcionó bien allí. – jisaacstone

-1

De los MySQL manual respecto subconsultas:

Another restriction is that currently you cannot modify a table and select from the same table in a subquery.

Quizás un alias o una unión (de otro modo inútil) en la subconsulta ayudaría aquí.

EDITAR: Resulta que hay una solución alternativa. La solución temporal se describe http://www.xaprb.com/blog/2006/06/23/how-to-select-from-an-update-target-in-mysql/.

+0

Eso 'funciona' implica una declaración de 'actualización' posterior, apenas "elegante". – Bohemian

+0

Espero no haber implicado "elegante" :) –

2

Utilice un disparador:

drop trigger if exists featured_insert_trigger; 

delimiter // 
create trigger featured_insert_trigger before insert on featured 
for each row 
begin 
    set new.featured_order = ifnull((select max(featured_order) from featured), -1) + 1; 
end; // 
delimiter ; 

Ahora su inserta el siguiente aspecto:

insert into featured (entry_id) values (200); 

featured_order se establecerá en el valor más alto featured_order más uno. Esto permite que las filas se eliminen/actualicen y siempre garantiza la exclusividad.

El ifnull está allí en caso de que no haya filas en la tabla, en cuyo caso el primer valor será cero.

Este código ha sido probado porque funciona correctamente.

+0

Investigué un poco sobre los desencadenantes (nunca había oído hablar de ellos), y definitivamente esta es la mejor solución. Sin embargo, cuando lo probé, me sale este error: 'comando TRIGGER negó al usuario 'xxxx' @ 'xxxx' para la tabla 'featured'' que he dado al usuario MySQL 'todos los privilegios', según HostGator, por lo que parece que un desencadenador no está en las tarjetas para este proyecto. :/ – jlengstorf

+0

¿Funciona esto si hay dos insertos exactamente al mismo tiempo? – xms

+0

@xms, nunca hay actualizaciones/insertos que ocurran exactamente "al mismo tiempo". La base de datos serializa todas las consultas de mutación. Cada uno siempre comienza y completa atómicamente. – Bohemian

1

Tienes que Simpley alias de uso que va a resolver el problema:

INSERT INTO `featured` 
(
    `entry_id`, `featured_order` 
) 
VALUES 
(
    200, 
    (SELECT COUNT(*) AS `the_count` FROM `featured` as f1) 
) 
+0

Sería mejor si se proporciona una explicación a lo largo de un voto que sea útil –

+0

¿Fue por una ortografía incorrecta o una respuesta incorrecta? –

2
INSERT INTO `featured` 
(
    `entry_id`, `featured_order` 
) 
VALUES 
(
    200, 
    (SELECT COUNT(*) AS `the_count` FROM `featured` F1) 
) 

corrección con sólo añadir "F1" alias de tabla. (Según lo sugerido por Angelin Nadar justo por encima)

Esta debe ser la mejor solución en un entorno de múltiples db ya misma sintaxis funciona bien en MySQL + Oracle + DB2


también sugieren una mejora sobre: ​​

  • SELECT COUNT (*) 1 (Problema: si una fila se elimina obtiene el error)

  • SELECT MAX (featured_order) 1 (Problema: primera fila error)

SELECT (COALESCE (MAX (featured_order), 0) 1) (no hay problema)

+0

Me preguntaba ... ¿Funcionará bien si hay dos insertos exactamente al mismo tiempo? – xms

Cuestiones relacionadas