Copie uma tabela (incluindo índices) no postgres

85

Eu tenho uma mesa postgres. Eu preciso deletar alguns dados dele. Eu iria criar uma tabela temporária, copiar os dados, recriar os índices e excluir as linhas de que preciso. Não posso excluir dados da tabela original, porque essa tabela original é a fonte dos dados. Em um caso, preciso obter alguns resultados que dependem da exclusão de X, em outro caso, preciso excluir Y. Portanto, preciso que todos os dados originais estejam sempre disponíveis e disponíveis.

No entanto, parece um pouco bobo recriar a tabela e copiá-la novamente e recriar os índices. Existe alguma maneira no postgres para dizer "Eu quero uma cópia separada completa desta tabela, incluindo estrutura, dados e índices"?

Infelizmente PostgreSQL não tem um "CREATE TABLE .. LIKE X INCLUDING INDEXES '

Rory
fonte

Respostas:

108

O novo PostgreSQL (desde 8.3 de acordo com os documentos) pode usar "INCLUINDO ÍNDICE":

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

Como você pode ver, estou testando no 8.3.

Agora, vamos criar a tabela:

# 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

E veja como fica:

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

Agora podemos copiar a estrutura:

# 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

E verifique a estrutura:

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

Se você estiver usando PostgreSQL pré-8.3, você pode simplesmente usar pg_dump com a opção "-t" para especificar 1 tabela, alterar o nome da tabela no dump e carregá-la novamente:

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

E agora a mesa é:

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

fonte
14
Desta forma, a sequência da chave primária (x1_id_seq) será compartilhada entre as duas tabelas!
Jauzsika
2
Ops, com pg9.X, a sequência da chave primária será compartilhada ao usar "INCLUINDO RESTRIÇÕES" (não "INCLUINDO ÍNDICE").
Peter Krauss
44
[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]  

Aqui está um exemplo

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

A outra maneira de criar uma nova tabela a partir da primeira é usar

    CREATE TABLE films_recent (LIKE films INCLUDING INDEXES);  

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

Observe que o Postgresql tem um patch para corrigir problemas de espaço de tabela se o segundo método for usado

WolfmanDragon
fonte
Não há "INCLUINDO ÍNDICE" no postgres.
Rory,
2
Qual versão você está usando? Leia o documento mais recente, ele está lá
WolfmanDragon,
6
com pg9.X, ao usar "INCLUDING CONSTRAINTS" (não "INCLUING INDEXES"), a sequência da chave primária será compartilhada entre as duas tabelas (!).
Peter Krauss
Parece que precisa ser em CREATE TABLE my_table (LIKE...)vez de CREATE TABLE my_table LIKE...para funcionar. Resposta editada.
Jason Swett,
@PeterKrauss você descobriu a sequência de chave primária compartilhada? Estou tentando copiar um monte de dados em uma nova tabela. Não posso descartar a tabela antiga e renomear a nova porque o pk da nova aponta para a antiga.
yellottyellott
5

Existem muitas respostas na web, uma delas pode ser encontrada aqui .

Acabei fazendo algo assim:

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

Isso copiará o esquema e os dados, incluindo índices, mas não incluindo gatilhos e restrições. Observe que os índices são compartilhados com a tabela original, portanto, ao adicionar uma nova linha a qualquer uma das tabelas, o contador aumentará.

Oshai
fonte
4

Eu tenho uma mesa postgres. Eu preciso deletar alguns dados dele.

Eu presumo que ...

delete from yourtable
where <condition(s)>

... não funcionará por algum motivo. (Importa-se de compartilhar esse motivo?)

Eu iria criar uma tabela temporária, copiar os dados, recriar os índices e excluir as linhas de que preciso.

Procure em pg_dump e pg_restore. Usar o pg_dump com algumas opções inteligentes e talvez editar a saída antes do pg_restoring pode resolver o problema.


Já que você está fazendo uma análise do tipo "e se" nos dados, eu me pergunto se seria melhor usar visualizações.

Você pode definir uma visão para cada cenário que deseja testar com base na negação do que deseja excluir. Ou seja, defina uma visualização com base no que você deseja incluir. Por exemplo, se você quiser uma "janela" nos dados onde você "excluiu" as linhas onde X = Y, então você criaria uma visualização como linhas onde (X! = Y).

As visualizações são armazenadas no banco de dados (no Catálogo do Sistema) como sua consulta de definição. Cada vez que você consulta a visualização, o servidor de banco de dados procura a consulta subjacente que a define e a executa (com AND com quaisquer outras condições que você usou). Existem vários benefícios para esta abordagem:

  1. Você nunca duplica nenhuma parte de seus dados.
  2. Os índices já em uso para a tabela base (sua tabela "real" original) serão usados ​​(conforme considerado adequado pelo otimizador de consulta) quando você consultar cada visão / cenário. Não há necessidade de redefini-los ou copiá-los.
  3. Uma vez que uma visão é uma "janela" (NÃO um shapshot) nos dados "reais" na tabela base, você pode adicionar / atualizar / deletar em sua tabela base e simplesmente consultar novamente os cenários de visão sem a necessidade de recriar nada como os dados mudam com o tempo.

Há uma compensação, é claro. Como uma visão é uma tabela virtual e não uma tabela "real" (base), você está na verdade executando uma consulta (talvez complexa) toda vez que a acessa. Isso pode desacelerar um pouco as coisas. Mas pode não ser. Depende de muitos problemas (tamanho e natureza dos dados, qualidade das estatísticas no Catálogo do Sistema, velocidade do hardware, carga de uso e muito mais). Você não saberá até tentar. Se (e somente se) você realmente achar que o desempenho está inaceitavelmente lento, você pode procurar outras opções. (Visualizações materializadas, cópias de tabelas, ... qualquer coisa que troque espaço por tempo.)

Alan
fonte
Eu atualizei a pergunta para explicar por que não posso simplesmente excluir da tabela original
Rory,
1

Crie uma nova tabela usando um select para obter os dados desejados. Em seguida, troque a mesa antiga pela nova.

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

Uma maneira simples é incluir tudo:

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