【问题标题】:Postgresql : Converting comma separated integer values and intervals to sequenced numbersPostgresql:将逗号分隔的整数值和间隔转换为序列数
【发布时间】:2015-07-29 05:30:36
【问题描述】:

我有一个包含以下内容的表格:

Value 是一个 varchar 类型,用于存储字符串字符,例如 commadashes,所以任何事情都会发生。但通常它只包含numberscommadash 来指定间隔。

id | value      | 
------------------
1  | 1,2,5,8-10 |
2  | 1,2,3      |
3  | 1-3        |
4  | 1-3, 4-5   |
5  | 1-2,2-3    |

我想执行select 查询,以在数据库级别(而不是代码级别)以“规范化”代码可读格式(逗号分隔)检索值,这就是为什么我需要选择一个像这样的表。

id | value      | normalized
-------------------------------
1  | 1,2,5,8-10 |1,2,5,8,9,10
2  | 1,2,3      |1,2,3
3  | 1-3        |1,2,3
4  | 1-3, 4-5   |1,2,3,4,5
5  | 1-2,2-3    |1,2,3

id 为 5 的记录的特殊情况,即使它指定了 2 两次,它仍然应该只检索 2 一次。 postgres 中是否有一个功能可以做到这一点?如果不是,我如何解析字符串并对Postgres sql 中的数字进行排序?

【问题讨论】:

  • 因此您有一种自定义方式来存储一组可能不相交的范围,并且您希望将其转换为一个简单的集合。
  • 是的,完全正确...因为数据是作为一个简单的字符串接收的,就像用户输入的那样,并希望以这种方式检索它。
  • 但您不必担心数据是如何输入的,表单中的代码会处理它。除了逗号和破折号外,它永远不会包含字符串。我们允许这样的用户输入,因为它是最具逻辑可读性的输入方式。对于一个区间,比如说 330-540 ......你不能真的期望用户输入 330,331,332 ......等等......我是 postgres 和 sql 的新手......没什么帮助? :)

标签: sql regex postgresql postgresql-9.3


【解决方案1】:

对于您首选的 PL 中的过程或简单的 C 扩展,这似乎是一个很好的案例。 pl/perl、pl/pythonu 或 pl/v8 将是我的选择。

也就是说,在 SQL 中这很容易。拆分以查找子范围,可以是单个数字或范围。然后对于它上面的每个范围 generate_series。

例如:

SELECT n 
FROM
   regexp_split_to_table('1,2,5,8-10', ',') subrange,
   regexp_split_to_array(subrange, '-') subrange_parts,
   generate_series(subrange_parts[1]::integer, 
                  coalesce(subrange_parts[2], subrange_parts[1])::integer
   ) n;

您可以将其包装为 SQL 函数,或用作表查询的一部分。

应用于表格,你会得到类似的东西:

CREATE TABLE example
    ("id" int, "value" varchar)
;

INSERT INTO example
    ("id", "value")
VALUES
    (1, '1,2,5,8-10'),
    (2, '1,2,3'),
    (3, '1-3'),
    (4, '1-3, 4-5'),
    (5, '1-2,2-3')
;

当应用于类似于以下内容的表格时:

SELECT
  example.id,
  array_agg(DISTINCT n) AS expanded_set
FROM
   example,
   regexp_split_to_table(example.value, ',') subrange,
   regexp_split_to_array(subrange, '-') subrange_parts,
   generate_series(subrange_parts[1]::integer, 
                  coalesce(subrange_parts[2], subrange_parts[1])::integer
   ) n
 GROUP BY
   example.id;

结果(添加了原始 col):

 id | original_format |  expanded_set  
----+-----------------+----------------
  1 | 1,2,5,8-10      | {1,2,5,8,9,10}
  2 | 1,2,3           | {1,2,3}
  3 | 1-3             | {1,2,3}
  4 | 1-3, 4-5        | {1,2,3,4,5}
  5 | 1-2,2-3         | {1,2,3}
(5 rows)

这不会特别快,但可能没问题。如果没有,请在 C 中写一些更快的东西作为扩展,或者可能是 plperl 或其他东西。

要了解发生了什么,请阅读 PostgreSQL 手册部分:

  • GROUP BY 和聚合
  • 聚合函数,尤其是array_agg
  • DISTINCT 作为聚合限定符
  • PostgreSQL 数组,我在这里用作中间状态和结果
  • generate_series 函数
  • regexp_split_to_tableregexp_split_to_array 函数
  • LATERAL 查询,这里隐式使用,因为一个函数使用连接列表中另一个函数的结果。

以上示例仅适用于 PostgreSQL 9.2 及更新版本。如果您有旧版本,则必须使用嵌套子查询层来解决缺少 LATERAL 的问题。

【讨论】:

  • + 为真正有用的解释和阅读建议点赞。这对像我这样的初学者真的很有帮助:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-02
相关资源
最近更新 更多