在PL / pgSQL中动态执行查询

EJB*_*EJB 2 postgresql dynamic-sql plpgsql aggregate-functions dynamic-queries

我已经找到了我要在Oracle和SQL Server上解决的问题的解决方案(我认为),但是似乎无法将其转换为Postgres解决方案。我正在使用Postgres 9.3.6。

这个想法是为了生成用于分析目的的有关表内容的“元数据”。这只能通过让每一列都运行查询来找出(例如...)最小/最大/计数值等来完成(AFAIK)。为了使过程自动化,最好让数据库生成查询,然后执行查询。

使用示例salesdata表,我可以使用以下代码段为每列生成一个选择查询,并返回min()值:

SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '  
FROM information_schema.columns 
WHERE table_name = 'salesdata'
Run Code Online (Sandbox Code Playgroud)

优点是,无论列数如何,数据库都会生成代码。现在,我想到了无数个地方来存储这些查询,这些查询可以是某种变量,也可以是表列,其想法是执行这些查询。我想将生成的查询存储在变量中,然后使用EXECUTE(或EXECUTE IMMEDIATE)语句执行它们,这是此处采用的方法(请参见右窗格),但是Postgres不会让我在函数外部声明变量,因此我一直在抓挠我对如何将它们组合在一起,无论是什至是遵循的方向,也许还有更简单的东西。

您有任何指点吗,由于其他问题,我目前正在尝试类似的方法,但不知道我是否朝着正确的方向前进:

CREATE OR REPLACE FUNCTION foo()
RETURNS void AS
$$
DECLARE
    dyn_sql text; 
BEGIN            
dyn_sql := SELECT 'SELECT min('||column_name||') from salesdata'    
    FROM information_schema.columns 
    WHERE table_name = 'salesdata';
execute dyn_sql
END
$$ LANGUAGE PLPGSQL;    
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 5

系统统计

在自己滚动之前,请查看系统表pg_statistic或视图pg_stats

该视图仅允许访问与用户有权读取的表相对应的pg_statistic行,因此可以安全地允许对此视图进行公共读取。

它可能已经具有您要计算的某些统计信息。它由填充ANALYZE,因此您可以在检查之前为新(或任何)表运行该表。

-- ANALYZE tbl;  -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND   schemaname = 'public';
Run Code Online (Sandbox Code Playgroud)

通用动态plpgsql函数

似乎您想返回给定表中每一列的最小值。这不是一件容易的事,因为一个函数(一般来说就是SQL)要求在创建时或在调用时至少在多态数据类型的帮助下知道返回类型。

此功能自动安全地执行所有操作。只要每个列都允许使用聚合函数,则适用于任何min()。但是您需要了解如何使用plpgsql。

CREATE OR REPLACE FUNCTION f_min_of(_tbl anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE (
   SELECT format('SELECT (t::%2$s).* FROM (SELECT min(%1$s) FROM %2$s) t'
                , string_agg(quote_ident(attname), '), min(' ORDER BY attnum)
                , pg_typeof(_tbl)::text)
   FROM   pg_attribute
   WHERE  attrelid = pg_typeof(_tbl)::text::regclass
   AND    NOT attisdropped  -- no dropped (dead) columns
   AND    attnum > 0        -- no system columns
   );
END
$func$  LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

呼叫:

SELECT * FROM f_min_of(NULL::tbl);  -- tbl being the table name
Run Code Online (Sandbox Code Playgroud)

SQL提琴。

您需要了解以下概念:

  • 使用的plpgsql中的动态SQL EXECUTE
  • 多态类型。
  • Postgres中的行类型和表类型。
  • 如何防御SQL注入。
  • 汇总功能。
  • 系统目录。

相关答案及详细说明:

类型不匹配的特殊困难

我利用Postgres为每个现有表定义行类型。使用多态类型的概念,我能够创建一个适用于任何表的函数。

但是,与基础列相比,某些聚合函数返回相关但不同的数据类型。例如,min(varchar_column)return text是位兼容的,但数据类型不完全相同。PLPGSQL功能在这里有一个弱点,坚持数据类型完全相同的在所申报的RETURNS条款。没有尝试转换,甚至没有隐式转换,更不用说任务转换了。

那应该加以改善。用Postgres 9.3测试。没用9.4重新测试,但是我很确定,这方面没有任何改变。

这就是这种构造的来源:

SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;
Run Code Online (Sandbox Code Playgroud)

一开始似乎有点扭曲。这是解决方法:通过整行铸行类型基础表中明确我们强迫转让强制类型转换来获得原始数据类型的每一列。

请注意,这对于某些聚合函数可能会失败。sum()返回numerica sum(bigint_column)以容纳基本数据类型溢出的总和。投射回bigint可能会失败...