PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Ajouter une clé primaire multi-colonnes à une table avec 40 millions d'enregistrements

Utiliser une colonne de série

Votre plan est d'ajouter un index inutilement énorme pour 40 millions (!) De lignes. Et vous n'êtes même pas sûr que ce sera unique. Je déconseillerais fortement cette voie d'action. Ajoutez un serial colonne à la place et finissons-en :

ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;

C'est tout ce que vous devez faire. Le reste se fait automatiquement. Plus d'informations dans le manuel ou dans ces réponses étroitement liées :
L'incrémentation automatique de la clé primaire PostgreSQL se bloque en C++
Fonction SQL d'incrémentation automatique

Ajouter un serial la colonne est une opération unique, mais coûteuse. Toute la table doit être réécrite, bloquant les mises à jour pendant toute la durée de l'opération. Mieux fait sans charge simultanée en dehors des heures de travail. Je cite le manuel ici :

Puisque cela réécrit efficacement toute la table, vous pouvez aussi bien créer une nouvelle table avec une colonne série pk, insérer toutes les lignes de l'ancienne table, laisser la série se remplir avec les valeurs par défaut de sa séquence, supprimer l'ancienne et renommer la nouvelle. Plus dans ces réponses étroitement liées :
Mettre à jour les lignes de la base de données sans verrouiller la table dans PostgreSQL 9.2
Ajouter une nouvelle colonne sans table verrouiller ?

Assurez-vous que toutes vos instructions INSERT ont une liste cible, alors une colonne supplémentaire ne peut pas les confondre :

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

Non :

INSERT INTO tbl VALUES ...

Un serial est implémenté avec un integer colonne (4 octets).
Une contrainte de clé primaire est implémentée avec un index unique et un NOT NULL contrainte sur les colonnes impliquées.
Le contenu d'un index est stocké un peu comme des tables. Un stockage physique supplémentaire est nécessaire séparément. Plus d'informations sur le stockage physique dans cette réponse connexe :
Calculer et économiser de l'espace dans PostgreSQL

Votre index comprendrait 2 horodatages (2 x 8 octets) plus un long nom de fichier incl. path (~ 50 octets ?) Cela rendrait l'index plus grand d'environ 2,5 Go (40 M x 60 .. quelque chose d'octets) et ralentirait toutes les opérations.

Traitement des doublons

La manière de gérer "l'importation de doublons" dépend de la manière dont vous importez les données et de la définition exacte du "duplicata".

Si nous parlons de COPY instructions, une façon serait d'utiliser une table intermédiaire temporaire et de réduire les doublons avec un simple SELECT DISTINCT ou DISTINCT ON dans le INSERT commande :

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;

Ou, pour interdire également les doublons avec des lignes déjà existantes :

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;

La temp. la table est supprimée automatiquement à la fin de la session.

Mais la solution appropriée serait de traiter la racine de l'erreur qui produit des doublons en premier lieu.

Question initiale

1) Vous ne pouvez pas du tout ajouter le pk, s'il y a un seul doublon sur toutes les colonnes.

2) Je ne toucherais qu'à une base de données PostgreSQL version 8.1 avec une perche de cinq pieds. Il est désespérément ancien, obsolète et inefficace, n'est plus pris en charge et présente probablement un certain nombre de failles de sécurité non corrigées. Site officiel de gestion des versions de Postgres.
@David déjà fourni l'instruction SQL.

3 &4) Une violation de clé en double. PostgreSQL lançant une erreur signifie également que toute la transaction est annulée. Attraper cela dans un script perl ne peut pas faire passer le reste de la transaction. Vous devrez créer un script côté serveur avec plpgsql par exemple, où vous pourrez intercepter les exceptions.