PostgreSQL:Select 相关子查询中的动态列
PostgreSQL: Select dynamic column in correlated subquery
我正在使用实体-属性-值 (EAV) 模式来存储 'overrides' 目标对象。即有3个table:
- 实体,包含目标记录
- 属性,包含实体table
中'overridable'列的列名
- 覆盖,包含 EAV 记录
我想要做的是 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;
我正在使用实体-属性-值 (EAV) 模式来存储 'overrides' 目标对象。即有3个table:
- 实体,包含目标记录
- 属性,包含实体table 中'overridable'列的列名
- 覆盖,包含 EAV 记录
我想要做的是 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;