简体   繁体   中英

How to execute a dynamic SQL statement in a single Select statement?

I just wonder how to eval the content of dynamic SQL using one select; this is the example. This is only an example. but I would like dynamically functions, and manage using single selects. ( I know that sqls are only for SELECT instead of modify... but In this deep querentee Im becomeing in a crazy developer)

SELECT 'SELECT SETVAL(' || chr(39) || c.relname || chr(39)|| ' , 
(SELECT MAX(Id)+1 FROM '  || regexp_replace(c.relname, '_[a-zA-Z]+_[a-zA-Z]+(_[a-zA-Z0-9]+)?', '', 'g')   ||' ), true );' 
FROM pg_class c WHERE c.relkind = 'S';

The original output is:

SELECT SETVAL('viewitem_id_seq' , (SELECT MAX(Id)+1 FROM viewitem ), true );
SELECT SETVAL('userform_id_seq' , (SELECT MAX(Id)+1 FROM userform ), true );

This is the dynamic sentence: (SELECT MAX(Id)+1 FROM ' || regexp_replace(c.relname, ' [a-zA-Z]+ [a-zA-Z]+(_[a-zA-Z0-9]+)?', '', 'g')

is an string that generates as output a SQL, how to eval in the same line this statement?

The desired output is:

SELECT SETVAL('viewitem_id_seq' , 25, true );
SELECT SETVAL('userform_id_seq' , 85, true );

thanks!

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 eg used to find the row count for all tables

Some background on how query_to_xml() works

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM