Copia una tabella (inclusi gli indici) in postgres


85

Ho un tavolo postgres. Devo eliminare alcuni dati da esso. Stavo per creare una tabella temporanea, copiare i dati, ricreare gli indici e cancellare le righe di cui ho bisogno. Non riesco a eliminare i dati dalla tabella originale, perché questa tabella originale è l'origine dei dati. In un caso ho bisogno di ottenere alcuni risultati che dipendono dall'eliminazione di X, in un altro caso, dovrò eliminare Y. Quindi ho bisogno che tutti i dati originali siano sempre disponibili e disponibili.

Tuttavia sembra un po 'sciocco ricreare la tabella e copiarla di nuovo e ricreare gli indici. C'è comunque in postgres per dirgli "Voglio una copia separata completa di questa tabella, inclusi struttura, dati e indici"?

Sfortunatamente PostgreSQL non ha un "CREATE TABLE .. COME X INCLUDENDO INDICI"

Risposte:


108

Il nuovo PostgreSQL (dalla 8.3 secondo la documentazione) può usare "INCLUSI INDICI":

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

Come puoi vedere sto testando su 8.3.

Ora creiamo una tabella:

# 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

E guarda come appare:

# \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)

Ora possiamo copiare la struttura:

# 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

E controlla la struttura:

# \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)

Se stai usando PostgreSQL precedente alla 8.3, puoi semplicemente usare pg_dump con l'opzione "-t" per specificare 1 tabella, cambiare il nome della tabella in dump e caricarla di nuovo:

=> 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

E ora il tavolo è:

# \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)

14
In questo modo la sequenza di chiavi primarie (x1_id_seq) sarà condivisa tra le due tabelle!
Jauzsika

2
Ops, con pg9.X, la sequenza di chiavi primarie verrà condivisa quando si utilizza "INCLUSI VINCOLI" (non "INCLUSI INDICI").
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]  

Ecco un esempio

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

L'altro modo per creare una nuova tabella dalla prima è usare

    CREATE TABLE films_recent (LIKE films INCLUDING INDEXES);  

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

Nota che Postgresql ha una patch per risolvere i problemi di tablespace se viene utilizzato il secondo metodo


Non ci sono "INDICI INCLUSI" in postgres.
Rory

2
Quale versione stai usando? Leggi l'ultimo documento, è lì
WolfmanDragon

6
con pg9.X, quando si usa "INCLUSI VINCOLI" (non "INCLUSI INDICI") la sequenza di tasti primari sarà condivisa tra le due tabelle (!).
Peter Krauss

Sembra che potrebbe essere necessario essere al CREATE TABLE my_table (LIKE...)posto di CREATE TABLE my_table LIKE...per funzionare. Risposta modificata.
Jason Swett

@PeterKrauss hai capito la cosa della sequenza di chiavi primarie condivisa? Sto cercando di COPIARE un mucchio di dati in una nuova tabella. Non posso eliminare la vecchia tabella e rinominare quella nuova perché il pk di quella nuova punta a quella vecchia.
yellottyellott

5

Ci sono molte risposte sul web, una di queste può essere trovata qui .

Ho finito per fare qualcosa del genere:

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

Ciò copierà lo schema ei dati inclusi gli indici, ma non includerà trigger e vincoli. Si noti che gli indici sono condivisi con la tabella originale, quindi quando si aggiunge una nuova riga a una delle tabelle il contatore aumenterà.


4

Ho un tavolo postgres. Devo eliminare alcuni dati da esso.

Presumo che ...

delete from yourtable
where <condition(s)>

... non funzionerà per qualche motivo. (Ti interessa condividere questo motivo?)

Stavo per creare una tabella temporanea, copiare i dati, ricreare gli indici e cancellare le righe di cui ho bisogno.

Guarda in pg_dump e pg_restore. Usare pg_dump con alcune opzioni intelligenti e forse modificare l'output prima di pg_restoring potrebbe fare il trucco.


Dato che stai facendo un'analisi del tipo "what if" sui dati, mi chiedo se faresti meglio a usare le visualizzazioni.

È possibile definire una visualizzazione per ogni scenario che si desidera testare in base alla negazione di ciò che si desidera escludere. Cioè, definisci una vista in base a ciò che vuoi INCLUDERE. Ad esempio, se vuoi una "finestra" sui dati in cui hai "cancellato" le righe dove X = Y, allora creeresti una vista come righe dove (X! = Y).

Le viste vengono memorizzate nel database (nel Catalogo di sistema) come query di definizione. Ogni volta che si interroga la vista, il server del database cerca la query sottostante che la definisce e la esegue (con AND con qualsiasi altra condizione utilizzata). Ci sono diversi vantaggi in questo approccio:

  1. Non duplichi mai nessuna parte dei tuoi dati.
  2. Gli indici già in uso per la tabella di base (la tabella originale "reale") verranno utilizzati (come ritenuto opportuno da Query Optimizer) quando si interroga ogni vista / scenario. Non è necessario ridefinirli o copiarli.
  3. Poiché una vista è una "finestra" (NON uno shapshot) sui dati "reali" nella tabella di base, puoi aggiungere / aggiornare / eliminare sulla tabella di base e semplicemente ri-interrogare gli scenari di visualizzazione senza bisogno di ricreare nulla come i dati cambiano nel tempo.

Ovviamente c'è un compromesso. Poiché una vista è una tabella virtuale e non una tabella "reale" (di base), in realtà stai eseguendo una query (forse complessa) ogni volta che vi accedi. Questo potrebbe rallentare un po 'le cose. Ma potrebbe non esserlo. Dipende da molti problemi (dimensione e natura dei dati, qualità delle statistiche nel catalogo di sistema, velocità dell'hardware, carico di utilizzo e molto altro). Non lo saprai finché non lo proverai. Se (e solo se) trovi effettivamente che le prestazioni sono inaccettabilmente lente, potresti prendere in considerazione altre opzioni. (Viste materializzate, copie di tabelle, ... tutto ciò che scambia spazio per tempo.)


Ho aggiornato la domanda per spiegare perché non posso semplicemente cancellare dalla tabella originale
Rory

1

Crea una nuova tabella utilizzando una selezione per acquisire i dati desiderati. Quindi scambia il vecchio tavolo con quello nuovo.

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

0

Un modo semplice è includere tutto:

CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.