PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Adicionando uma chave primária de várias colunas a uma tabela com 40 milhões de registros

Use uma coluna serial


Seu plano é adicionar um índice desnecessariamente grande para 40 milhões (!) de linhas. E você nem tem certeza de que será único. Eu aconselharia fortemente contra essa via de ação. Adicione um serial coluna em vez disso e pronto:
ALTER TABLE tbl ADD COLUMN tbl_id serial PRIMARY KEY;

Isso é tudo que você precisa fazer. O resto acontece automaticamente. Mais no manual ou nestas respostas intimamente relacionadas:
O incremento automático da chave primária do PostgreSQL falha em C++
Função SQL de incremento automático

Adicionando um serial coluna é uma operação única, mas cara. A tabela inteira deve ser reescrita, bloqueando as atualizações durante a operação. Melhor feito sem carga simultânea em horas de folga. Cito o manual aqui :

Como isso efetivamente reescreve toda a tabela, você também pode criar uma nova tabela com uma coluna serial pk, inserir todas as linhas da tabela antiga, deixando o serial preencher com valores padrão de sua sequência, descartar o antigo e renomear o novo. Mais nestas respostas intimamente relacionadas:
Atualizando linhas do banco de dados sem bloquear a tabela no PostgreSQL 9.2
Adicionar nova coluna sem tabela bloquear?

Certifique-se de que todas as suas instruções INSERT tenham uma lista de destino, então uma coluna adicional não poderá confundi-las:
INSERT INTO tbl (col1, col2, ...) VALUES ...

Não:
INSERT INTO tbl VALUES ...

Um série é implementado com um inteiro coluna (4 bytes).
Uma restrição de chave primária é implementada com um índice exclusivo e um NOT NULL restrição nas colunas envolvidas.
O conteúdo de um índice é armazenado como tabelas. O armazenamento físico adicional é necessário separadamente. Mais sobre armazenamento físico nesta resposta relacionada:
Calculando e economizando espaço no PostgreSQL

Seu índice incluiria 2 carimbos de data/hora (2 x 8 bytes) mais um nome de arquivo longo incl. path (~ 50 bytes?) Isso tornaria o índice em torno de 2,5 GB maior (40M x 60 .. algo bytes) e todas as operações mais lentas.

Lidando com duplicatas


Como lidar com "importar duplicatas" depende de como você está importando dados e como "duplicado" é definido exatamente.

Se estamos falando de COPY instruções, uma maneira seria usar uma tabela de teste temporária e recolher duplicatas com um simples SELECT DISTINCT ou DISTINTO ON no INSERIR comando:
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, para também proibir duplicatas com linhas já existentes:
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;

A temperatura. tabela é descartada no final da sessão automaticamente.

Mas a correção adequada seria lidar com a raiz do erro que produz duplicatas em primeiro lugar.

Pergunta original


1) Você não pode adicionar o pk, se houver uma única duplicata em todas as colunas.

2) Eu só tocaria em um banco de dados PostgreSQL versão 8.1 com um poste de cinco pés. É irremediavelmente antigo, desatualizado e ineficiente, não é mais suportado e provavelmente tem várias falhas de segurança não corrigidas. Site oficial de versão do Postgres.
@David já forneceu a instrução SQL.

3 e 4) Uma violação de chave duplicada. PostgreSQL lançando um erro também significa que toda a transação é revertida. Capturar isso em um script perl não pode fazer o resto da transação passar. Você teria que criar um script do lado do servidor com plpgsql por exemplo, onde você pode capturar exceções.