簡體   English   中英

解鎖以鎖步方式存儲為文本的多個數組列

[英]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'

重要的部分是stateftime 我需要取消嵌套元素並保持它們的順序。 這會產生步驟。

例如,第一行將是:

目標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<> 在這里擺弄

由於stateftime可以是NULL ,我使用LEFT JOIN... ON true來在結果中保留這些行。

看:

當然,你真正應該做的是:

  1. 與設計數據庫的人解除好友關系。 (我的真實建議PC版。)
  2. 安裝當前的 Postgres 版本。 請參閱: https://www.postgresql.org/support/versioning/
  3. 使用適當的關系設計創建一個新數據庫。
  4. 遷移您的數據。 (並保留原件的備份以確保安全。)
  5. 燒掉舊的數據庫,再也不提它了。

現代 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM