我有一个系统,可以在不同时间在服务器之间同步各种表。它工作正常,除了某些表的SERIAL键列过时,因为同步不会更新序列。因此,我正在尝试编写一个pl / pgsql函数,以确保如果一个表(在参数中命名)具有序列号,则其下一个值将大于表中的最大值。
实际上,我认为我已经完成了,但是我认为无论如何我都会发布问题,以便人们可以使用它或提出改进建议。
答案 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;