PostgreSQL:Select 相关子查询中的动态列

PostgreSQL: Select dynamic column in correlated subquery

我正在使用实体-属性-值 (EAV) 模式来存储 'overrides' 目标对象。即有3个table:

我想要做的是 select 覆盖实体 table 中 'overridden' 列的值。因此,需要在 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 标识符,而只是 returns 列名称,例如 'col1'、'col2'等,而不是使用列名称查询实体。

我知道这是动态的 SQL,但我对 PL/pgSQL 还很陌生,无法理解,因为它是 correlated/lateral 加入的。另外,这是否可能,因为列类型不是均匀类型的?请注意覆盖 table 中的所有 'values' 都存储为字符串以解决此问题。

任何帮助将不胜感激!

您可以使用 PL/pgSQL 动态请求列。我假设以下简化的数据库结构(在这个例子中所有原始值和覆盖值都是 "character varying" 因为我没有找到任何进一步的类型信息):

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;