在 PL/pgSQL 中动态执行查询
Executing queries dynamically in PL/pgSQL
我已经找到(我认为)解决我将要在 Oracle 和 SQL 服务器上提出的问题的解决方案,但似乎无法将其转化为 Postgres 解决方案。我正在使用 Postgres 9.3.6。
我们的想法是能够生成 "metadata" 关于 table 内容以进行分析。这只能通过对每一列进行查询 运行 来完成(AFAIK),以便找出,比如说... min/max/count 值等。为了使过程自动化,最好让数据库生成查询,然后执行。
举个例子 salesdata
table,我可以为每一列生成一个 select 查询,返回 min() 值,使用以下代码片段:
SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '
FROM information_schema.columns
WHERE table_name = 'salesdata'
优点是无论列数如何,数据库都会生成代码。
现在我想到了无数个地方来存储这些查询,可以是某种变量,也可以是 table 列,然后执行这些查询。
我想将生成的查询存储在一个变量中,然后使用 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;
系统统计
在你自己动手之前,先看看系统 table pg_statistic
or the view pg_stats
:
This view allows access only to rows of pg_statistic
that correspond
to tables the user has permission to read, and therefore it is safe to
allow public read access to this view.
它可能已经有了您要计算的一些统计数据。它由 ANALYZE
填充,因此您可以 运行 在检查之前为新的(或任何)tables。
-- ANALYZE tbl; -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND schemaname = 'public';
通用动态 plpgsql 函数
您想要 return 给定 table 中每一列的 最小值。这不是一项简单的任务,因为一个函数(通常像 SQL)需要在创建时知道 return 类型——或者至少在调用时借助多态数据类型。
此功能自动且安全地完成所有工作。适用于 any table,只要每一列都允许聚合函数 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
你需要了解这些概念:
- plpgsql 中的动态 SQL 与
EXECUTE
- 多态类型
- Postgres 中的行类型和 table 类型
- 如何防御SQL注入
- 聚合函数
- 系统目录
详细解释的相关回答:
- Table name as a PostgreSQL function parameter
- Refactor a PL/pgSQL function to return the output of various SELECT queries
- Postgres data type cast
- How to set value of composite variable field using dynamic SQL
- How to check if a table exists in a given schema
- Select columns with particular column names in PostgreSQL
- Generate series of dates - using date type as input
类型不匹配的特殊困难
我正在利用 Postgres 为每个现有 table 定义行类型。使用多态类型的概念,我能够创建适用于任何 table.
的 one 函数
但是,一些聚合函数 return 相关但与基础列相比不同的数据类型。例如,min(varchar_column)
returns text
,它是位兼容的,但不完全相同的数据类型。 PL/pgSQL 函数在这里有一个弱点,它坚持在 RETURNS
子句中声明的数据类型 完全 。没有尝试转换,甚至没有隐式转换,更不用说赋值转换了。
应该改进。使用 Postgres 9.3 测试。没有用 9.4 重新测试,但我很确定,这方面没有任何变化。
这就是这个构造作为 解决方法:
出现的地方
SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;
通过将整行强制转换为底层 table 的行类型,我们强制强制转换以获取每一列的原始数据类型。
对于某些聚合函数,这可能会失败。 sum()
returns numeric
用于 sum(bigint_column)
以容纳溢出基本数据类型的总和。转换回 bigint
可能会失败 ...
@Erwin B运行dstetter,非常感谢您的广泛回答。 pg_stats 确实提供了一些东西,但我真正需要绘制一个完整的配置文件的是各种各样的东西,最小值、最大值、计数、空值计数、平均值等......所以一堆查询有每列为 运行,有些列带有 GROUP BY 等。
此外,感谢您强调数据类型的重要性,我有点期待这会在某个时候在工作中引发麻烦,我主要关心的是如何自动生成查询及其执行,这最后一点是我最关心的。
我已经尝试了您提供的功能(我可能需要开始学习一些 plpgsql)但是在 SELECT (t::tbl) 处出现错误:
ERROR: type "tbl" does not exist
顺便说一句,(t::abc) 符号指的是什么,在 python 中这将是一个列表切片,但在 PLPGSQL
中可能不是这种情况
我已经找到(我认为)解决我将要在 Oracle 和 SQL 服务器上提出的问题的解决方案,但似乎无法将其转化为 Postgres 解决方案。我正在使用 Postgres 9.3.6。
我们的想法是能够生成 "metadata" 关于 table 内容以进行分析。这只能通过对每一列进行查询 运行 来完成(AFAIK),以便找出,比如说... min/max/count 值等。为了使过程自动化,最好让数据库生成查询,然后执行。
举个例子 salesdata
table,我可以为每一列生成一个 select 查询,返回 min() 值,使用以下代码片段:
SELECT 'SELECT min('||column_name||') as minval_'||column_name||' from salesdata '
FROM information_schema.columns
WHERE table_name = 'salesdata'
优点是无论列数如何,数据库都会生成代码。
现在我想到了无数个地方来存储这些查询,可以是某种变量,也可以是 table 列,然后执行这些查询。
我想将生成的查询存储在一个变量中,然后使用 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;
系统统计
在你自己动手之前,先看看系统 table pg_statistic
or the view pg_stats
:
This view allows access only to rows of
pg_statistic
that correspond to tables the user has permission to read, and therefore it is safe to allow public read access to this view.
它可能已经有了您要计算的一些统计数据。它由 ANALYZE
填充,因此您可以 运行 在检查之前为新的(或任何)tables。
-- ANALYZE tbl; -- optionally, to init / refresh
SELECT * FROM pg_stats
WHERE tablename = 'tbl'
AND schemaname = 'public';
通用动态 plpgsql 函数
您想要 return 给定 table 中每一列的 最小值。这不是一项简单的任务,因为一个函数(通常像 SQL)需要在创建时知道 return 类型——或者至少在调用时借助多态数据类型。
此功能自动且安全地完成所有工作。适用于 any table,只要每一列都允许聚合函数 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
你需要了解这些概念:
- plpgsql 中的动态 SQL 与
EXECUTE
- 多态类型
- Postgres 中的行类型和 table 类型
- 如何防御SQL注入
- 聚合函数
- 系统目录
详细解释的相关回答:
- Table name as a PostgreSQL function parameter
- Refactor a PL/pgSQL function to return the output of various SELECT queries
- Postgres data type cast
- How to set value of composite variable field using dynamic SQL
- How to check if a table exists in a given schema
- Select columns with particular column names in PostgreSQL
- Generate series of dates - using date type as input
类型不匹配的特殊困难
我正在利用 Postgres 为每个现有 table 定义行类型。使用多态类型的概念,我能够创建适用于任何 table.
的 one 函数但是,一些聚合函数 return 相关但与基础列相比不同的数据类型。例如,min(varchar_column)
returns text
,它是位兼容的,但不完全相同的数据类型。 PL/pgSQL 函数在这里有一个弱点,它坚持在 RETURNS
子句中声明的数据类型 完全 。没有尝试转换,甚至没有隐式转换,更不用说赋值转换了。
应该改进。使用 Postgres 9.3 测试。没有用 9.4 重新测试,但我很确定,这方面没有任何变化。
这就是这个构造作为 解决方法:
出现的地方SELECT (t::tbl).* FROM (SELECT ... FROM tbl) t;
通过将整行强制转换为底层 table 的行类型,我们强制强制转换以获取每一列的原始数据类型。
对于某些聚合函数,这可能会失败。 sum()
returns numeric
用于 sum(bigint_column)
以容纳溢出基本数据类型的总和。转换回 bigint
可能会失败 ...
@Erwin B运行dstetter,非常感谢您的广泛回答。 pg_stats 确实提供了一些东西,但我真正需要绘制一个完整的配置文件的是各种各样的东西,最小值、最大值、计数、空值计数、平均值等......所以一堆查询有每列为 运行,有些列带有 GROUP BY 等。
此外,感谢您强调数据类型的重要性,我有点期待这会在某个时候在工作中引发麻烦,我主要关心的是如何自动生成查询及其执行,这最后一点是我最关心的。
我已经尝试了您提供的功能(我可能需要开始学习一些 plpgsql)但是在 SELECT (t::tbl) 处出现错误:
ERROR: type "tbl" does not exist
顺便说一句,(t::abc) 符号指的是什么,在 python 中这将是一个列表切片,但在 PLPGSQL
中可能不是这种情况