Функция PostgreSQL со свободным типом возвращаемых данных - PullRequest
0 голосов
/ 02 октября 2018

Вот основная таблица с данными:

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
);

Содержит несколько столбцов для каждого значения, поскольку значения могут быть разных типов данных.

Какой столбец будет взят, зависит от 'столбец data_type 'второй таблицы, связанный с value_id внешним ключом:

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)
);

Теперь я пытаюсь сократить огромный запрос SQL, создав функцию, которая может возвращать либо text_value, либо numeric_value, либо bool_value или dt_value, но обнаружил, что я должен явно определить возвращаемый тип данных:

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;

Эта функция еще не завершена, поэтому, пожалуйста, не жалуйтесь об этом здесь.

Iне люблю конвертировать все в текст.Типы данных должны быть сохранены.

Когда невозможно создать функцию с переменным типом выходных данных, тогда я должен сгенерировать полный 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;

Теперь я хотел быиметь универсальную функцию для всех типов данных.

PS Зачем использовать EAV ?

Потому что раньше это была обычная таблица, а ЭТО было кошмаромпотому что столбцы нужно было автоматически создавать время от времени.Иногда обычный пользователь не имеет прав на выполнение команд DDL.

У меня полностью динамически настраиваемое программное обеспечение, и количество значений, которые необходимо сохранить в таблице, зависит от конфигурационного файла этого программного обеспечения.Я выбрал эту модель, чтобы упростить ведение базы данных и передачу данных между системами.

Кроме того, я мог бы сказать, что различные значения должны сохраняться через свои интервалы.

Итак, я выигралне сдавайтесь EAV , поскольку он уже работает более эффективно, чем прежняя обычная таблица.

Нет необходимости представлять эту вертикальную структуру в виде горизонтальных строк данных, за исключением создания экспорта для устаревших систем.,И это как раз тема моего вопроса.

1 Ответ

0 голосов
/ 02 октября 2018

Используйте полиморфную функцию, пример:

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 в качестве аргумента для определения возвращаемого типа.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...