使用 split_part 后替换空字段中的值

Substituting value in empty field after using split_part

我有两列,id integerversion text。我正在尝试将 version 中的字符串转换为整数,以便我可以 select id 的最大(最新)版本。

但是,id 的第一个实例将自己存储为 version。示例:

id | version
---+--------
10 | '10'

相对于:

id | version
---+--------
10 | '10-0'

其他行遵循约定 ID:10,版本:10-1。等等

我怎样才能做到这一点?我试过 split_part() 并转换为 int。然而,split_part(version, "-", 2) 将 return 看起来像一个空字符串。我已经尝试 运行 使用 COALESCE(splitpart..., '0') 无济于事,因为它试图读取由字段索引 2 编辑的空字段 return。

要避开没有连字符的版本字符串,您可以使用 CASE 表达式:

CASE WHEN version LIKE '%-%'
     THEN SPLIT_PART(version, '-', 2)::int
     ELSE 0 END

基本思想是在存在连字符时使用版本号,转换为 int,否则如果没有连字符则假定版本为零。

解决了这个障碍后,您的查询现在就减少为 ROW_NUMBER() 查询。在这里,分区是 id,并且使用上面的 CASE 表达式给出版本的顺序。

SELECT
    t.id, t.version
FROM
(
    SELECT
        id,
        CASE WHEN version LIKE '%-%'
             THEN version
             ELSE version || '-0' END AS version,
        ROW_NUMBER() OVER (PARTITION BY id
                           ORDER BY
                               CASE WHEN version LIKE '%-%'
                                    THEN SPLIT_PART(version, '-', 2)::int
                                    ELSE 0 END DESC) rn
    FROM yourTable
) t
WHERE t.rn = 1
ORDER BY t.id;

此处演示:

Rextester

使用组合coalesce() and nullif(),例子:

with my_table(version) as (
values
    ('10'), ('10-1'), ('10-2')
)

select 
    version, 
    split_part(version, '-', 1)::int as major, 
    coalesce(nullif(split_part(version, '-', 2), ''), '0')::int as minor
from my_table

 version | major | minor 
---------+-------+-------
 10      |    10 |     0
 10-1    |    10 |     1
 10-2    |    10 |     2
(3 rows)    

split_part() returns the empty string ('') - not NULL - when the part to be returned is empty or non-existent. That's why COALESCE 在这里什么都不做。并且空字符串 ('') 没有表示为 integer 值,因此在尝试转换它时会抛出错误。

此示例中最短的方法应该是 GREATEST(split_part( ... ) , '0') 转换之前,因为空字符串排在任何其他非空字符串甚至 NULL(在任何语言环境中)之前。然后使用 DISTINCT ON () 为每个 id.

获取带有 "biggest" version 的行

测试设置

CREATE TABLE tbl (
   id      integer NOT NULL
 , version text    NOT NULL
);

INSERT INTO tbl VALUES
     (10, '10-2')
   , (10, '10-1')
   , (10, '10')      -- missing subversion
   , (10, '10-111')  -- multi-digit number
   , (11, '11-1')
   , (11, '11-0')    -- proper '0'
   , (11, '11-')     -- missing subversion but trailing '-'
   , (11, '11-2');

解决方案

SELECT DISTINCT ON (id) *
FROM   tbl
ORDER  BY id, GREATEST(split_part(version, '-', 2), '0')::int DESC;

结果:

 id | version 
----+---------
 10 | 10-111
 11 | 10-2

可以也可以使用NULLIF并使用NULLS LAST(降序)排序:

SELECT DISTINCT ON (id) *
FROM   tbl
ORDER  BY id, NULLIF(split_part(version, '-', 2), '')::int DESC NULLS LAST;

同样的结果。

更明确的CASE声明:

CASE WHEN split_part(version, '-', 2) = '' THEN '0' ELSE split_part(version, '-', 2) END

dbfiddle here

相关:

  • Order varchar string as numeric
  • Select first row in each GROUP BY group?
  • PostgreSQL sort by datetime asc, null first?
  • How to convert empty to null in PostgreSQL?