Kopieren Sie eine Tabelle (einschließlich Indizes) in Postgres

84

Ich habe einen Postgres-Tisch. Ich muss einige Daten daraus löschen. Ich wollte eine temporäre Tabelle erstellen, die Daten kopieren, die Indizes neu erstellen und die benötigten Zeilen löschen. Ich kann keine Daten aus der Originaltabelle löschen, da diese Originaltabelle die Datenquelle ist. In einem Fall muss ich einige Ergebnisse erhalten, die vom Löschen von X abhängen. In einem anderen Fall muss ich Y löschen. Daher müssen alle Originaldaten immer verfügbar sein.

Es scheint jedoch ein bisschen albern, die Tabelle neu zu erstellen und erneut zu kopieren und die Indizes neu zu erstellen. Gibt es in postgres überhaupt eine Meldung "Ich möchte eine vollständige separate Kopie dieser Tabelle, einschließlich Struktur, Daten und Indizes"?

Leider hat PostgreSQL keine "CREATE TABLE .. LIKE X INCLUDING INDEXES"

Rory
quelle

Antworten:

107

Neues PostgreSQL (seit 8.3 laut Dokumentation) kann "INCLUDING INDEXES" verwenden:

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

Wie Sie sehen, teste ich auf 8.3.

Jetzt erstellen wir eine Tabelle:

# 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

Und sehen Sie, wie es aussieht:

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

Jetzt können wir die Struktur kopieren:

# 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

Und überprüfen Sie die Struktur:

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

Wenn Sie PostgreSQL vor 8.3 verwenden, können Sie einfach pg_dump mit der Option "-t" verwenden, um 1 Tabelle anzugeben, den Tabellennamen in dump zu ändern und erneut zu laden:

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

Und jetzt ist der Tisch:

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

quelle
14
Auf diese Weise wird die Primärschlüsselfolge (x1_id_seq) zwischen den beiden Tabellen geteilt!
Jauzsika
2
Bei pg9.X wird die Primärschlüsselfolge bei Verwendung von "INCLUDING CONSTRAINTS" (nicht "INCLUING INDEXES") gemeinsam genutzt.
Peter Krauss
43
[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]  

Hier ist ein Beispiel

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

Die andere Möglichkeit, eine neue Tabelle aus der ersten zu erstellen, ist die Verwendung

    CREATE TABLE films_recent (LIKE films INCLUDING INDEXES);  

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

Beachten Sie, dass Postgresql einen Patch zur Behebung von Tabellenbereichsproblemen hat, wenn die zweite Methode verwendet wird

WolfmanDragon
quelle
Es gibt keine "INKLUSIVE INDEXE" in Postgres.
Rory
2
Welche Version benutzt du? Lesen Sie das neueste Dokument, es ist da
WolfmanDragon
6
Bei pg9.X wird bei Verwendung von "INCLUDING CONSTRAINTS" (nicht "INCLUING INDEXES") die Primärschlüsselfolge zwischen den beiden Tabellen (!) geteilt.
Peter Krauss
Es sieht so aus, als müsste es CREATE TABLE my_table (LIKE...)statt sein, CREATE TABLE my_table LIKE...um zu arbeiten. Antwort bearbeitet.
Jason Swett
@PeterKrauss hast du die gemeinsame Sache mit der Primärschlüsselsequenz herausgefunden? Ich versuche, eine Reihe von Daten in eine neue Tabelle zu kopieren. Ich kann die alte Tabelle nicht löschen und die neue umbenennen, da der pk der neuen auf die alte zeigt.
Yellottyellott
4

Ich habe einen Postgres-Tisch. Ich muss einige Daten daraus löschen.

Ich nehme an, dass ...

delete from yourtable
where <condition(s)>

... wird aus irgendeinem Grund nicht funktionieren. (Möchtest du diesen Grund teilen?)

Ich wollte eine temporäre Tabelle erstellen, die Daten kopieren, die Indizes neu erstellen und die benötigten Zeilen löschen.

Schauen Sie in pg_dump und pg_restore. Die Verwendung von pg_dump mit einigen cleveren Optionen und möglicherweise das Bearbeiten der Ausgabe vor pg_restoring könnte den Trick tun.


Da Sie die Daten vom Typ "Was wäre wenn" analysieren, frage ich mich, ob Sie möglicherweise besser dran sind, Ansichten zu verwenden.

Sie können für jedes zu testende Szenario eine Ansicht definieren, die auf der Negation dessen basiert, was Sie ausschließen möchten. Definieren Sie eine Ansicht basierend auf dem, was Sie einschließen möchten. Wenn Sie beispielsweise ein "Fenster" für die Daten wünschen, in dem Sie die Zeilen mit X = Y "gelöscht" haben, erstellen Sie eine Ansicht als Zeilen mit (X! = Y).

Ansichten werden in der Datenbank (im Systemkatalog) als definierende Abfrage gespeichert. Jedes Mal, wenn Sie die Ansicht abfragen, sucht der Datenbankserver nach der zugrunde liegenden Abfrage, die sie definiert, und führt diese aus (UND-verknüpft mit allen anderen von Ihnen verwendeten Bedingungen). Dieser Ansatz bietet mehrere Vorteile:

  1. Sie duplizieren niemals einen Teil Ihrer Daten.
  2. Die Indizes, die bereits für die Basistabelle verwendet werden (Ihre ursprüngliche, "echte" Tabelle), werden verwendet (wie vom Abfrageoptimierer als passend erachtet), wenn Sie jede Ansicht / jedes Szenario abfragen. Sie müssen nicht neu definiert oder kopiert werden.
  3. Da eine Ansicht ein "Fenster" (KEIN Shapshot) für die "echten" Daten in der Basistabelle ist, können Sie Ihre Basistabelle hinzufügen / aktualisieren / löschen und die Ansichtsszenarien einfach erneut abfragen, ohne etwas neu erstellen zu müssen Die Daten ändern sich im Laufe der Zeit.

Es gibt natürlich einen Kompromiss. Da eine Ansicht eine virtuelle Tabelle und keine "echte" (Basis-) Tabelle ist, führen Sie bei jedem Zugriff eine (möglicherweise komplexe) Abfrage aus. Dies kann die Dinge etwas verlangsamen. Aber es kann nicht. Dies hängt von vielen Aspekten ab (Größe und Art der Daten, Qualität der Statistiken im Systemkatalog, Geschwindigkeit der Hardware, Nutzungslast und vieles mehr). Sie werden es nicht wissen, bis Sie es versuchen. Wenn Sie (und nur dann) tatsächlich feststellen, dass die Leistung inakzeptabel langsam ist, sollten Sie sich andere Optionen ansehen. (Materialisierte Ansichten, Kopien von Tabellen, ... alles, was Raum gegen Zeit tauscht.)

Alan
quelle
Ich habe die Frage aktualisiert, um zu erklären, warum ich nicht einfach aus der Originaltabelle löschen kann
Rory
3

Es gibt viele Antworten im Internet, eine davon finden Sie hier .

Am Ende habe ich so etwas gemacht:

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

Dadurch werden das Schema und die Daten einschließlich Indizes kopiert, jedoch ohne Trigger und Einschränkungen. Beachten Sie, dass Indizes für die ursprüngliche Tabelle freigegeben sind. Wenn Sie also eine neue Zeile zu einer der beiden Tabellen hinzufügen, wird der Zähler erhöht.

oshai
quelle
1

Erstellen Sie eine neue Tabelle mit einer Auswahl, um die gewünschten Daten abzurufen. Tauschen Sie dann den alten Tisch gegen den neuen aus.

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

Es gibt viele Antworten im Internet, eine davon finden Sie hier .

Am Ende habe ich so etwas gemacht:

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

Dadurch werden das Schema und die Daten einschließlich Indizes kopiert, jedoch ohne Trigger und Einschränkungen. Beachten Sie, dass Indizes für die ursprüngliche Tabelle freigegeben sind. Wenn Sie also eine neue Zeile zu einer der beiden Tabellen hinzufügen, wird der Zähler erhöht.

Siehe auch die Antwort hier .

oshai
quelle
0

Ein einfacher Weg ist, alle einzuschließen:

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