[英]How to unnest multiple array columns while preserving the order of the array
[英]Unnest multiple array columns stored as text in lockstep
我在 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)
);
數據如下:
目標ID | 機器ID | 日期讀取 | State | 時間 |
---|---|---|---|---|
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' |
重要的部分是state
和ftime
。 我需要取消嵌套元素並保持它們的順序。 這會產生步驟。
例如,第一行將是:
目標ID | 機器ID | 日期讀取 | State | 時間 | 步 |
---|---|---|---|---|---|
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個 |
ORDER 很重要,因為 FTIME 850 ms 始終是第一個並且在 STEP 中獲得值 0,然后是第二個 930 ms 並獲得第 1 步,最后 32000 ms 是第三個並獲得第 2 步。
目前,我通過首先使用string_to_array()
將文本轉換為數組,然后使用unnnest()
最后使用row_number()
分配步驟編號來解決此問題。
這項工作非常出色 - 除了有時某些索引出現故障。 第一行像這樣:
目標ID | 機器ID | 日期讀取 | State | 時間 | 步 |
---|---|---|---|---|---|
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 過程),但如果我執行 select 檢查特定行有錯誤,它顯示完美。 所以我假設 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
有沒有更好的方法來做到這一點?
一種優雅的方法是對LATERAL
子查詢中的多個輸入 arrays 使用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<> 在這里擺弄
由於state
和ftime
可以是NULL
,我使用LEFT JOIN... ON true
來在結果中保留這些行。
看:
當然,你真正應該做的是:
現代 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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.