Скопируйте таблицу (включая индексы) в postgres

85

У меня есть таблица postgres. Мне нужно удалить из него некоторые данные. Я собирался создать временную таблицу, скопировать данные, воссоздать индексы и удалить нужные мне строки. Я не могу удалить данные из исходной таблицы, потому что эта исходная таблица является источником данных. В одном случае мне нужно получить некоторые результаты, которые зависят от удаления X, в другом случае мне нужно удалить Y. Поэтому мне нужно, чтобы все исходные данные всегда были под рукой и были доступны.

Однако кажется немного глупым воссоздать таблицу, скопировать ее снова и воссоздать индексы. Есть ли способ в postgres сказать: «Мне нужна полная отдельная копия этой таблицы, включая структуру, данные и индексы»?

К сожалению, в PostgreSQL нет команды «СОЗДАТЬ ТАБЛИЦУ .. КАК X, ВКЛЮЧАЯ ИНДЕКСЫ»

Рори
источник

Ответы:

108

Новый PostgreSQL (начиная с версии 8.3 согласно документации) может использовать "ВКЛЮЧАЮЩИЕ ИНДЕКСЫ":

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

Как видите, я тестирую 8.3.

Теперь создадим таблицу:

# 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

И посмотрите, как это выглядит:

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

Теперь мы можем скопировать структуру:

# 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

И проверим структуру:

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

Если вы используете PostgreSQL до 8.3, вы можете просто использовать pg_dump с опцией «-t», чтобы указать 1 таблицу, изменить имя таблицы в дампе и загрузить ее снова:

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

А теперь таблица:

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

источник
14
Таким образом, последовательность первичного ключа (x1_id_seq) будет совместно использоваться двумя таблицами!
Jauzsika 05
2
Ops, с pg9.X, последовательность первичного ключа будет совместно использоваться при использовании «ВКЛЮЧАЯ ОГРАНИЧЕНИЯ» (не «ВКЛЮЧАЯ ИНДЕКСЫ»).
Питер Краусс
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]  

Вот пример

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

Другой способ создать новую таблицу из первой - использовать

    CREATE TABLE films_recent (LIKE films INCLUDING INDEXES);  

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

Обратите внимание, что Postgresql имеет патч для исправления проблем с табличным пространством, если используется второй метод.

Человек-ВолкДракон
источник
В postgres нет "ВКЛЮЧАЮЩИХ ИНДЕКСОВ".
Рори
2
Какую версию вы используете? Прочтите последний документ, он есть
WolfmanDragon
6
с pg9.X, при использовании «ВКЛЮЧАЯ ОГРАНИЧЕНИЯ» (не «ВКЛЮЧАЯ ИНДЕКСЫ») последовательность первичных ключей будет совместно использоваться двумя таблицами (!).
Питер Краусс
Похоже, это должно быть CREATE TABLE my_table (LIKE...)вместо CREATE TABLE my_table LIKE...того, чтобы работать. Отредактированный ответ.
Джейсон Светт
@PeterKrauss, вы выяснили, что такое последовательность общего первичного ключа? Я пытаюсь скопировать кучу данных в новую таблицу. Я не могу отбросить старую таблицу и переименовать новую, потому что pk из новой указывает на старую.
yellottyellott
5

В сети много ответов, один из них можно найти здесь .

В итоге я сделал что-то вроде этого:

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

Это скопирует схему и данные, включая индексы, но не включая триггеры и ограничения. Обратите внимание, что индексы используются совместно с исходной таблицей, поэтому при добавлении новой строки в любую таблицу счетчик будет увеличиваться.

Ошай
источник
4

У меня есть таблица postgres. Мне нужно удалить из него некоторые данные.

Я полагаю, что ...

delete from yourtable
where <condition(s)>

... по какой-то причине не работает. (Не хотите поделиться этой причиной?)

Я собирался создать временную таблицу, скопировать данные, воссоздать индексы и удалить нужные мне строки.

Загляните в pg_dump и pg_restore. Использование pg_dump с некоторыми умными опциями и, возможно, редактирование вывода перед pg_restoring может помочь.


Поскольку вы проводите анализ данных типа «что, если», мне интересно, не лучше ли вам использовать представления.

Вы можете определить представление для каждого сценария, который хотите протестировать, на основе отрицания того, что вы хотите исключить. То есть определите представление на основе того, что вы хотите включить. Например, если вам нужно «окно» для данных, в котором вы «удалили» строки, где X = Y, вы должны создать представление в виде строк, где (X! = Y).

Представления хранятся в базе данных (в системном каталоге) как их определяющий запрос. Каждый раз, когда вы запрашиваете представление, сервер базы данных ищет базовый запрос, который его определяет, и выполняет его (соединяется с любыми другими условиями, которые вы использовали). У этого подхода есть несколько преимуществ:

  1. Вы никогда не дублируете какую-либо часть своих данных.
  2. Индексы, уже используемые для базовой таблицы (исходная, «настоящая» таблица), будут использоваться (как считает оптимизатор запросов) при запросе каждого представления / сценария. Нет необходимости переопределять или копировать их.
  3. Поскольку представление представляет собой "окно" (НЕ фигуру) "реальных" данных в базовой таблице, вы можете добавлять / обновлять / удалять в своей базовой таблице и просто повторно запрашивать сценарии представления без необходимости воссоздавать что-либо как данные меняются со временем.

Конечно, есть компромисс. Поскольку представление является виртуальной таблицей, а не «реальной» (базовой) таблицей, вы фактически выполняете (возможно, сложный) запрос каждый раз, когда получаете к нему доступ. Это может немного замедлить работу. Но может и не быть. Это зависит от многих факторов (размер и характер данных, качество статистики в системном каталоге, скорость оборудования, загрузка и многое другое). Вы не узнаете, пока не попробуете. Если (и только если) вы действительно обнаружите, что производительность неприемлемо низкая, вы можете рассмотреть другие варианты. (Материализованные представления, копии таблиц и т. Д. Все, что меняет пространство на время.)

Алан
источник
Я обновил вопрос, чтобы объяснить, почему я не могу просто удалить из исходной таблицы
Рори
1

Создайте новую таблицу, используя select, чтобы получить нужные данные. Затем замените старую таблицу новой.

create table mynewone as select * from myoldone where ...
mess (re-create) with indexes after the table swap.
gsamaras
источник
0

Простой способ - включить все:

CREATE TABLE new_table (LIKE original_table INCLUDING ALL);
Самка луня
источник