Как я могу получить хеш всей таблицы в postgresql? - PullRequest
18 голосов
/ 26 октября 2010

Я хотел бы довольно эффективный способ сжать всю таблицу до значения хеш-функции.

У меня есть несколько инструментов, которые генерируют целые таблицы данных, которые затем можно использовать для создания дополнительных таблиц и так далее.Я пытаюсь реализовать упрощенную систему сборки, чтобы координировать прогоны сборки и избежать повторения работы.Я хочу иметь возможность записывать хэши входных таблиц, чтобы потом можно было проверить, изменились ли они.Построение таблицы занимает минуты или часы, поэтому тратить несколько секунд на сборку хеш-кодов вполне приемлемо.

Я использовал хак, просто перенаправив вывод pg_dump в md5sum, но для этого требуется перенести весь дамп таблицы по сети.хешировать его на локальном ящике.В идеале я хотел бы создать хеш на сервере базы данных.

Поиск значения хеш-строки в postgresql дает мне способ вычислять хеш-строку для строки за раз,которые потом можно было бы как-то объединить.

Буду очень признателен за любые советы.

Отредактируйте, чтобы опубликовать то, чем я закончил: Ответ tinychen не работал для меня напрямуюпотому что я не мог использовать 'plpgsql', по-видимому.Когда я реализовал функцию в SQL вместо этого, она работала, но была очень неэффективна для больших таблиц.Поэтому вместо объединения всех хэшей строк и последующего хеширования я переключился на использование «скользящего хеша», где предыдущий хеш соединяется с текстовым представлением строки, а затем хэшируется для получения следующего хеша.Это было намного лучше;очевидно, что запуск md5 для коротких строк в миллионы дополнительных раз лучше, чем конкатенация коротких строк в миллионы раз.

create function zz_concat(text, text) returns text as 
    'select md5($1 || $2);' language 'sql';

create aggregate zz_hashagg(text) (
    sfunc = zz_concat,
    stype = text,
    initcond = '');

Ответы [ 6 ]

20 голосов
/ 19 декабря 2012

Я знаю, что это старый вопрос, но это мое решение:

SELECT        
    md5(CAST((array_agg(f.* order by id))AS text)) /* id is a primary key of table (to avoid random sorting) */
FROM
    foo f; 
7 голосов
/ 26 октября 2010

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

create function pg_concat( text, text ) returns text as '
begin
    if $1 isnull then
        return $2;
    else
       return $1 || $2;
    end if;
end;' language 'plpgsql';

create function pg_concat_fin(text) returns text as '
begin
    return $1;
end;' language 'plpgsql';

create aggregate pg_concat (
    basetype = text,
    sfunc = pg_concat,
    stype = text,
    finalfunc = pg_concat_fin);

, тогда вы можете использовать функцию pg_concat для вычисления значения хеш-таблицы.

select md5(pg_concat(md5(CAST((f.*)AS text)))) from f order by id
6 голосов
/ 05 августа 2016
SELECT md5(array_agg(md5((t.*)::varchar))::varchar)
  FROM (
        SELECT *
          FROM my_table
         ORDER BY 1
       ) AS t
3 голосов
/ 21 июля 2015

У меня было похожее требование, чтобы использовать его при тестировании специализированного решения для репликации таблиц.

@ Решение Бена по MD5 (которое он добавил к вопросу) кажется довольно эффективным, но была пара ловушек, которые сбили меня с толку.

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

select zz_hashagg(CAST((example.*)AS text) order by id) from example;

Обратите внимание, что order by находится внутри совокупности.

Во-вторых, использование CAST((example.*)AS text не даст одинаковых результатов для двух таблиц с одинаковым содержимым столбцов, если столбцы не были созданы в одинаковом порядке. В моем случае это не гарантировалось, поэтому, чтобы получить истинное сравнение, мне пришлось перечислить столбцы отдельно, например:

select zz_hashagg(CAST((example.id, example.a, example.c)AS text) order by id) from example;

Для полноты (в случае, если последующее редактирование должно удалить его), вот определение zz_hashagg из вопроса @ Бена:

create function zz_concat(text, text) returns text as 
    'select md5($1 || $2);' language 'sql';

create aggregate zz_hashagg(text) (
    sfunc = zz_concat,
    stype = text,
    initcond = '');
1 голос
/ 06 сентября 2017

Великолепные ответы.

В случае, если кому-то понадобится не использовать функции агрегации, но поддерживать поддержку таблиц размером несколько ГиБ, вы можете использовать это с небольшими потерями производительности по сравнению случшие ответы в случае самых больших таблиц.

CREATE OR REPLACE FUNCTION table_md5(
  table_name CHARACTER VARYING
  , VARIADIC order_key_columns CHARACTER VARYING [])
RETURNS CHARACTER VARYING AS $$
DECLARE
  order_key_columns_list CHARACTER VARYING;
  query CHARACTER VARYING;
  first BOOLEAN;
  i SMALLINT;
  working_cursor REFCURSOR;
  working_row_md5 CHARACTER VARYING;
  partial_md5_so_far CHARACTER VARYING;
BEGIN
  order_key_columns_list := '';

  first := TRUE;
  FOR i IN 1..array_length(order_key_columns, 1) LOOP
    IF first THEN
      first := FALSE;
    ELSE
      order_key_columns_list := order_key_columns_list || ', ';
    END IF;
    order_key_columns_list := order_key_columns_list || order_key_columns[i];
  END LOOP;

  query := (
    'SELECT ' ||
      'md5(CAST(t.* AS TEXT)) ' ||
    'FROM (' ||
      'SELECT * FROM ' || table_name || ' ' ||
      'ORDER BY ' || order_key_columns_list ||
    ') t');

  OPEN working_cursor FOR EXECUTE (query);
  -- RAISE NOTICE 'opened cursor for query: ''%''', query;

  first := TRUE;
  LOOP
    FETCH working_cursor INTO working_row_md5;
    EXIT WHEN NOT FOUND;
    IF first THEN 
      SELECT working_row_md5 INTO partial_md5_so_far;
    ELSE 
      SELECT md5(working_row_md5 || partial_md5_so_far)
      INTO partial_md5_so_far;
    END IF;
    -- RAISE NOTICE 'partial md5 so far: %', partial_md5_so_far;
  END LOOP;

  -- RAISE NOTICE 'final md5: %', partial_md5_so_far;
  RETURN partial_md5_so_far :: CHARACTER VARYING;
END;
$$ LANGUAGE plpgsql;

Используется как:

SELECT table_md5(
  'table_name', 'sorting_col_0', 'sorting_col_1', ..., 'sorting_col_n'
);
1 голос
/ 26 октября 2010

Что касается алгоритма, вы можете выполнить XOR для всех отдельных хэшей MD5 или объединить их и хэшировать конкатенацию.

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

select my_table_hash(md5(CAST((f.*)AS text)) from f order by id 

В качестве промежуточного шага, вместо копирования всей таблицы на клиент, вы можете просто выбрать результаты MD5 для всех строк и выполнить их через md5sum.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...