30 таблиц с несколькими строками - TRUNCATE - самый быстрый способ их удаления и reset прикрепленных последовательностей?

Интересно, какой самый быстрый способ выполнить такую задачу в PostgreSQL? Я заинтересован в самых быстрых решениях, когда-либо возможных.

Я нашел себе такое решение для MySQL, оно работает намного быстрее, чем просто усечение таблиц одна за другой. Но в любом случае меня интересуют и самые быстрые решения для MySQL. Смотрите мой результат здесь, конечно, это только для MySQL: https://github.com/bmabey/database_cleaner/issues/126

У меня есть следующие предположения:

  • У меня 30-100 столов. Пусть им будет 30.
  • Половина столов пуста.
  • Каждая непустая таблица имеет, скажем, не более 100 строк. Я имею в виду, что таблицы НЕ большие.
  • Мне нужна дополнительная возможность исключить из этой процедуры 2, 5 или N таблиц.

  • Я не могу! использовать транзакции.

Мне нужна самая быстрая стратегия очистки для такого случая, работающая на PostgreSQL как 8, так и 9.

Я вижу следующие подходы:

  1. Усекать каждую таблицу. Я думаю, это слишком медленно, особенно для пустых таблиц.

  2. Проверьте каждую таблицу на пустоту более быстрым методом, а затем, если она пуста, сбросьте столбец уникального идентификатора (аналог AUTO_INCREMENT в MySQL) в исходное состояние (1), то есть восстановите ее last_value из последовательности обратно в 1, в противном случае запустите truncate в теме.

Я использую код Ruby для перебора всех таблиц, вызывая приведенный ниже код для каждой из них, я пытался настроить код SQL, работающий с каждой таблицей, например:

DO $$DECLARE r record;
BEGIN
  somehow_captured = SELECT last_value from #{table}_id_seq
  IF (somehow_captured == 1) THEN
    == restore initial unique identifier column value here ==
  END

  IF (somehow_captured > 1) THEN
    TRUNCATE TABLE #{table};
  END IF;
END$$;

Управляя этим кодом в различных аспектах, я не мог заставить его работать, потому что я незнаком с функциями и блоками PostgreSQL (и переменными).

Также я предположил, что EXISTS (ВЫБРАТЬ что-то из TABLE) может как-то использоваться для хорошей работы в качестве одного из элементов "процедуры проверки", из которого должна состоять процедура очистки, но также не выполнила ее.

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

ОБНОВИТЬ:

Мне нужно все это для запуска модульных и интеграционных тестов для проектов Ruby или Ruby on Rails. Каждый тест должен иметь чистую БД перед выполнением или выполнять очистку после себя (так называемый демонтаж). Транзакции очень хороши, но они становятся непригодными для запуска тестов с конкретными веб-драйверами, в моем случае необходим переход на стратегию усечения. Как только я обновлю это со ссылкой на RoR, пожалуйста, не публикуйте здесь ответы о "Очевидно, вам нужен DatabaseCleaner для PG" и так далее, и так далее.

ОБНОВЛЕНИЕ 2:

Стратегия, описанная здесь недавно, была объединена с DatabaseCleaner, https://github.com/bmabey/database_cleaner как опция: pre_count (см. README там).

Ответ 1

Если кто-то заинтересован в текущей стратегии, я использую для этого, см. этот репозиторий на основе Ruby https://github.com/stanislaw/truncate-vs-count для MySQL и PostgreSQL.

Мои результаты:

MySQL: самая быстрая стратегия очистки баз данных - усечение со следующими изменениями:

if table is not empty
  truncate. 
else 
  if AUTO_INCREMENT is not 0
    truncate.
  end
end
  • Для MySQL просто усечение намного быстрее, чем просто удаление. Единственный случай, когда DELETE выигрывает над TRUNCATE, делает это на пустой таблице.
  • Для усечения MySQL с пустыми проверками выполняется намного быстрее, чем просто несколько усечений.
  • Для удаления MySQL с пустыми проверками намного быстрее, чем просто DELETE для каждой таблицы.

PostgreSQL: самая быстрая стратегия очистки баз данных - это удаление с теми же пустыми проверками, что и для MySQL, но вместо этого полагаться на currval:

if table is not empty
  delete table
else 
  if currval is not 0
    delete table
  end
end
  • Для PostgreSQL просто удаление выполняется намного быстрее, чем просто TRUNCATION (даже несколько).
  • Для PostgreSQL несколько TRUNCATE, выполняющих пустые проверки перед, немного быстрее, чем просто несколько TRUNCATE
  • Для удаления PostgreSQL с пустыми проверками выполняется несколько быстрее, чем просто удаление PostgreSQL.

Это с начала: https://github.com/bmabey/database_cleaner/issues/126

Это код результата и длительное обсуждение: https://github.com/bmabey/database_cleaner/pull/127

Это обсуждение списка рассылки pgsql-performance: http://archives.postgresql.org/pgsql-performance/2012-07/msg00047.php

Мы начали собирать отзывы пользователей, подтверждающие мою идею, с первой проверкой пустых таблиц.

Ответ 2

PostgreSQL может обрезать многие таблицы в одном выражении TRUNCATE TABLE. Не утруждайте себя повторением и просто делайте

TRUNCATE TABLE table1,table2,table3,...,table30;

Ответ 3

См. также:

Скорость усечения Postgresql

для обсуждения того, почему усечение может быть медленнее на Pg и почему DELETE - это не одно и то же.

Ответ 4

Как указано в комментарии
(хотя я не считаю, что это правильный ответ, но он слишком длинный для комментария)

Не существует (заметной) разницы в производительности между усечением пустой таблицы или усечением большой таблицы.

Как указано в руководстве (http://www.postgresql.org/docs/current/static/sql-truncate.html), "он фактически не сканирует таблицы"

Итак, если вы сначала проверите, есть ли в таблице какие-либо строки, вы сканируете таблицу. Что-то, чего не произойдет, если вы просто выпустите truncate, не беспокоясь о том,

Ответ 5

[Я не знаю RoR]

Хороший способ начать с чистого листа - создать и использовать временную SCHEMA:

DROP SCHEMA fuzz CASCADE;
CREATE SCHEMA fuzz;
SET search_path='fuzz';

(это то, что я использую для тестирования фрагментов sql). Но это создало бы пустую схему, и вы не можете копировать схемы, IFAIK.

Другой способ - создать вашу базу данных (включая пустые таблицы) и использовать ее в качестве шаблона для построения тестовой установки:

DROP DATABASE testdb;
CREATE DATABASE testdb TEMPLATE my_spcial_template;

Проблема заключается в том, что вы не можете удалить базу данных, если к ней все еще подключены (например, сам процесс удаления). Таким образом, ваш интерфейс должен сначала отключиться, чем временно подключиться к какой-либо другой БД (такой как my_spcial_template), чем dropdb + createdb, чем connect testdb. Я не знаю о производительности, но, по крайней мере, это надежная схема.