2011-08-19 21 views
8

Tengo la base de datos PostgreSQL 9 que usa números enteros que se incrementan automáticamente como claves principales. Quiero duplicar algunas de las filas de una tabla (según algunos criterios de filtro), al mismo tiempo que cambio uno o dos valores, es decir, copio todos los valores de columna, excepto la ID (que se genera automáticamente) y posiblemente otra columna.Duplicar de manera eficiente algunas filas en la tabla de PostgreSQL

Sin embargo, también quiero obtener el mapeo de identificaciones antiguas a nuevas. ¿Hay una mejor manera de hacerlo, simplemente consultar las filas para copiar primero y luego insertar nuevas filas de una en una?

Básicamente quiero hacer algo como esto:

INSERT INTO my_table (col1, col2, col3) 
SELECT col1, 'new col2 value', col3 
FROM my_table old 
WHERE old.some_criteria = 'something' 
RETURNING old.id, id; 

Sin embargo, esto no funciona con ERROR: missing FROM-clause entry for table "old" y puedo ver por qué: Postgres debe estar haciendo el primer SELECT y luego insertarlo y los RETURNING cláusulas sólo tiene acceso a la fila recién insertada.

+0

¿Por casualidad tiene idea de cómo hacer la consulta w/o nombrar todos los atributos Quiero copiar? Tengo una mesa bastante grande (con muchos atributos) y escribir todos ellos y no olvidar que uno es un dolor ... –

+0

¿por qué dos preguntas sobre lo mismo? http://stackoverflow.com/questions/29256888/insert-into-from-select-returning-id-mappings – murison

Respuesta

8

RETURN solo puede hacer referencia a las columnas en la fila final insertada. No puede hacer referencia a la ID "ANTIGUO" de esta manera, a menos que haya una columna en la tabla que contenga tanto esta como la nueva ID.

vuelva a emitir este que debe trabajar y mostrará todos los valores posibles que se pueden obtener a través de Regreso:

INSERT INTO my_table (col1, col2, col3) 
    SELECT col1, 'new col2 value', col3 
    FROM my_table AS old 
    WHERE old.some_criteria = 'something' 
RETURNING *; 

No te va a obtener el comportamiento que desea, sino que debe ilustrar mejor cómo está diseñado Volviendo trabajar.

+0

Gracias, pero creo que ya entiendo cómo funciona el RETORNO. – EMP

0

'anterior' es una palabra reservada, utilizada por el sistema de reescritura de reglas. [Supongo que este fragmento de consulta no es parte de una regla; en ese caso, habría formulado la pregunta de manera diferente]

+0

Sí, tienes razón: hice la consulta para la publicación porque la real es más compleja. – EMP

2

¡Bueno! Puedo probar este código, pero este cambio (FROM my_table AS old) en (FROM my_table) y esto (WHERE old.some_criteria = 'something') en (WHERE some_criteria = 'something')

Este es el código final que yo uso

INSERT INTO my_table (col1, col2, col3) 
    SELECT col1, 'new col2 value', col3 
    FROM my_table AS old 
    WHERE some_criteria = 'something' 
RETURNING *; 

Gracias!

0
DROP TABLE IF EXISTS tmptable; 
CREATE TEMPORARY TABLE tmptable as SELECT * FROM products WHERE id = 100; 
UPDATE tmptable SET id = sbq.id from (select max(id)+1 as id from products) as sbq; 
INSERT INTO products (SELECT * FROM tmptable); 
DROP TABLE IF EXISTS tmptable; 

añadir otra actualización antes de la inserción de modificar otro campo

UPDATE tmptable SET another = 'data'; 
+0

'UPDATE tmptable SET id =' solo funciona si id es un entero, no si es serial – DarkMukke

2

Esto se puede hacer con la ayuda de datos CTE-modifiying (Postgres 9.1+):

WITH sel AS (
    SELECT id, col1, col3 
     , row_number() OVER (ORDER BY id) AS rn -- order any way you like 
    FROM my_table 
    WHERE some_criteria = 'something' 
    ORDER BY id -- match order or row_number() 
    ) 
, ins AS (
    INSERT INTO my_table (col1, col2, col3) 
    SELECT col1, 'new col2 value', col3 
    FROM sel 
    ORDER BY id -- redundant to be sure 
    RETURNING id 
) 
SELECT s.id AS old_id, i.id AS new_id 
FROM (SELECT id, row_number() OVER (ORDER BY id) AS rn FROM ins) i 
JOIN sel s USING (rn); 

SQL Fiddle demostración.

Esto se basa en el detalle de la implementación no documentada de que las filas de SELECT se insertan en el orden proporcionado (y se devuelven en el orden proporcionado). Funciona en todas las versiones actuales de Postgres y no se va a romper.Relacionado:

funciones de ventana no están permitidos en la cláusula RETURNING, por lo que se aplican row_number() en otra subconsulta.

Más explicación en esta respuesta más adelante relacionadas:

Cuestiones relacionadas