PostgreSQL:在相关子查询中选择动态列

时间:2018-05-10 09:17:44

标签: sql postgresql plpgsql dynamic-sql

我正在使用实体 - 属性 - 值(EAV)模式来存储目标对象的“覆盖”。也就是说,有三个表:

  • 实体,包含目标记录
  • 属性,包含实体表中“可覆盖”列的列名
  • 覆盖,包含EAV记录

我想要做的是选择Overrides以及Entity表中''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标识符,只返回列名,例如'col1','col2'等,而不是使用列名查询实体。

我知道这是动态SQL,但我对PL / pgSQL很新,并且因为它是相关/横向连接而无法弄清楚。另外,这是否可能,因为色谱柱类型不均匀?请注意,覆盖表中的所有“值”都存储为字符串以解决此问题。

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:2)

您可以使用PL / pgSQL动态请求列。我假设以下简化的数据库结构(所有原始值和覆盖值都是"字符变化"在此示例中,因为我没有找到任何其他类型信息):

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;