【问题标题】:Executing queries dynamically in PL/pgSQL在 PL/pgSQL 中动态执行查询
【发布时间】:2020-05-28 09:50:41
【问题描述】:

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

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

salesdata 表为例,我可以为每一列生成一个选择查询,返回 min() 值,使用以下 sn-p:

SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '  
FROM information_schema.columns 
WHERE table_name = 'salesdata'

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

您有什么建议吗,我目前正在尝试类似的东西,受到other question 的启发,但不知道我是否朝着正确的方向前进:

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;    

【问题讨论】:

  • 真的不清楚你想做什么。生成查询字符串以保存它们?执行查询并保存结果?请澄清任务。并且,一如既往,请提供您的 Postgres 版本。
  • “用于分析目的” --- 这到底是什么意思?
  • 你希望你的函数返回什么?每列的最小值?
  • plpgsql中没有EXECUTE IMMEDIATE,只有EXECUTE
  • @zerkms:数据分析是一种很好地了解表中的内容、最小/最大值/平均值、值频率、值分布、空值计数等的方法......这个是数据质量评估的起点。

标签: postgresql aggregate-functions plpgsql dynamic-sql dynamic-queries


【解决方案1】:

系统统计

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

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

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

-- ANALYZE tbl;  -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND   schemaname = 'public';

通用动态plpgsql函数

您希望返回给定表中每一列的最小值。这不是一项简单的任务,因为函数(如一般的 SQL)需要在创建时知道返回类型 - 或者至少在调用时借助多态数据类型。

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

CREATE OR REPLACE FUNCTION f_min_of(_tbl anyelement)
  RETURNS SETOF anyelement
  LANGUAGE plpgsql 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$;

打电话(重要!):

SELECT * FROM f_min_of(NULL::tbl);  -- tbl being the table name

db小提琴here
sqlfiddle

您需要了解这些概念:

  • 带有EXECUTE 的plpgsql 中的动态SQL
  • 多态类型
  • Postgres 中的行类型和表类型
  • 如何防御 SQL 注入
  • 聚合函数
  • 系统目录

相关答案及详细解释:

类型不匹配的特殊困难

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

但是,与基础列相比,某些聚合函数返回相关但不同的数据类型。例如,min(varchar_column) 返回text,这是位兼容的,但不是完全相同的数据类型。 PL/pgSQL 函数在这里有一个弱点,它坚持使用RETURNS 子句中声明的数据类型完全。没有尝试转换,甚至没有隐式转换,更不用说赋值转换了。

这应该改进。用 Postgres 9.3 测试。没有用 9.4 重新测试,但我很确定,这方面没有任何变化。

这就是这个构造作为解决方法出现的地方:

SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;

通过将整行显式转换为基础表的行类型,我们强制赋值转换以获取每一列的原始数据类型。

对于某些聚合函数,这可能会失败。 sum()sum(bigint_column) 返回 numeric,以适应溢出基本数据类型的总和。转换回 bigint 可能会失败...

【讨论】:

    【解决方案2】:

    @Erwin Brandstetter,非常感谢您的广泛回答。 pg_stats 确实提供了一些东西,但我真正需要绘制一个完整的配置文件是各种各样的东西,最小值、最大值、计数、空值计数、平均值等......所以必须运行一堆查询每列,有些带有 GROUP BY 等。

    另外,感谢您强调数据类型的重要性,我有点期待这会在某些时候给工作带来麻烦,我主要关心的是如何自动化查询生成及其执行,最后一点是我最关心的问题。

    我已经尝试过您提供的功能(我可能需要开始学习一些 plpgsql)但在 SELECT (t::tbl) 处出现错误:

    ERROR: type "tbl" does not exist
    

    顺便说一句,(t::abc) 表示法是什么,在 python 中这将是一个列表切片,但在 PLPGSQL 中可能不是这样

    【讨论】:

    • 请注意,我不会收到其他答案的通知。偶然发现了这个。我只会收到关于我的帖子的 cmets(或答案)或者您 @-reply 的通知。 Detailed instructions here
    • 关于错误:我不小心将表名硬编码在一个地方。考虑我的更新。
    • 再次感谢您提供广泛的答案,尝试了您的代码,现在效果很好。我必须尝试以我想要的结果的确切方式格式化,但现在很好。
    猜你喜欢
    • 1970-01-01
    • 2018-06-21
    • 2012-05-21
    • 1970-01-01
    • 2012-10-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多