Функция PostgreSQL для обновления столбца последовательного ключа для таблицы переменных - PullRequest
0 голосов
/ 23 января 2019

У меня есть система, которая синхронизирует различные таблицы между серверами в разное время.Он работает нормально, за исключением того, что некоторые таблицы имеют столбцы SERIAL key, которые устарели, потому что синхронизация не обновляет последовательности.Поэтому я пытаюсь написать функцию pl / pgsql, чтобы убедиться, что если таблица (названная в параметре) имеет последовательный ключ, ее следующее значение будет на одно больше, чем наибольшее значение в таблице.

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

Ответы [ 2 ]

0 голосов
/ 24 января 2019

Вам на самом деле не нужна функция для этого.С вариацией этот ответ это можно сделать с помощью одного оператора:

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

select table_schema, table_name, column_name,
       pg_get_serial_sequence(format('%I.%I', table_schema, table_name), column_name)
from information_schema.columns
where table_schema = 'public'
  and column_default like 'nextval%'

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

with sequences as (
  select table_schema, table_name, column_name,
         pg_get_serial_sequence(format('%I.%I', table_schema, table_name), column_name) as col_sequence
  from information_schema.columns
  where table_schema = 'public' --<< adjust for your schemas
    and column_default like 'nextval%'
), maxvals as (
  select table_schema, table_name, column_name, col_sequence,
          (xpath('/row/max/text()',
             query_to_xml(format('select coalesce(max(%I),0) from %I.%I', column_name, table_schema, table_name), true, true, ''))
          )[1]::text::bigint as max_val
  from sequences
  where col_sequence is not null
) 
select table_schema, 
       table_name, 
       column_name, 
       col_sequence,
       max_val,
       setval(col_sequence, max_val)
from maxvals;
0 голосов
/ 23 января 2019

Это похоже на работу. Кажется, нужно много использовать EXECUTE.

-- Update the serial key sequence of this given table, if appropriate.
CREATE OR REPLACE FUNCTION update_serial(i_table CHARACTER VARYING)
  RETURNS CHARACTER VARYING AS $$
DECLARE
    v_key_col CHARACTER VARYING;
    v_seq_name CHARACTER VARYING;
    v_max RECORD;

BEGIN
    -- Get the name of the primary key, if any.
    SELECT c.column_name, c.data_type INTO v_key_col
    FROM information_schema.table_constraints tc
    JOIN information_schema.constraint_column_usage AS ccu USING (constraint_schema, constraint_name)
    JOIN information_schema.columns AS c
        ON c.table_schema = tc.constraint_schema AND tc.table_name = c.table_name AND ccu.column_name = c.column_name
    WHERE constraint_type = 'PRIMARY KEY' and tc.table_name = i_table;

    IF v_key_col IS NULL THEN RETURN 'No key found';
        END IF;

    -- Get the name of the sequence that determines the next number for the primary key, if any.
    SELECT pg_get_serial_sequence(i_table, v_key_col) INTO v_seq_name;

    IF v_seq_name IS NULL THEN RETURN 'No sequence found';
        END IF;

    -- Get the maximum value in the primary key data, and add 1.
    EXECUTE 'SELECT MAX(' || v_key_col || ') + 1 m FROM ' || i_table INTO v_max;

-- Set the value of the sequence, converting to regclass and back to text so as to clean up the name and remove
-- the schema. It needs to put its output somewhere though we're not using it, so it goes back into v_max.
SELECT SETVAL(quote_ident(v_seq_name::regclass::text), v_max.m) INTO v_max;

    RETURN 'Done';
END;
$$ LANGUAGE 'plpgsql';
COMMENT ON FUNCTION update_serial(i_table CHARACTER VARYING) IS
'Update the serial key sequence of this given table, if appropriate.';
...