【问题标题】:Substituting value in empty field after using split_part使用 split_part 后在空字段中替换值
【发布时间】:2018-01-27 17:31:32
【问题描述】:

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

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

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

相对于:

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

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

我怎样才能做到这一点?我试过split_part() 并转换为int。但是,split_part(version, "-", 2) 将返回看起来像空字符串的内容。我尝试使用 COALESCE(splitpart..., '0') 运行它,但无济于事,因为它试图读取字段索引 2 返回的空字段。

【问题讨论】:

    标签: sql postgresql split natural-sort


    【解决方案1】:

    要绕过没有连字符的版本字符串,您可以使用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

    【讨论】:

    • 感谢您的解决方案!
    【解决方案2】:

    使用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)    
    

    【讨论】:

    • coalesce(nullif(split_part(version, '-', 2), ''), '0')::int ---- 就像一个魅力!谢谢。
    【解决方案3】:

    split_part() 返回空字符串 ('') - 不是 NULL - 当要返回的部分为空或不存在时。这就是为什么COALESCE 在这里什么都不做。并且空字符串 ('') 没有表示为 integer 值,因此在尝试强制转换时会引发错误。

    这个例子中最短的方法应该是GREATEST(split_part( ... ) , '0'),因为空字符串在任何其他非空字符串甚至NULL(在任何语言环境中)之前排序。然后使用DISTINCT ON () 为每个id 获取具有“最大”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
    

    或者也可以使用NULLIFNULLS 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

    相关:

    【讨论】:

    • greatest(val, '0')::int 通常比coalesce(nullif(val, ''), '0')::int 慢。
    • @klin:是的,即使只是轻微的。通常CASE 是最快的。或者在这种情况下只是 NULLIF 而没有 COALESCEGREATEST 是这里最短/最简单的。
    猜你喜欢
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2020-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多