[英]Get even / odd / all numbers between two numbers
I want to display all the numbers (even / odd / mixed) between two numbers (1-9; 2-10; 11-20) in one (or two) column. 我想在一列(或两列)中显示两个数字(1-9; 2-10; 11-20)之间的所有数字(偶数/奇数/混合)。
Example initial data: 示例初始数据:
| 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');
Result: 结果:
| output |
----------------------------------
| 1,3,5,7,9 |
| 2,4,6,8,10 |
| 11,12,13,14,15,16,17,18,19,20 |
Something like this: 像这样的东西:
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
This outputs: 这输出:
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
Building on your first example, simplified, but with PK: 基于您的第一个示例,简化,但使用PK:
CREATE TABLE tbl1 (
tbl1_id serial PRIMARY KEY -- optional
, rang text -- can be NULL ?
);
Use split_part()
to extract lower and upper bound. 使用split_part()
来提取下限和上限。 (
would be needlessly expensive and error-prone). (
regexp_split_to_array()
将是不必要的昂贵且容易出错)。 And regexp_split_to_array()
generate_series()
to generate the numbers. 并使用generate_series()
生成数字。
Use a LATERAL
join and aggregate the set immediately to simplify aggregation. 使用LATERAL
连接并立即聚合该集合以简化聚合。 An ARRAY constructor is fastest in this case: 在这种情况下, 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, you want every number in the range only if upper and lower bound are a mix of even and odd numbers. AIUI, 只有当上限和下限是偶数和奇数的混合时,你才想要范围内的每个数字。 Else, only return every 2nd number, resulting in even / odd numbers for those cases. 否则,只返回每个第二个数字,导致这些情况的偶数/奇数。 This expression implements the calculation of the interval: 此表达式实现间隔的计算:
CASE WHEN (z-a)%2 = 0 THEN 2 ELSE 1 END
Result as desired: 结果符合要求:
output
-----------------------------
1,3,5,7,9
2,4,6,8,10
11,12,13,14,15,16,17,18,19,20
You do not need WITH ORDINALITY
in this case, because the order of elements is guaranteed. 你不需要 WITH ORDINALITY
在这种情况下,因为元素的顺序保证。
The aggregate function array_agg()
makes the query slightly shorter (but slower) - or use string_agg()
to produce a string directly, depending on your desired output format: 聚合函数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;
Note a subtle difference when using an aggregate function or ARRAY
constructor in the LATERAL
subquery: Normally, rows with rang IS NULL
are excluded from the result because the LATERAL
subquery returns no row . 注意在LATERAL
子查询中使用聚合函数或ARRAY
构造函数时的细微差别 :通常,从结果中排除具有rang IS NULL
行,因为LATERAL
子查询不返回任何行 。
If you aggregate the result immediately, "no row" is transformed to one row with a NULL value, so the original row is preserved. 如果立即聚集的结果,“没有行”转换为一个排 ,一个NULL值,所以原始行被保留。 I added demos to the fiddle. 我向小提琴添加了演示。
You do not need a CTE for this, which would be more expensive. 你不需要CTE,这会更昂贵。
Aside: The type conversion to integer
removes leading / training white space automatically, so a string like this works as well for rank
: ' 1 - 3'
. 旁白:类型转换为integer
会自动删除前导/训练空格,因此像这样的字符串也适用于rank
: ' 1 - 3'
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.