La pregunta es antigua, pero recibimos una nueva pregunta de un usuario desesperado en dba.SE después de intentar aplicar lo que se sugiere aquí. Encuentre una respuesta con más detalles y explicaciones allí :
La respuesta actualmente aceptada fallará en la mayoría de los casos .
-
Por lo general, tiene una
PRIMARY KEY
oUNIQUE
restricción en unid
columna, que esNOT DEFERRABLE
por defecto. (OP mencionareferences and constraints
.) Dichas restricciones se verifican después de cada fila, por lo que lo más probable es que obtenga una infracción única errores al intentar. Detalles: -
Por lo general, se desea conservar el orden de filas original. mientras se cierran brechas. Pero el orden en que se actualizan las filas es arbitrario , lo que lleva a números arbitrarios. El ejemplo demostrado parece conservar la secuencia original porque el almacenamiento físico aún coincide con el orden deseado (las filas insertadas en el orden deseado un momento antes), lo que casi nunca es el caso en las aplicaciones del mundo real y es completamente poco confiable.
El asunto es más complicado de lo que podría parecer en un principio. Uno solución (entre otras) si puede eliminar la restricción PK/UNIQUE (y las restricciones FK relacionadas) temporalmente:
BEGIN;
LOCK tbl;
-- remove all FK constraints to the column
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
-- for the simple case without FK references - or see below:
UPDATE tbl t -- intermediate unique violations are ignored now
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
-- Update referencing value in FK columns at the same time (if any)
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
-- add all FK constraints to the column back
COMMIT;
Esto también es mucho más rápido para tablas grandes, porque verificar las restricciones PK (y FK) para cada fila cuesta mucho más que eliminar las restricciones y volver a agregarlas.
Si hay columnas FK en otras tablas que hacen referencia a tbl.id
, use CTE de modificación de datos para actualizarlos todos.
Ejemplo para una tabla fk_tbl
y una columna FK fk_id
:
WITH u1 AS (
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id
RETURNING t.id, t1.new_id -- return old and new ID
)
UPDATE fk_tbl f
SET fk_id = u1.new_id -- set to new ID
FROM u1
WHERE f.fk_id = u1.id; -- match on old ID
Más en la respuesta referenciada en dba.SE .