PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Hinzufügen eines mehrspaltigen Primärschlüssels zu einer Tabelle mit 40 Millionen Datensätzen

Verwenden Sie eine serielle Spalte

Ihr Plan ist es, einen unnötig großen Index für 40 Millionen (!) Zeilen hinzuzufügen. Und Sie sind nicht einmal sicher, dass es einzigartig sein wird. Von dieser Vorgehensweise würde ich dringend abraten. Fügen Sie eine Seriennummer Spalte stattdessen und fertig:

ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;

Das ist alles, was Sie tun müssen. Der Rest passiert automatisch. Mehr im Handbuch oder in diesen eng verwandten Antworten:
Die automatische Inkrementierung des PostgreSQL-Primärschlüssels stürzt in C++ ab
SQL-Funktion automatisch inkrementieren

Hinzufügen einer Seriennummer Spalte ist ein einmaliger Vorgang, aber teuer. Die gesamte Tabelle muss neu geschrieben werden, wodurch Aktualisierungen für die Dauer des Vorgangs blockiert werden. Am besten ohne gleichzeitige Last außerhalb der Geschäftszeiten. Ich zitiere das Handbuch hier :

Da dies effektiv die gesamte Tabelle neu schreibt, können Sie genauso gut eine neue Tabelle mit einer seriellen pk-Spalte erstellen, alle Zeilen aus der alten Tabelle einfügen, die serielle Tabelle mit Standardwerten aus ihrer Sequenz füllen lassen, die alte löschen und die neue umbenennen. Mehr in diesen eng verwandten Antworten:
Aktualisieren von Datenbankzeilen ohne Sperren der Tabelle in PostgreSQL 9.2
Neue Spalte ohne Tabelle hinzufügen sperren?

Stellen Sie sicher, dass alle Ihre INSERT-Anweisungen eine Zielliste haben, dann kann eine zusätzliche Spalte sie nicht verwirren:

INSERT INTO tbl (col1, col2, ...) VALUES ...

Nicht:

INSERT INTO tbl VALUES ...

Eine Seriennummer wird mit einem integer implementiert Spalte (4 Bytes).
Eine Primärschlüsselbeschränkung wird mit einem eindeutigen Index und einem NOT NULL implementiert Beschränkung auf die beteiligten Spalten.
Der Inhalt eines Indexes wird ähnlich wie Tabellen gespeichert. Zusätzlicher physischer Speicher wird separat benötigt. Mehr über physischen Speicher in dieser verwandten Antwort:
Berechnen und Platz sparen in PostgreSQL

Ihr Index würde 2 Zeitstempel (2 x 8 Bytes) plus einen langen Dateinamen inkl. Pfad (~ 50 Bytes?) Das würde den Index um 2,5 GB größer machen (40M x 60 .. etwas Bytes) und alle Operationen langsamer machen.

Umgang mit Duplikaten

Wie mit dem „Importieren von Duplikaten“ umgegangen wird, hängt davon ab, wie Sie Daten importieren und wie „Duplikat“ genau definiert ist.

Wenn wir über COPY sprechen Anweisungen wäre eine Möglichkeit, eine temporäre Staging-Tabelle zu verwenden und Duplikate mit einem einfachen SELECT DISTINCT zu reduzieren oder DISTINCT ON im INSERT Befehl:

CREATE TEMP TABLE tbl_tmp AS
SELECT * FROM tbl LIMIT 0;     -- copy structure without data and constraints

COPY tbl_tmp FROM '/path/to/file.csv';

INSERT INTO tbl (col1, col2, col3)
SELECT DISTINCT ON (col1, col2)
       col1, col2, col3 FROM tbl_tmp;

Oder, um auch Duplikate mit bereits existierenden Zeilen zu verbieten:

INSERT INTO tbl (col1, col2, col3)
SELECT i.*
FROM  (
   SELECT DISTINCT ON (col1, col2)
          col1, col2, col3
   FROM   tbl_tmp
   ) i
LEFT   JOIN tbl t USING (col1, col2)
WHERE  t.col1 IS NULL;

Die Temp. Tabelle wird am Ende der Sitzung automatisch gelöscht.

Aber die richtige Lösung wäre, sich mit der Wurzel des Fehlers zu befassen, der überhaupt zu Duplikaten führt.

Ursprüngliche Frage

1) Sie könnten das pk überhaupt nicht hinzufügen, wenn es ein einziges Duplikat über alle Spalten gibt.

2) Ich würde nur eine PostgreSQL-Datenbank Version 8.1 anfassen mit einer fünf Meter langen Stange. Es ist hoffnungslos alt, veraltet und ineffizient, wird nicht mehr unterstützt und hat wahrscheinlich eine Reihe von nicht behobenen Sicherheitslücken. Offizielle Postgres-Versionierungsseite.
@David hat die SQL-Anweisung bereits bereitgestellt.

3 &4) Eine doppelte Schlüsselverletzung. Das Auslösen eines Fehlers durch PostgreSQL bedeutet auch, dass die gesamte Transaktion zurückgesetzt wird. Wenn Sie das in einem Perl-Skript abfangen, kann der Rest der Transaktion nicht ausgeführt werden. Sie müssten beispielsweise mit plpgsql ein serverseitiges Skript erstellen, in dem Sie Ausnahmen abfangen können.