Använd en seriell kolumn
Din plan är att lägga till ett onödigt stort index för 40 miljoner (!) rader. Och du är inte ens säker på att det kommer att bli unikt. Jag skulle starkt avråda från den vägen. Lägg till en serie
kod>
kolumn istället och vara klar med den:
ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;
Det är allt du behöver göra. Resten sker automatiskt. Mer i manualen eller i dessa närbesläktade svar:
PostgreSQL primärnyckel automatiskt inkrement kraschar i C++
Automatisk ökning av SQL-funktion
Lägga till en serie
kolumn är engångsdrift, men dyr. Hela tabellen måste skrivas om, vilket blockerar uppdateringar under operationens varaktighet. Görs bäst utan samtidig belastning vid lediga timmar. Jag citerar handboken här
:
Eftersom detta effektivt skriver om hela tabellen kan du lika gärna skapa en ny tabell med en seriell pk-kolumn, infoga alla rader från den gamla tabellen, låta serien fyllas med standardvärden från dess sekvens, släppa den gamla och byta namn på den nya. Mer i dessa närbesläktade svar:
Uppdatera databasrader utan att låsa tabellen i PostgreSQL 9.2
Lägg till ny kolumn utan tabell låsa?
Se till att alla dina INSERT-satser har en mållista, så kan en extra kolumn inte förväxla dem:
INSERT INTO tbl (col1, col2, ...) VALUES ...
Inte:
INSERT INTO tbl VALUES ...
En serie
implementeras med ett heltal
kolumn (4 byte).
En primärnyckelbegränsning implementeras med ett unikt index och en NOT NULL
begränsning på de inblandade kolumnerna.
Innehållet i ett index lagras ungefär som tabeller. Ytterligare fysisk lagring behövs separat. Mer om fysisk lagring i detta relaterade svar:
Beräkna och spara utrymme i PostgreSQL
Ditt index skulle innehålla 2 tidsstämplar (2 x 8 byte) plus ett långt filnamn inkl. sökväg (~ 50 byte?) Det skulle göra indexet cirka 2,5 GB större (40M x 60 .. något byte) och alla operationer långsammare.
Hantera dubbletter
Hur man hanterar "importera dubbletter" beror på hur du importerar data och hur "duplicering" definieras exakt.
Om vi pratar om KOPIERA
satser, skulle ett sätt vara att använda en temporär mellanställningstabell och kollapsa dubbletter med en enkel SELECT DISTINCT
eller DISTINCT ON
i INSERT
kommando:
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;
Eller för att även förbjuda dubbletter med redan befintliga rader:
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;
Temp. tabellen släpps automatiskt i slutet av sessionen.
Men den rätta lösningen skulle vara att ta itu med roten till felet som ger dubbletter i första hand.
Ursprunglig fråga
1) Du kunde inte lägga till pk alls, om det finns en enda dubblett över alla kolumner.
2) Jag skulle bara röra en PostgreSQL-databas version 8.1 med en femfots stång. Det är hopplöst gammalt, föråldrat och ineffektivt, stöds inte längre och har förmodligen ett antal ofixade säkerhetshål. Officiell Postgres-versionswebbplats.
@David
har redan tillhandahållit SQL-satsen.
3 &4) Ett duplicerat nyckelbrott. Att PostgreSQL kastar ett fel innebär också att hela transaktionen återställs. Att fånga det i ett perl-skript kan inte göra att resten av transaktionen går igenom. Du måste skapa ett skript på serversidan med plpgsql till exempel, där du kan fånga undantag.