簡體   English   中英

得到兩個數字之間的偶數/奇數/所有數字

[英]Get even / odd / all numbers between two numbers

我想在一列(或兩列)中顯示兩個數字(1-9; 2-10; 11-20)之間的所有數字(偶數/奇數/混合)。
示例初始數據:

| rang  |              | r1 | r2 |
--------               -----|-----
| 1-9   |              | 1  | 9  |
| 2-10  |              | 2  | 10 |
| 11-20 |      or      | 11 | 20 |

CREATE TABLE initialtableone(rang TEXT);
INSERT INTO initialtableone(rang) VALUES
  ('1-9'),
  ('2-10'),
  ('11-20');

CREATE TABLE initialtabletwo(r1 NUMERIC, r2 NUMERIC);
INSERT INTO initialtabletwo(r1, r2) VALUES
  ('1', '9'),
  ('2', '10'),
  ('11', '20');

結果:

| output                         |
----------------------------------
| 1,3,5,7,9                      |
| 2,4,6,8,10                     |
| 11,12,13,14,15,16,17,18,19,20  |

像這樣的東西:

create table ranges (range varchar);

insert into ranges 
values
('1-9'),
('2-10'),
('11-20');

with bounds as (
  select row_number() over (order by range) as rn,
         range,
         (regexp_split_to_array(range,'-'))[1]::int as start_value,
         (regexp_split_to_array(range,'-'))[2]::int as end_value
  from ranges
)
select rn, range, string_agg(i::text, ',' order by i.ordinality)
from bounds b
  cross join lateral generate_series(b.start_value, b.end_value) with ordinality i
group by rn, range

這輸出:

rn | range | string_agg                   
---+-------+------------------------------
 3 | 2-10  | 2,3,4,5,6,7,8,9,10           
 1 | 1-9   | 1,2,3,4,5,6,7,8,9            
 2 | 11-20 | 11,12,13,14,15,16,17,18,19,20

基於您的第一個示例,簡化,但使用PK:

CREATE TABLE tbl1 (
  tbl1_id serial PRIMARY KEY  -- optional
, rang text  -- can be NULL ?
);

使用split_part()來提取下限和上限。 regexp_split_to_array() 將是不必要的昂貴且容易出錯)。 並使用generate_series()生成數字。

使用LATERAL連接立即聚合該集合以簡化聚合。 在這種情況下, ARRAY構造函數最快:

SELECT t.tbl1_id, a.output  -- array; added id is optional
FROM  (
   SELECT tbl1_id
        , split_part(rang, '-', 1)::int AS a
        , split_part(rang, '-', 2)::int AS z
   FROM   tbl1
   ) t
 , LATERAL (
   SELECT ARRAY(  -- preserves rows with NULL
      SELECT g FROM generate_series(a, z, CASE WHEN (z-a)%2 = 0 THEN 2 ELSE 1 END) g
      ) AS output
   ) a;

AIUI, 只有當上限和下限是偶數和奇數的混合時,你才想要范圍內的每個數字。 否則,只返回每個第二個數字,導致這些情況的偶數/奇數。 此表達式實現間隔的計算:

CASE WHEN (z-a)%2 = 0 THEN 2 ELSE 1 END

結果符合要求:

output
-----------------------------
1,3,5,7,9
2,4,6,8,10
11,12,13,14,15,16,17,18,19,20

不需要 WITH ORDINALITY在這種情況下,因為元素的順序保證。

聚合函數array_agg()使查詢稍微縮短(但速度較慢) - 或者使用string_agg()直接生成字符串,具體取決於您所需的輸出格式:

SELECT a.output  -- string
FROM  (
   SELECT split_part(rang, '-', 1)::int AS a
        , split_part(rang, '-', 2)::int AS z
   FROM   tbl1
   ) t
, LATERAL (
      SELECT string_agg(g::text, ',') AS output
      FROM   generate_series(a, z, CASE WHEN (z-a)%2 = 0 THEN 2 ELSE 1 END) g
   ) a;

注意在LATERAL子查詢中使用聚合函數或ARRAY構造函數時的細微差別 :通常,從結果中排除具有rang IS NULL行,因為LATERAL子查詢不返回任何行
如果立即聚集的結果,“沒有行”轉換為一個排 ,一個NULL值,所以原始行被保留。 我向小提琴添加了演示。

SQL小提琴。

你不需要CTE,這會更昂貴。

旁白:類型轉換為integer會自動刪除前導/訓練空格,因此像這樣的字符串也適用於rank' 1 - 3'

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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