PostgreSQL: выбор динамического столбца в коррелированном подзапросе - PullRequest
0 голосов
/ 10 мая 2018

Я использую шаблон Entity-Attribute-Value (EAV) для хранения «переопределений» для целевых объектов. То есть есть три таблицы:

  • Сущность , содержит целевые записи
  • Атрибут , содержит имена столбцов «перезаписываемых» столбцов в таблице Entity
  • Переопределить , содержит записи EAV

То, что я хотел бы сделать, это выбрать Переопределения вместе со значением столбца «переопределено» из таблицы сущностей. Таким образом, требуется динамическое использование имени атрибута в SQL.

Моя наивная попытка (PostgreSQL) SQL:

SELECT
  OV.entity_id        as entity,
  AT.name             as attribute,
  OV.value            as value,
  ENT.base_value      as base_value
FROM "override" AS OV
  LEFT JOIN "attribute" as AT
    ON (OV.attribute_id = AT.id)
  LEFT JOIN LATERAL (
            SELECT
              id,
              AT.name as base_value -- AT.name doesn't resolve to a SQL identifier
            FROM "entity"
            ) AS ENT
    ON ENT.id = OV.entity_id;

Это не работает, поскольку AT.name не разрешается в идентификатор SQL и просто возвращает имена столбцов, такие как 'col1', 'col2' и т. Д., А не запрашивает Entity с именем столбца.

Я знаю, что это динамический SQL, но я довольно новичок в PL / pgSQL и не могу понять, как он коррелирован / латерально соединен. Кроме того, возможно ли это, поскольку типы столбцов не являются однородно типизированными? Обратите внимание, что все «значения» в таблице переопределения хранятся в виде строк, чтобы обойти эту проблему.

Любая помощь будет наиболее ценной!

1 Ответ

0 голосов
/ 10 мая 2018

Вы можете использовать PL / pgSQL для динамического запроса столбцов.Я предполагаю следующую упрощенную структуру базы данных (в этом примере все исходные значения и значения overide являются «изменяющимися символами», поскольку я не нашел никакой дополнительной информации о типе):

CREATE TABLE public.entity (
   id integer NOT NULL DEFAULT nextval('entity_id_seq'::regclass),   
   attr1 character varying,   
   attr2 character varying,   
   <...>
   CONSTRAINT entity_pkey PRIMARY KEY (id) 
)

CREATE TABLE public.attribute (   
   id integer NOT NULL DEFAULT nextval('attribute_id_seq'::regclass),
   name character varying,
   CONSTRAINT attribute_pkey PRIMARY KEY (id) 
)

CREATE TABLE public.override (   
   entity_id integer NOT NULL,
   attribute_id integer NOT NULL,
   value character varying,
   CONSTRAINT override_pkey PRIMARY KEY (entity_id, attribute_id),
   CONSTRAINT override_attribute_id_fkey FOREIGN KEY (attribute_id)
      REFERENCES public.attribute (id),
   CONSTRAINT override_entity_id_fkey FOREIGN KEY (entity_id)
      REFERENCES public.entity (id))

С функцией PL / pgSQL

create or replace function get_base_value(
  entity_id integer,
  column_identifier character varying
)
returns setof character varying
language plpgsql as $$
declare
begin
  return query execute 'SELECT "' || column_identifier || '" FROM "entity" WHERE "id" = ' || entity_id || ';';
end $$;

Вы можете использовать почти точно ваш запрос:

SELECT
      OV.entity_id        as entity,
      AT.name             as attribute,
      OV.value            as value,
      ENT.get_base_value  as base_value
    FROM "override" AS OV
      LEFT JOIN "attribute" as AT
        ON (OV.attribute_id = AT.id)
      LEFT JOIN LATERAL (
        SELECT id, get_base_value FROM get_base_value(OV.entity_id, AT.name)
      ) AS ENT
    ON ENT.id = OV.entity_id;
...