2017-09-15 123 views
0

我的表格每年都包含相同类型的数据,但收集的数据略有不同,因为它们可能没有相同的字段。尝试在PL/PgSQL中创建动态查询字符串以在PostgreSQL中创建DRY函数9.6

d_abc_2016 
d_def_2016 
d_ghi_2016 
d_jkl_2016 

有用于每个表中的某些常数:company_idemployee_idsalary

但是,每个人可能有也可能没有这些字段用于计算总激励:bonus,commission,cash_incentives。还有很多,但只是以这些为例。全部numeric

我应该注意,在这一点上,用户只有能力运行SELECT报表。

我希望能够做的是这样的:

  1. 给用户在SELECT调用,并指定自己的领域,除了通话
  2. 传递表名的能力正在使用进入功能在条件逻辑用于确定查询字符串应该如何构造,为最终total_incentives计算除了传递整个表,这样一吨的参数没有被传递到函数

基本上这:

SELECT employee_id, salary, total_incentives(t, 'd_abc_2016') 
FROM d_abc_2016 t; 

所以被称为将计算total_incentives这是numeric该功能employee_id,也表明他们salary。但用户可能会选择添加其他字段来查看。

对于函数,因为total_incentives函数中使用的字段会因表而异,所以我需要创建逻辑来动态构造查询字符串。

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($2; 

    -- 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 $1 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 

因为它应该返回一组numeric值。将其更改为以下内容:

CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text) 
    RETURNS SETOF numeric AS 
$$ 
... 
    RETURN; 

获取相同的错误。

好吧,也许它是一个表格,它试图返回。

CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text) 
    RETURNS TABLE(tot_inc numeric) AS 
$$ 
... 

获取相同的错误。

真的,任何变化都会产生这样的结果。所以真的不知道如何让这个工作。

RESULT QUERY,RESULT NEXTRESULT QUERY EXECUTE

https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html

RESULT QUERY将无法​​工作,因为它从我可以告诉一个硬编码查询,不会在变量取。

RESULT NEXT遍历每个记录,我不认为会适合我的需求和看起来这将是很慢...它需要从我可以告诉一个硬编码查询。

RESULT QUERY EXECUTE听起来很有希望。

-- EXECUTE format('SELECT $1 FROM %I', tbl) 
-- INTO total_incentives_calc 
-- USING query_string; 

RETURN QUERY 
    EXECUTE format('SELECT $1 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 

应该返回numeric

最后,我可以得到这个工作,但它不会干。我宁愿不使用重复代码为每个表创建一堆单独的函数。大部分时间我都看到了工作的例子有一个函数整个查询,被称为像这样:

SELECT total_incentives(d_abc_2016, 'd_abc_2016'); 

因此,任何附加列必须在功能作为指定:鉴于

EXECUTE format('SELECT employee_id...) 

用户将只能在查询中运行SELECT,这实在不是一种选择。他们需要指定他们希望在查询中看到的任何附加列。

我已经发布了一个类似的问题,但被告知它不清楚,所以希望这个更长的版本将更清楚地解释我正在尝试做什么。

回答

1

列名和表名不应该用作USING子句传递的查询参数。

大概行:

RETURN QUERY 
    EXECUTE format('SELECT $1 FROM %I', tbl) 
    USING query_string; 

应该是:

RETURN QUERY 
    EXECUTE format('SELECT %s FROM %I', query_string, tbl); 

这种情况是例子,为什么太DRY原则有时是有问题的。如果你直接编写它,那么你的代码将会更简单,更简洁并且可能更短。

动态SQL是最后一个解决方案 - 不是第一个。只有当动态SQL比不使用动态SQL时代码短得多时才使用动态SQL。

+0

很酷,得到了查询运行。谢谢您的帮助!现在整理并防止它将返回的记录数量乘以总记录数量。 – sockpuppet