Postgresql 在Postgres中有效地循环通过一个变量

Postgresql 在Postgres中有效地循环通过一个变量,postgresql,union,variadic-functions,Postgresql,Union,Variadic Functions,我在博士后中有下表 通常会像下面这样填充 id日访问通行证 1星期一{11,13,19}{13,17} 2星期二{7,9}{11,13,19} 3星期三{2,5,21}{21,27} 4星期四{3,11,39}{21,19}` 为了在一段时间内获得访问或通行证ID,我编写了以下函数 CREATE OR REPLACE FUNCTION day_entries(p_column TEXT,VARIADIC ids int[]) RETURNS bigint[] AS $$ DECLARE res

我在博士后中有下表

通常会像下面这样填充

id日访问通行证
1星期一{11,13,19}{13,17}
2星期二{7,9}{11,13,19}
3星期三{2,5,21}{21,27}
4星期四{3,11,39}{21,19}`
为了在一段时间内获得
访问
通行证
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 = $1',p_column) USING ids[i] INTO hold;
    result := unnest(result) UNION unnest(hold);
  END LOOP;
  RETURN result;
END;
$$
LANGUAGE 'plpgsql';
它与后续调用
日项目('visions',1,2,3)
返回一起工作

{11,9,19,21,5,13,2,7}

虽然这项工作可以完成,但我担心,基于我一天的博士后写作经验,我在这个过程中遇到了一个或多个低效的问题。能否以某种方式简化该功能


另一个问题与其说是问题,不如说是好奇——结果中元素的顺序似乎与所触及的三行中
访问
条目的顺序无关。虽然就我而言,这不是一个问题,但我很想知道为什么会发生。

您可以在一条语句中完成取消测试和聚合,无需循环。您可以对数组使用
ANY
操作符来选择所有匹配的行

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($1))', p_column) 
    USING p_ids -- pass the whole array as a parameter
    INTO result;

  RETURN result;
END;
$$
LANGUAGE plpgsql;

与你的问题无关,但我认为你的设计走错了路。虽然数组在一开始对初学者来说可能很有趣,但它们应该很少使用

如果你发现自己对事物感到不安,并且前后聚合,这是一个强有力的迹象,表明某些事物可以改进

我会将您的表分为两个表,一个表存储“日”信息,另一个表存储访问和通过,并在同一个表中用一列区分这两种信息。然后查找访问就很简单,只需添加一个where
…=访问“
,而不必处理(速度慢且容易出错)动态SQL

在不了解更多细节的情况下,我可能会创建如下表:

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\u id
甚至可能是您没有向我们展示的另一个表的外键,这也是非规范化表无法实现的

获取特定日期的所有访问,非常简单:_

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';

您可以在一条语句中完成取消测试和聚合,无需循环。您可以对数组使用
ANY
操作符来选择所有匹配的行

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($1))', p_column) 
    USING p_ids -- pass the whole array as a parameter
    INTO result;

  RETURN result;
END;
$$
LANGUAGE plpgsql;

与你的问题无关,但我认为你的设计走错了路。虽然数组在一开始对初学者来说可能很有趣,但它们应该很少使用

如果你发现自己对事物感到不安,并且前后聚合,这是一个强有力的迹象,表明某些事物可以改进

我会将您的表分为两个表,一个表存储“日”信息,另一个表存储访问和通过,并在同一个表中用一列区分这两种信息。然后查找访问就很简单,只需添加一个where
…=访问“
,而不必处理(速度慢且容易出错)动态SQL

在不了解更多细节的情况下,我可能会创建如下表:

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\u id
甚至可能是您没有向我们展示的另一个表的外键,这也是非规范化表无法实现的

获取特定日期的所有访问,非常简单:_

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';