在锁步中取消嵌套存储为文本的多个数组列

Posted

技术标签:

【中文标题】在锁步中取消嵌套存储为文本的多个数组列【英文标题】:Unnest multiple array columns stored as text in lockstep 【发布时间】:2021-09-29 21:52:26 【问题描述】:

我在 Postgres 9.6 中有下表:

CREATE TABLE some_tbl(
  target_id integer NOT NULL
, machine_id integer NOT NULL
, dateread timestamp without time zone NOT NULL
, state text
, ftime text
, CONSTRAINT pk_sometable PRIMARY KEY (target_id, machine_id, dateread)
 );

使用如下数据:

targetID MachineID DateRead State FTime
60000 30 '2021-09-29 15:20:00' '0|1|0' '850|930|32000'
60000 31 '2021-09-29 16:35:13' '0|0|0' '980|1050|30000'

重要的部分是stateftime。我需要取消嵌套元素并保持它们的顺序。这会生成步骤。

例如,第一行将是:

targetID MachineID DateRead State FTime Step
60000 30 '2021-09-29 15:20:00' '0' '850' 0
60000 30 '2021-09-29 15:20:00' '1' '930' 1
60000 30 '2021-09-29 15:20:00' '0' '32000' 2

顺序很重要,因为 FTIME 850 ms 始终是第一个并在 STEP 中获得值 0,然后是 930 ms 是第二个并获得第 1 步,最后 32000 ms 是第三个并获得第 2 步。

目前,我首先使用string_to_array() 将文本转换为数组,然后使用unnnest(),最后使用row_number() 分配步骤编号来解决此问题。

这项工作非常出色 - 除了有时某些索引出现乱序。第一行是这样的:

targetID MachineID DateRead State Ftime Step
60000 30 '2021-09-29 15:20:00' '1' '930' 0
60000 30 '2021-09-29 15:20:00' '0' '32000' 1
60000 30 '2021-09-29 15:20:00' '0' '850' 2

我对数千条记录执行此操作,实际上一切正常,但后来我必须进行统计,需要获取最小值、最大值、平均值并得到错误的值,所以我检查并看到索引是否错误(我移动统计有一个庞大的 ETL 过程),但如果我执行选择检查有错误的特定行,它显示完美。所以我假设 row_number 有时会出现索引问题,这是非常随机的。

这是我使用的 SQL:

SELECT foo.target_id,
            dateread,
            foo.machine_id,
            foo.state,
            foo.ftime::integer,
            (row_number() OVER (PARTITION BY foo.dateread, foo.machine_id, foo.target_id)) - 1 AS step
           FROM ( SELECT target_id,
                machine_id,
                dateread
                unnest(string_to_array(state, '|'::text))::integer AS state,
                unnest(string_to_array(ftime, '|'::text))::integer AS tiempo
               FROM some_table
               WHERE target_id IN (6000) AND dateread = '2021-06-09')foo

有更好的方法吗?

【问题讨论】:

请提供精确的表定义(CREATE TABLE 显示数据类型和约束的语句)和 Postgres 版本。 嗨,这是基表:'CREATE TABLE schema.sometable ( target_id integer NOT NULL, machine_id integer NOT NULL, dateread timestamp without time zone NOT NULL, state text, ftime text, CONSTRAINT pk_sometable PRIMARY KEY (target_id, machine_id, dateread)) WITH (OIDS=FALSE); ALTER TABLE schema.sometable OWNER TO postgres;'而且我正在使用 Postgres 9.6。谢谢@ErwinBrandstetter Edit 将表定义放入问题中,请勿放入评论中。 好的,我做到了。 ty 【参考方案1】:

一种优雅的方法是在LATERAL 子查询中对多个输入数组使用unnest() 的特殊实现并附加WITH ORDINALITY

SELECT t.target_id, t.dateread, t.machine_id, u.state, u.tiempo
     , ord - 1 AS step
FROM   tbl t
LEFT   JOIN LATERAL unnest(string_to_array(state, '|')::int[]
                         , string_to_array(ftime, '|')::int[]) WITH ORDINALITY AS u(state, tiempo, ord) ON true
WHERE  target_id = 60000
AND    dateread = '2021-09-29 15:20:00'   -- adapted
ORDER  BY t.target_id, t.dateread, t.machine_id, step;

db小提琴here

由于stateftime 可以是NULL,我使用LEFT JOIN ... ON true 将这些行保留在结果中。

见:

What is the difference between LATERAL JOIN and a subquery in PostgreSQL? Unnest multiple arrays in parallel PostgreSQL unnest() with element number What is the expected behaviour for multiple set-returning functions in SELECT clause?

当然,你真正应该做的是这样的:

    与设计数据库的人解除好友关系。 (PC 版我的真实建议。) 安装当前的 Postgres 版本。见:https://www.postgresql.org/support/versioning/ 使用适当的关系设计创建一个新数据库。 迁移您的数据。 (并保留原始文件的备份以确保安全。) 烧掉旧数据库,再也不提了。

现代 Postgres 中适当的(规范化)关系设计可能如下所示:

CREATE TABLE tbl (
  tbl_id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY
, target_id integer NOT NULL
, machine_id integer NOT NULL
, read_timestamp timestamp with time zone NOT NULL
, CONSTRAINT tbl_uni UNIQUE (target_id, machine_id, read_timestamp)
);

CREATE TABLE tbl_step (
  tbl_id int REFERENCES tbl ON DELETE CASCADE
, step int NOT NULL
, state int NOT NULL
, tiempo int NOT NULL
, CONSTRAINT tbl_step_pkey PRIMARY KEY (tbl_id, step)
);

那么你的查询就是:

SELECT *
FROM   tbl 
LEFT   JOIN tbl_step USING (tbl_id);

【讨论】:

非常感谢 Erwin,我正在检查您的 sql 并且效果很好,甚至比较前后的大量负载并发现超过 30k 的差异。我一直在检查差异,到目前为止,一切都很完美。真的非常感谢大师。周末愉快。

以上是关于在锁步中取消嵌套存储为文本的多个数组列的主要内容,如果未能解决你的问题,请参考以下文章

BigQuery - 如何取消嵌套多个数组,并从一列分配值?

如果 MATLAB Rb2020 中的行和列维度不一致,如何取消嵌套具有嵌套数据和文本内容的元胞数组?

如何在查询中取消嵌套嵌套表的集合?

并行取消嵌套多个数组

如何在 BigQuery 中取消嵌套多个数组?

可变数组(PLSQL)