在 Postgres 中高效地循环 VARIADIC
Efficiently looping through a VARIADIC in Postgres
我在 Postgres
中有以下 table
通常会像下面这样填充
id day visits passes
1 Monday {11,13,19} {13,17}
2 Tuesday {7,9} {11,13,19}
3 Wednesday {2,5,21} {21,27}
4 Thursday {3,11,39} {21,19}`
为了在一定天数内获取 visit
或 passes
id,我编写了以下函数
CREATE OR REPLACE FUNCTION day_entries(p_column TEXT,VARIADIC ids int[]) RETURNS bigint[] AS
$$
DECLARE result bigint[];
DECLARE hold bigint[];
BEGIN
FOR i IN 1 .. array_upper(ids,1) LOOP
execute format('SELECT %I FROM days WHERE id = ',p_column) USING ids[i] INTO hold;
result := unnest(result) UNION unnest(hold);
END LOOP;
RETURN result;
END;
$$
LANGUAGE 'plpgsql';
与后续调用 day_entries('visits',1,2,3)
一起返回
{11,9,19,21,5,13,2,7}
虽然它完成了工作,但我担心根据我编写 Postgres 函数的一天旧知识,我在该过程中遇到了一个或多个低效问题。可以通过某种方式使该功能更简单吗?
另一个问题与其说是问题,不如说是好奇 - 结果中元素的顺序似乎与所触及的三行中 visits
条目的顺序无关。尽管就我而言这不是问题,但我很想知道为什么会这样。
您可以在一条语句中完成取消嵌套和聚合,无需循环。您可以将 ANY
运算符与数组一起使用 select 所有匹配的行。
CREATE OR REPLACE FUNCTION day_entries(p_column TEXT, variadic p_ids int[])
RETURNS bigint[] AS
$$
DECLARE
result bigint[];
BEGIN
execute
format('SELECT array(select unnest(%I) from days WHERE id = any())', p_column)
USING p_ids -- pass the whole array as a parameter
INTO result;
RETURN result;
END;
$$
LANGUAGE plpgsql;
与您的问题无关,但我认为您的设计走错了路。虽然数组在一开始对初学者来说可能看起来很有趣,但它们应该很少使用。
如果您发现自己反复取消嵌套和聚合事物,这强烈表明某些事情可以改进。
我会将您的 table 分成两个 table,一个存储 "day" 信息,一个存储访问 和 在相同的 table 中传递一个区分两者的列。然后查找访问就像添加 where ... = 'visit'
一样简单,而不必处理(缓慢且容易出错的)动态 SQL。
在不知道更多细节的情况下,我可能会像这样创建 table:
create table days
(
id integer not null primary key,
day character varying(9) not null
);
create table event
(
day_id integer not null references days,
event_id integer not null,
event_type varchar(10) not null check (event_type in ('visit', 'pass'))
);
event_id
甚至可能是外键,无法对您尚未向我们展示的另一个 table 进行键控 - 这又是您无法使用非规范化 table 真正做到的事情。
获取特定日期的所有访问,就这么简单 as:_
select event_id
from event
where day_id in (1,2)
and event_type = 'visit';
或者如果您确实需要它作为数组:
select array_agg(event_id)
from event
where day_id in (1,2)
and event_type = 'visit';
我在 Postgres
中有以下 table通常会像下面这样填充
id day visits passes
1 Monday {11,13,19} {13,17}
2 Tuesday {7,9} {11,13,19}
3 Wednesday {2,5,21} {21,27}
4 Thursday {3,11,39} {21,19}`
为了在一定天数内获取 visit
或 passes
id,我编写了以下函数
CREATE OR REPLACE FUNCTION day_entries(p_column TEXT,VARIADIC ids int[]) RETURNS bigint[] AS
$$
DECLARE result bigint[];
DECLARE hold bigint[];
BEGIN
FOR i IN 1 .. array_upper(ids,1) LOOP
execute format('SELECT %I FROM days WHERE id = ',p_column) USING ids[i] INTO hold;
result := unnest(result) UNION unnest(hold);
END LOOP;
RETURN result;
END;
$$
LANGUAGE 'plpgsql';
与后续调用 day_entries('visits',1,2,3)
一起返回
{11,9,19,21,5,13,2,7}
虽然它完成了工作,但我担心根据我编写 Postgres 函数的一天旧知识,我在该过程中遇到了一个或多个低效问题。可以通过某种方式使该功能更简单吗?
另一个问题与其说是问题,不如说是好奇 - 结果中元素的顺序似乎与所触及的三行中 visits
条目的顺序无关。尽管就我而言这不是问题,但我很想知道为什么会这样。
您可以在一条语句中完成取消嵌套和聚合,无需循环。您可以将 ANY
运算符与数组一起使用 select 所有匹配的行。
CREATE OR REPLACE FUNCTION day_entries(p_column TEXT, variadic p_ids int[])
RETURNS bigint[] AS
$$
DECLARE
result bigint[];
BEGIN
execute
format('SELECT array(select unnest(%I) from days WHERE id = any())', p_column)
USING p_ids -- pass the whole array as a parameter
INTO result;
RETURN result;
END;
$$
LANGUAGE plpgsql;
与您的问题无关,但我认为您的设计走错了路。虽然数组在一开始对初学者来说可能看起来很有趣,但它们应该很少使用。
如果您发现自己反复取消嵌套和聚合事物,这强烈表明某些事情可以改进。
我会将您的 table 分成两个 table,一个存储 "day" 信息,一个存储访问 和 在相同的 table 中传递一个区分两者的列。然后查找访问就像添加 where ... = 'visit'
一样简单,而不必处理(缓慢且容易出错的)动态 SQL。
在不知道更多细节的情况下,我可能会像这样创建 table:
create table days
(
id integer not null primary key,
day character varying(9) not null
);
create table event
(
day_id integer not null references days,
event_id integer not null,
event_type varchar(10) not null check (event_type in ('visit', 'pass'))
);
event_id
甚至可能是外键,无法对您尚未向我们展示的另一个 table 进行键控 - 这又是您无法使用非规范化 table 真正做到的事情。
获取特定日期的所有访问,就这么简单 as:_
select event_id
from event
where day_id in (1,2)
and event_type = 'visit';
或者如果您确实需要它作为数组:
select array_agg(event_id)
from event
where day_id in (1,2)
and event_type = 'visit';