If those are serial
or identity
columns it would be better to use pg_get_serial_sequence()
to get the link between a table's column and its sequence.
You can actually run dynamic SQL inside a SQL statement by using query_to_xml()
I use the following script if I need to synchronize the sequences for serial
(or identity
) columns with their actual values:
with sequences as (
-- this query is only to identify all sequences that belong to a column
-- it's essentially similar to your select * from pg_class where reltype = 'S'
-- but returns the sequence name, table and column name to which the
-- sequence belongs
select *
from (
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 not in ('pg_catalog', 'information_schema')
) t
where col_sequence is not null
), maxvals as (
select table_schema, table_name, column_name, col_sequence,
--
-- this is the "magic" that runs the SELECT MAX() query
--
(xpath('/row/max/text()',
query_to_xml(format('select max(%I) from %I.%I', column_name, table_schema, table_name), true, true, ''))
)[1]::text::bigint as max_val
from sequences
)
select table_schema,
table_name,
column_name,
col_sequence,
coalesce(max_val, 0) as max_val,
setval(col_sequence, coalesce(max_val, 1)) --<< this uses the value from the dynamic query
from maxvals;
The dynamic part here is the call to query_to_xml()
First I use format()
to properly deal with identifiers. It also makes writing the SQL easier as no concatenation is required. So for every table returned by the first CTE, something like this is executed:
query_to_xml('select max(id) from public.some_table', true, true, '')
This returns something like:
<row xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<max>42</max>
</row>
The value is than extracted from the XML value using xpath() and converted to a number which then is used in the final SELECT to actually call setval()
The nesting with multiple CTEs is only used to make each part more readable.
The same approach can e.g. used to find the row count for all tables
Some background on how query_to_xml()
works