PostgreSQL函数更新变量表的串行键列

时间:2019-01-23 16:40:56

标签: postgresql function plpgsql

我有一个系统,可以在不同时间在服务器之间同步各种表。它工作正常,除了某些表的SERIAL键列过时,因为同步不会更新序列。因此,我正在尝试编写一个pl / pgsql函数,以确保如果一个表(在参数中命名)具有序列号,则其下一个值将大于表中的最大值。

实际上,我认为我已经完成了,但是我认为无论如何我都会发布问题,以便人们可以使用它或提出改进建议。

2 个答案:

答案 0 :(得分:0)

这似乎起作用。似乎需要经常使用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.';

答案 1 :(得分:0)

您实际上并不需要功能。使用this answer的变体,可以用一个语句来完成:

首先,我们需要找到所有使用序列作为默认值的列:

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;