Copiar una tabla (incluidos los índices) en postgres

85

Tengo una mesa de postgres. Necesito eliminar algunos datos. Iba a crear una tabla temporal, copiar los datos, recrear los índices y eliminar las filas que necesito. No puedo eliminar datos de la tabla original, porque esta tabla original es la fuente de datos. En un caso, necesito obtener algunos resultados que dependen de eliminar X, en otro caso, necesitaré eliminar Y. Por lo tanto, necesito que todos los datos originales estén siempre disponibles.

Sin embargo, parece un poco tonto volver a crear la tabla y copiarla de nuevo y volver a crear los índices. ¿Hay alguna forma en Postgres para decirle "Quiero una copia separada completa de esta tabla, incluida la estructura, los datos y los índices"?

Desafortunadamente, PostgreSQL no tiene un "CREAR TABLA ... COMO X INCLUYENDO ÍNDICES"

Rory
fuente

Respuestas:

108

El nuevo PostgreSQL (desde 8.3 según los documentos) puede usar "INCLUYENDO ÍNDICES":

# select version();
                                             version
-------------------------------------------------------------------------------------------------
 PostgreSQL 8.3.7 on x86_64-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.4 (Ubuntu 4.2.4-1ubuntu3)
(1 row)

Como puede ver, estoy probando en 8.3.

Ahora, creemos la tabla:

# create table x1 (id serial primary key, x text unique);
NOTICE:  CREATE TABLE will create implicit sequence "x1_id_seq" for serial column "x1.id"
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "x1_pkey" for table "x1"
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "x1_x_key" for table "x1"
CREATE TABLE

Y mira cómo se ve:

# \d x1
                         Table "public.x1"
 Column |  Type   |                    Modifiers
--------+---------+-------------------------------------------------
 id     | integer | not null default nextval('x1_id_seq'::regclass)
 x      | text    |
Indexes:
    "x1_pkey" PRIMARY KEY, btree (id)
    "x1_x_key" UNIQUE, btree (x)

Ahora podemos copiar la estructura:

# create table x2 ( like x1 INCLUDING DEFAULTS INCLUDING CONSTRAINTS INCLUDING INDEXES );
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "x2_pkey" for table "x2"
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "x2_x_key" for table "x2"
CREATE TABLE

Y revisa la estructura:

# \d x2
                         Table "public.x2"
 Column |  Type   |                    Modifiers
--------+---------+-------------------------------------------------
 id     | integer | not null default nextval('x1_id_seq'::regclass)
 x      | text    |
Indexes:
    "x2_pkey" PRIMARY KEY, btree (id)
    "x2_x_key" UNIQUE, btree (x)

Si está usando PostgreSQL pre-8.3, simplemente puede usar pg_dump con la opción "-t" para especificar 1 tabla, cambiar el nombre de la tabla en el volcado y volver a cargarla:

=> pg_dump -t x2 | sed 's/x2/x3/g' | psql
SET
SET
SET
SET
SET
SET
SET
SET
CREATE TABLE
ALTER TABLE
ALTER TABLE
ALTER TABLE

Y ahora la mesa es:

# \d x3
                         Table "public.x3"
 Column |  Type   |                    Modifiers
--------+---------+-------------------------------------------------
 id     | integer | not null default nextval('x1_id_seq'::regclass)
 x      | text    |
Indexes:
    "x3_pkey" PRIMARY KEY, btree (id)
    "x3_x_key" UNIQUE, btree (x)

fuente
14
¡De esta forma, la secuencia de clave primaria (x1_id_seq) se compartirá entre las dos tablas!
Jauzsika
2
Ops, con pg9.X, la secuencia de la clave principal se compartirá al usar "INCLUYENDO RESTRICCIONES" (no "INCLUYENDO ÍNDICES").
Peter Krauss
44
[CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } ] TABLE table_name
    [ (column_name [, ...] ) ]
    [ WITH ( storage_parameter [= value] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
    [ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
    [ TABLESPACE tablespace ]
    AS query][1]  

Aquí hay un ejemplo

CREATE TABLE films_recent AS
  SELECT * FROM films WHERE date_prod >= '2002-01-01';

La otra forma de crear una nueva tabla a partir de la primera es usar

    CREATE TABLE films_recent (LIKE films INCLUDING INDEXES);  

    INSERT INTO films_recent
         SELECT *
           FROM books
          WHERE date_prod >= '2002-01-01';  

Tenga en cuenta que Postgresql tiene un parche para solucionar problemas de espacio de tabla si se usa el segundo método

Hombre LoboDragón
fuente
No hay "ÍNDICES INCLUIDOS" en postgres.
Rory
2
¿Qué versión estás usando? Lea el último documento, está ahí
WolfmanDragon
6
con pg9.X, cuando se utiliza "INCLUYENDO RESTRICCIONES" (no "INCLUYENDO ÍNDICES"), la secuencia de clave primaria se compartirá entre las dos tablas (!).
Peter Krauss
Parece que debe ser en CREATE TABLE my_table (LIKE...)lugar de CREATE TABLE my_table LIKE...para que funcione. Respuesta editada.
Jason Swett
@PeterKrauss, ¿descubriste la secuencia de la clave principal compartida? Estoy tratando de COPIAR un montón de datos en una nueva tabla. No puedo quitar la tabla anterior y cambiar el nombre de la nueva porque el pk de la nueva apunta a la anterior.
yellottyellott
4

Tengo una mesa de postgres. Necesito eliminar algunos datos.

Presumo que ...

delete from yourtable
where <condition(s)>

... no funcionará por alguna razón. (¿Te importaría compartir esa razón?)

Iba a crear una tabla temporal, copiar los datos, recrear los índices y eliminar las filas que necesito.

Busque en pg_dump y pg_restore. Usar pg_dump con algunas opciones inteligentes y tal vez editar la salida antes de pg_restoring podría ser suficiente.


Ya que está haciendo un análisis de tipo "qué pasaría si" en los datos, me pregunto si sería mejor utilizar vistas.

Puede definir una vista para cada escenario que desee probar en función de la negación de lo que desea excluir. Es decir, defina una vista basada en lo que desea INCLUIR. Por ejemplo, si desea una "ventana" en los datos donde "eliminó" las filas donde X = Y, entonces crearía una vista como filas donde (X! = Y).

Las vistas se almacenan en la base de datos (en el catálogo del sistema) como su consulta de definición. Cada vez que consulta la vista, el servidor de la base de datos busca la consulta subyacente que la define y la ejecuta (junto con cualquier otra condición que haya utilizado). Hay varios beneficios de este enfoque:

  1. Nunca duplicas ninguna parte de tus datos.
  2. Los índices que ya están en uso para la tabla base (su tabla "real" original) se usarán (según lo considere apropiado el optimizador de consultas) cuando consulte cada vista / escenario. No es necesario redefinirlos ni copiarlos.
  3. Dado que una vista es una "ventana" (NO una instantánea) en los datos "reales" en la tabla base, puede agregar / actualizar / eliminar en su tabla base y simplemente volver a consultar los escenarios de vista sin necesidad de volver a crear nada como los datos cambian con el tiempo.

Hay una compensación, por supuesto. Dado que una vista es una tabla virtual y no una tabla "real" (base), en realidad está ejecutando una consulta (quizás compleja) cada vez que accede a ella. Esto puede ralentizar un poco las cosas. Pero puede que no. Depende de muchos problemas (tamaño y naturaleza de los datos, calidad de las estadísticas en el catálogo del sistema, velocidad del hardware, carga de uso y mucho más). No lo sabrá hasta que lo pruebe. Si (y solo si) realmente encuentra que el rendimiento es inaceptablemente lento, entonces podría buscar otras opciones. (Vistas materializadas, copias de tablas, ... cualquier cosa que intercambie espacio por tiempo).

Alan
fuente
Actualicé la pregunta para explicar por qué no puedo simplemente eliminar de la tabla original
Rory
4

Hay muchas respuestas en la web, una de ellas se puede encontrar aquí .

Terminé haciendo algo como esto:

create table NEW ( like ORIGINAL including all);
insert into NEW select * from ORIGINAL

Esto copiará el esquema y los datos, incluidos los índices, pero sin incluir los desencadenantes ni las restricciones. Tenga en cuenta que los índices se comparten con la tabla original, por lo que al agregar una nueva fila a cualquiera de las tablas, el contador aumentará.

oshai
fuente
1

Cree una nueva tabla con una selección para obtener los datos que desee. Luego, cambie la mesa vieja por la nueva.

create table mynewone as select * from myoldone where ...
mess (re-create) with indexes after the table swap.
gsamaras
fuente
0

Una forma sencilla es incluir todo:

CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
Ringtail
fuente