Postgresql
從表中查找“n”個連續的空閒號碼
我有一些這樣的數字表(狀態是免費或已分配)
id_set 號碼狀態 ----------------------- 1 000001 已分配 1 000002 免費 1 000003 已分配 1 000004 免費 1 000005 免費 1 000006 已分配 1 000007 已分配 1 000008 免費 1 000009 免費 1 000010 免費 1 000011 已分配 1 000012 已分配 1 000013 已分配 1 000014 免費 1 000015 已分配
我需要找到“n”個連續數字,所以對於 n = 3,查詢將返回
1 000008 免費 1 000009 免費 1 000010 免費
它應該只返回每個 id_set 的第一個可能組(實際上,它只會為每個查詢的 id_set 執行)
我正在檢查 WINDOW 函式,嘗試了一些類似的查詢
COUNT(id_number) OVER (PARTITION BY id_set ROWS UNBOUNDED PRECEDING)
,但這就是我得到的全部:) 我想不出邏輯,如何在 Postgres 中做到這一點。我正在考慮使用 WINDOW 函式創建虛擬列,計算 status = ‘FREE’ 的每個數字的前行,然後選擇第一個數字,其中 count 等於我的“n”數字。
或者可能按狀態對數字進行分組,但只能從一個分配到另一個分配,並且只選擇包含至少“n”個數字的組
編輯
我找到了這個查詢(並對其進行了一些更改)
WITH q AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY id_set, status ORDER BY number) AS rnd, ROW_NUMBER() OVER (PARTITION BY id_set ORDER BY number) AS rn FROM numbers ) SELECT id_set, MIN(number) AS first_number, MAX(number) AS last_number, status, COUNT(number) AS numbers_count FROM q GROUP BY id_set, rnd - rn, status ORDER BY first_number
它產生免費/分配號碼組,但我想只從滿足條件的第一組中獲得所有號碼
這是一個差距和孤島問題。
id_set
假設在同一個集合中沒有間隙或重複:WITH partitioned AS ( SELECT *, number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp FROM atable WHERE status = 'FREE' ), counted AS ( SELECT *, COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt FROM partitioned ) SELECT id_set, number FROM counted WHERE cnt >= 3 ;
這是此查詢的 SQL Fiddle 展示*連結: http ://sqlfiddle.com/#!1/a2633/1 。
更新
要僅返回一組,您可以再添加一輪排名:
WITH partitioned AS ( SELECT *, number - ROW_NUMBER() OVER (PARTITION BY id_set) AS grp FROM atable WHERE status = 'FREE' ), counted AS ( SELECT *, COUNT(*) OVER (PARTITION BY id_set, grp) AS cnt FROM partitioned ), **ranked AS ( SELECT *, RANK() OVER (ORDER BY id_set, grp) AS rnk FROM counted WHERE cnt >= 3 )** SELECT id_set, number FROM **ranked WHERE rnk = 1** ;
這也是一個展示:http ://sqlfiddle.com/#!1/a2633/2 。
如果您需要將其設置為一組*,
id_set
*請像這樣更改RANK()
呼叫:RANK() OVER (***PARTITION BY id_set*** ORDER BY ***grp***) AS rnk
此外,您可以使查詢返回最小的匹配集(即首先嘗試返回恰好三個連續數字(如果存在,否則為四個、五個等)的第一組,如下所示:
RANK() OVER (ORDER BY ***cnt,*** id_set, grp) AS rnk
或像這樣(每個一個
id_set
):RANK() OVER (PARTITION BY id_set ORDER BY ***cnt,*** grp) AS rnk
- 此答案中連結的 SQL Fiddle 展示使用 9.1.8 實例,因為 9.2.1 目前似乎無法正常工作。