尝试使用 PL/PgSQL 创建动态查询字符串以在 PostgreSQL 9.6 中创建 DRY 函数
Trying to create dynamic query strings with PL/PgSQL to make DRY functions in PostgreSQL 9.6
我有 table 每年都包含相同类型的数据,但收集的数据略有不同,因为它们可能没有相同的字段。
d_abc_2016
d_def_2016
d_ghi_2016
d_jkl_2016
每个 table 都有特定的常量:company_id
、employee_id
、salary
。
但是,每个人可能有也可能没有这些用于计算总激励的字段:bonus
、commission
、cash_incentives
。还有很多,但仅以这些为例。全部 numeric
此时我应该注意,用户只能运行 SELECT
语句。
我希望能够做的是:
- 让用户能够调用
SELECT
并在调用 之外指定他们自己的字段
- 将正在使用的 table 名称传递到函数中,以便在条件逻辑中使用,以确定除了传递整个 [=103] 之外应该如何为最终的
total_incentives
计算构建查询字符串=] 因此不必将大量参数传递给函数
基本上是这样的:
SELECT employee_id, salary, total_incentives(t, 'd_abc_2016')
FROM d_abc_2016 t;
因此被调用的函数将计算 total_incentives
,即 employee_id
的 numeric
,并显示它们的 salary
。但用户可能会选择添加其他字段来查看。
对于函数,因为total_incentives
函数中使用的字段会从table到table不等,我需要创建逻辑来动态构造查询字符串。
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS numeric AS
$$
DECLARE
-- table name lower case in case user typed wrong
tbl varchar(255) := lower(;
-- parse out the table code to use in conditional logic
tbl_code varchar(255) := split_part(survey, '_', 2);
-- the starting point if the query string
base_calc varchar(255) := 'salary + '
-- query string
query_string varchar(255);
-- have to declare this to put computation INTO
total_incentives_calc numeric;
BEGIN
IF tbl_code = 'abc' THEN
query_string := base_calc || 'bonus';
ELSIF tbl_code = 'def' THEN
query_string := base_calc || 'bonus + commission';
ELSIF tbl_code = 'ghi' THEN
-- etc...
END IF;
EXECUTE format('SELECT FROM %I', tbl)
INTO total_incentives_calc
USING query_string;
RETURN total_incentives_calc;
END;
$$
LANGUAGE plpgsql;
这导致:
ERROR: invalid input syntax for type numeric: "salary + bonus"
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE
因为它应该是 return 一组 numeric
值。将其更改为以下内容:
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS SETOF numeric AS
$$
...
RETURN;
得到同样的错误。
图不错,说不定是个table正要return.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS TABLE(tot_inc numeric) AS
$$
...
得到同样的错误。
真的,任何变化都会产生那个结果。所以真的不确定如何让它工作。
查看 RESULT QUERY
、RESULT NEXT
或 RESULT QUERY EXECUTE
。
https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html
RESULT QUERY
不会工作,因为据我所知,它采用硬编码查询,不会接受变量。
RESULT NEXT
遍历每条记录,我认为这不会满足我的需要 table 并且看起来真的很慢......并且需要硬编码查询据我所知。
RESULT QUERY EXECUTE
听起来很有希望。
-- EXECUTE format('SELECT FROM %I', tbl)
-- INTO total_incentives_calc
-- USING query_string;
RETURN QUERY
EXECUTE format('SELECT FROM %I', tbl)
USING query_string;
并得到:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type numeric in column 1.
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY
应该是 returning numeric
.
最后,我可以让它工作,但它不会干。我不想用重复代码为每个 table 创建一堆单独的函数。我见过的大多数工作示例都在函数中包含整个查询,并且这样调用:
SELECT total_incentives(d_abc_2016, 'd_abc_2016');
所以任何额外的列都必须在函数中指定为:
EXECUTE format('SELECT employee_id...)
考虑到用户只能 运行 SELECT
查询,这确实不是一个选项。他们需要指定他们希望在查询中看到的任何其他列。
我发布了一个类似的问题,但被告知不清楚,所以希望这个较长的版本能更清楚地解释我正在尝试做的事情。
列名和表名不应用作 USING
子句传递的查询参数。
大概线路:
RETURN QUERY
EXECUTE format('SELECT FROM %I', tbl)
USING query_string;
应该是:
RETURN QUERY
EXECUTE format('SELECT %s FROM %I', query_string, tbl);
这个案例就是为什么过于 DRY 原则有时会出现问题的例子。如果您直接编写它,那么您的代码将更简单、更清晰并且可能更短。
动态 SQL 是上一个解决方案中的一个 - 不是第一个。仅当使用动态 sql 的代码比不使用动态 SQL.
的代码明显短时,才使用动态 SQL
我有 table 每年都包含相同类型的数据,但收集的数据略有不同,因为它们可能没有相同的字段。
d_abc_2016
d_def_2016
d_ghi_2016
d_jkl_2016
每个 table 都有特定的常量:company_id
、employee_id
、salary
。
但是,每个人可能有也可能没有这些用于计算总激励的字段:bonus
、commission
、cash_incentives
。还有很多,但仅以这些为例。全部 numeric
此时我应该注意,用户只能运行 SELECT
语句。
我希望能够做的是:
- 让用户能够调用
SELECT
并在调用 之外指定他们自己的字段
- 将正在使用的 table 名称传递到函数中,以便在条件逻辑中使用,以确定除了传递整个 [=103] 之外应该如何为最终的
total_incentives
计算构建查询字符串=] 因此不必将大量参数传递给函数
基本上是这样的:
SELECT employee_id, salary, total_incentives(t, 'd_abc_2016')
FROM d_abc_2016 t;
因此被调用的函数将计算 total_incentives
,即 employee_id
的 numeric
,并显示它们的 salary
。但用户可能会选择添加其他字段来查看。
对于函数,因为total_incentives
函数中使用的字段会从table到table不等,我需要创建逻辑来动态构造查询字符串。
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS numeric AS
$$
DECLARE
-- table name lower case in case user typed wrong
tbl varchar(255) := lower(;
-- parse out the table code to use in conditional logic
tbl_code varchar(255) := split_part(survey, '_', 2);
-- the starting point if the query string
base_calc varchar(255) := 'salary + '
-- query string
query_string varchar(255);
-- have to declare this to put computation INTO
total_incentives_calc numeric;
BEGIN
IF tbl_code = 'abc' THEN
query_string := base_calc || 'bonus';
ELSIF tbl_code = 'def' THEN
query_string := base_calc || 'bonus + commission';
ELSIF tbl_code = 'ghi' THEN
-- etc...
END IF;
EXECUTE format('SELECT FROM %I', tbl)
INTO total_incentives_calc
USING query_string;
RETURN total_incentives_calc;
END;
$$
LANGUAGE plpgsql;
这导致:
ERROR: invalid input syntax for type numeric: "salary + bonus"
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE
因为它应该是 return 一组 numeric
值。将其更改为以下内容:
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS SETOF numeric AS
$$
...
RETURN;
得到同样的错误。
图不错,说不定是个table正要return.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS TABLE(tot_inc numeric) AS
$$
...
得到同样的错误。
真的,任何变化都会产生那个结果。所以真的不确定如何让它工作。
查看 RESULT QUERY
、RESULT NEXT
或 RESULT QUERY EXECUTE
。
https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html
RESULT QUERY
不会工作,因为据我所知,它采用硬编码查询,不会接受变量。
RESULT NEXT
遍历每条记录,我认为这不会满足我的需要 table 并且看起来真的很慢......并且需要硬编码查询据我所知。
RESULT QUERY EXECUTE
听起来很有希望。
-- EXECUTE format('SELECT FROM %I', tbl)
-- INTO total_incentives_calc
-- USING query_string;
RETURN QUERY
EXECUTE format('SELECT FROM %I', tbl)
USING query_string;
并得到:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type numeric in column 1.
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY
应该是 returning numeric
.
最后,我可以让它工作,但它不会干。我不想用重复代码为每个 table 创建一堆单独的函数。我见过的大多数工作示例都在函数中包含整个查询,并且这样调用:
SELECT total_incentives(d_abc_2016, 'd_abc_2016');
所以任何额外的列都必须在函数中指定为:
EXECUTE format('SELECT employee_id...)
考虑到用户只能 运行 SELECT
查询,这确实不是一个选项。他们需要指定他们希望在查询中看到的任何其他列。
我发布了一个类似的问题,但被告知不清楚,所以希望这个较长的版本能更清楚地解释我正在尝试做的事情。
列名和表名不应用作 USING
子句传递的查询参数。
大概线路:
RETURN QUERY
EXECUTE format('SELECT FROM %I', tbl)
USING query_string;
应该是:
RETURN QUERY
EXECUTE format('SELECT %s FROM %I', query_string, tbl);
这个案例就是为什么过于 DRY 原则有时会出现问题的例子。如果您直接编写它,那么您的代码将更简单、更清晰并且可能更短。
动态 SQL 是上一个解决方案中的一个 - 不是第一个。仅当使用动态 sql 的代码比不使用动态 SQL.
的代码明显短时,才使用动态 SQL