具有松散返回数据类型的PostgreSQL函数

时间:2018-10-02 08:18:07

标签: postgresql function plpgsql entity-attribute-value

以下是包含数据的主表:

CREATE TABLE my_report
(
  id               serial                      NOT NULL primary key,
  report_timestamp timestamp without time zone NOT NULL,
  value_id         integer                     NOT NULL,
  text_value       character varying(255),
  numeric_value    double precision,
  bool_value       boolean,
  dt_value         timestamp with time zone,
  CONSTRAINT my_report_fkey_valdef FOREIGN KEY (value_id)
      REFERENCES public.my_value_defs (value_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE RESTRICT
);

每个值包含几列,因为值可以是不同的数据类型。

将采用哪个列取决于与value_id外键链接的第二个表的'data_type'列:

CREATE TABLE my_value_defs
(
  value_id     serial                NOT NULL primary key,
  value_name   character varying(50) NOT NULL,
  data_type    integer               NOT NULL,
  CONSTRAINT my_value_defs_pkey PRIMARY KEY (value_id),
  CONSTRAINT my_value_defs_value_name_key UNIQUE (value_name)
);

现在,我试图通过创建一个可以返回text_valuenumeric_valuebool_valuedt_value的函数来缩短一个庞大的SQL查询,但是发现我必须明确定义返回的数据类型:

CREATE OR REPLACE FUNCTION public.rep_dta_val(
    val_id integer,
    dt timestamp with time zone,
    timeout integer)
  RETURNS timestamp with time zone AS -- ********** HERE **********
$BODY$SELECT
   r.dt_value
FROM
   my_report r
WHERE
   r.value_id = val_id
   AND r.report_timestamp BETWEEN
      dt - make_interval(secs := timeout)
      AND dt
ORDER BY
   r.report_timestamp desc
LIMIT 1;$BODY$
  LANGUAGE sql VOLATILE
  COST 100;

此功能尚未完成,因此请不要在这里抱怨。

我不喜欢将所有内容都转换为文本。数据类型应保留。

如果无法创建具有可变输出数据类型的函数,则必须在应用程序中生成一个完整的全长SQL查询,如下所示:

SELECT
(SELECT r.text_value    as acc_right     FROM my_report r WHERE r.value_id =  3 AND r.report_timestamp BETWEEN now() - INTERVAL '60 seconds' AND now() ORDER BY r.report_timestamp desc LIMIT 1),
(SELECT r.numeric_value as h_angle       FROM my_report r WHERE r.value_id =  4 AND r.report_timestamp BETWEEN now() - INTERVAL '60 seconds' AND now() ORDER BY r.report_timestamp desc LIMIT 1),
(SELECT r.text_value    as vol_flow      FROM my_report r WHERE r.value_id = 25 AND r.report_timestamp BETWEEN now() - INTERVAL '60 seconds' AND now() ORDER BY r.report_timestamp desc LIMIT 1),
(SELECT r.numeric_value as draft_mid     FROM my_report r WHERE r.value_id = 57 AND r.report_timestamp BETWEEN now() - INTERVAL '60 seconds' AND now() ORDER BY r.report_timestamp desc LIMIT 1),
(SELECT r.dt_value      as eta_timestamp FROM my_report r WHERE r.value_id = 58 AND r.report_timestamp BETWEEN now() - INTERVAL '60 seconds' AND now() ORDER BY r.report_timestamp desc LIMIT 1);

上面提到的查询恰好满足了我的需要,但是太长了,所以我创建了几个函数,每个数据类型一个,并像这样使用它们:

SELECT
rep_txt_val( 3, now(), 60) as acc_right,
rep_num_val( 4, now(), 60) as h_angle,
rep_txt_val(25, now(), 60) as vol_flow,
rep_num_val(57, now(), 60) as draft_mid,
rep_dta_val(58, now(), 60) as eta_timestamp;

现在,我想为所有数据类型提供通用功能。

P.S。为什么要使用EAV

因为它以前是传统的表格,所以是噩梦,因为需要不时自动创建列。有时,常规用户没有执行DDL命令的权限。

我有一个完全动态的可配置软件,需要保存到表中的值量取决于该软件的配置文件。我选择此模型是为了简化数据库维护和系统之间的数据传输。

此外,我可以说应该以自己的间隔保存各种值。

因此,我不会放弃EAV,因为它已经比以前的常规表格更有效。

除了为传统系统生成导出文件外,无需将此垂直结构表示为水平数据行。这正是我的问题的主题。

1 个答案:

答案 0 :(得分:2)

使用polymorphic function,示例:

create or replace function func(anyelement)
returns anyelement language plpgsql as $$
begin
    raise notice '%', pg_typeof($1)::text;
    case pg_typeof($1)::text
        when 'text' then return 'some text';
        when 'numeric' then return 1.23;
        when 'timestamp without time zone' then return now();
        else return $1;
    end case;
end $$;

select func(null::text), func(null::numeric), func(null::timestamp)

NOTICE:  text
NOTICE:  numeric
NOTICE:  timestamp without time zone

   func    | func |            func            
-----------+------+----------------------------
 some text | 1.23 | 2018-10-02 14:51:51.407031
(1 row)

请注意,必须使用anyelement作为确定返回类型的参数。