我目前有一个类似下表的数据库模式:

CREATE TABLE Measures(
    expId SERIAL,
    iteration INT NOT NULL,
    value float4 NOT NULL,
    PRIMARY KEY(expId, iteration)
);

因此,一个包含各种测量值的表,重复n次. 但是,因为我们拥有比最初预期更多的数据,所以我想使用新的表布局,而不是使用数组列,这总体上提供了更好的性能(已经进行了测试和基准测试):

CREATE TABLE TmpMeasures(
    expId SERIAL PRIMARY KEY,
    values float4[] NOT NULL
);

我现在的问题是如何将旧数据转换为新格式. 数据可能如下所示.并不是说迭代不会 始终生成所有数据,因此最终数组中可能有NULL个值:

INSERT INTO Measures (expId, iteration, value)
VALUES
  (1, 1, 1.1), (1, 2, 1.2), (1, 3, 1.3),
  (2, 1, 2.1),                           (2, 4, 2.4),
  (3, 1, 3.1),                           (3, 4, 3.4);

转换可以通过两个步骤完成,大致如下,首先为实验创建数组,然后填充迭代值:

INSERT INTO TmpMeasures(expId, values)
  SELECT expId, '{}'::float4[] 
  FROM Measures
  ON CONFLICT DO NOTHING;

UPDATE TmpMeasures tm
  SET values[iteration] = m.value
  FROM Measures m WHERE tm.expId = m.expId;

然而,我现在的问题是,这UPDATE人实际上似乎只进行了第一次迭代,即iteration = 1次. 我不太明白为什么会这样.

我怀疑,对于values[iteration],其他方法将try 按expid分组,按迭代排序,并将其聚合到一个数组中.

不幸的是,数据并不完美,但迭代应该是一致的.

所以,下面的方法看起来很管用,但它非常慢,我不太明白为什么首先需要它.

DO
$do$
BEGIN 
   FOR i IN 1..(SELECT max(iteration) FROM Measures m) LOOP
      UPDATE TmpMeasures tm
      SET values[i] = m.value
      FROM Measures m
      WHERE
        tm.expId = m.expId AND
        m.iteration=i;
   END LOOP;
END
$do$;

为什么"普通"的UPDATE语句还不够呢? 答:多亏了@Zegarek下面的指针,这确实是UPDATE的预期行为,它只接受FROM子句中的一行.

因此,在这一点上,对我来说更相关的问题是,是否有更好的数据转换方法,例如为expId=3生成[3.1, NULL, NULL, 3.4].

推荐答案

  1. 对于介于两者之间的所有内容,将最小/最大值设置为iterations到之后的generate_series().
  2. distinct dExpIdleft join加上迭代得到102,缺少values
  3. 然后是array_agg(values order by i)美元.
  4. 不是update,而是create table asadd the constraints later:demo
create table if not exists TmpMeasures as
with min_max_iteration as (
  select min(iteration), 
         max(iteration) from Measures)
,distinct_expIds as (
  select distinct expId from Measures)
select expId,
       array_agg(value order by iteration) as "values"
from min_max_iteration
cross join distinct_expIds
cross join generate_series(min,max) g(iteration)
left join Measures using(iteration,ExpId)
group by ExpId;
expId values
1 {1.1,1.2,1.3,NULL}
2 {2.1,NULL,NULL,2.4}
3 {3.1,NULL,NULL,3.4}

之后添加约束:(identity列等于encouraged除以serial).

select max(expId)+1 from TmpMeasures;--4

alter table TmpMeasures 
   add primary key (expId)
  ,alter column expId add generated by default as identity(start with 4)
  ,alter column values set not null;

在此之前需要SELECT,因为您不能在alter table中将其作为标量子 Select 运行.The demo还显示了如何判断每个步骤,以及如何将其作为update运行.

如果您有一些在所有并发Measures中缺失的迭代,并且您希望在所有数组中跳过该字段,则可以使用where i in ()来筛选generate_series() g(i).

如果你在寻找性能,在28k samples(60个expId,每个800个iteration,40%的机会错过一个迭代),这需要0.25s:performance demo.循环的PL/pgSQL更新需要15.80s.In v16,差值增加到0.08srunning out of resources and failing.


The UPDATE t SET arr[m.k]=m.v FROM m WHERE m.id=t.id idea

作为mentioned in the comment,它是update's fault,它不会起作用:

目标行不应联接到另一个表(S)中的多行.如果是这样,那么只有一个联接行将用于更新目标行,但不能很容易地预测将使用哪一个.

即使下标寻址数组值中的特定元素,update也不会try 使用从源匹配的多个未确定行-实际上,对于每个id,只有一对(key,value)到达应用,其余的被丢弃.

没有order by,所有的东西都是无序的,update甚至不提供order by.如果这样做有效,您将看到由于将多个row updates应用于同一行的不可预测的顺序而导致的异常.在您的例子中,每个row update都以数组值中的不同索引为目标,所以这并不重要,因为它们不会相互干扰,但这是not always the case.

Postgresql相关问答推荐

PostgreSQL plpgsql函数问题

Pogresql性能中的枚举与文本数据类型

为什么 Postgres 中的 now() 和 now()::timestamp 对于 CET 和 CEST 时区如此错误?

在 jOOQ 中使用 $$ 引用字符串

需要用 jack/pgx 更新 golang 中复合类型的 PSQL 行

gorm 创建并返回值 many2many

Psycopg2 在大型 Select 查询中耗尽内存

是否可以使用 pgAdmin4 自动格式化/美化 SQL 查询?

postgresql查询中的正则表达式不区分大小写

Heroku PGError:operator does not exist: character varying = integer

需要将整个 postgreSQL 数据库加载到 RAM 中

在查询中的复合外键/主键列上连接表

PostgreSQL 权限解释

为 Django Postgres 连接强制 SSL

在 Windows 10 中执行时,Docker 容器关闭并给出data directory has wrong ownership错误

Postgresql varchar 是否使用 unicode 字符长度或 ASCII 字符长度计算?

Windows 上的 GeoDjango:Could not find the GDAL library" / "OSError: [WinError 126] The specified module could not be found

Postgres Hstore 与 Redis - 性能方面

如何减少存储(缩减)我的 RDS 实例?

在 docker-compose up 后加载 Postgres 转储