【问题标题】:Modify COALESCE function to consider two adjacent values for the result修改 COALESCE 函数以考虑结果的两个相邻值
【发布时间】:2016-01-22 05:51:48
【问题描述】:

我目前正在使用COALESCE 函数从字段列表中获取第一个 NOT NULL 值。示例:

 COALESCE(header_to_node_13.subsetname, header_to_node_12.subsetname,
 header_to_node_11.subsetname, header_to_node_10.subsetname, 
 header_to_node_9.subsetname, header_to_node_8.subsetname, 
 header_to_node_7.subsetname, header_to_node_6.subsetname,   
 header_to_node_5.subsetname, header_to_node_4.subsetname,
 header_to_node_3.subsetname, header_to_node_2.subsetname,     
 header_to_node_1.subsetname, 
 header_to_node.subsetname, header_to_node.setname) AS prctr1

我需要修改逻辑,以便同时获取 COALESCE 值右侧的值,以便可以比较这两个值以填充新创建的字段。

例如,如果COALESCE 的值是header_to_node_5.subsetname,我也需要获取header_to_node_4.subsetname,所以这两个可以在CASE 语句中使用。

如果第二个值 (header_to_node_4.subsetname) 为 NULL,我想要第一个值 (header_to_node_5.subsetname)。如果第二个值不为空,我想使用第二个值。

我不知道如何获取第二个值。我正在使用 Greenplum/PostgreSQL 8.2

【问题讨论】:

标签: postgresql null case coalesce greenplum


【解决方案1】:

如果我正确理解您的要求,您有一组有序的字段,可能来自将多个表连接在一起,并且您希望获得第一个非空字段紧随其后的字段。

在这种情况下,您可以使用LATERAL:这允许您引用前面的FROM 项提供的字段:

SELECT id, COALESCE(t.b, t.a) AS result
FROM mytable,
LATERAL (
  SELECT x.v, LEAD(x.v) OVER (ORDER BY x.i) 
  FROM (
    VALUES (header_to_node_13, 1), (header_to_node_12, 2),
           (header_to_node_11, 3), (header_to_node_10, 4),
           (header_to_node_9, 5), (header_to_node_8, 6),
           (header_to_node_7, 7), (header_to_node_6, 8),
           (header_to_node_5, 9), (header_to_node_4, 10),
           (header_to_node_3, 11), (header_to_node_2, 12),
           (header_to_node_1, 13)) AS x(v,i)
  ORDER BY 
     CASE WHEN x.v IS NULL THEN 1 ELSE 0 END,
     x.i
  LIMIT 1 ) AS t(a, b)  

LATERAL 子查询中使用VALUES 实质上会构造一个表,其中包含查询的COALESCE 函数引用的所有字段。在VALUES 中还包含一个顺序字段x.i,以定义字段顺序。

一旦设置了这个内联表,您就可以轻松地对所有字段执行ORDER BYLIMIT 1,以便找到第一个非空字段。 LEAD窗口函数用于获取下一个字段。

注意:为简单起见,我已重命名 OP 中存在的字段,例如header_to_node_13.subsetnameheader_to_node_13。我也省略了COALESCE 中的一些字段。

Demo here

【讨论】:

  • 这看起来很棒。不幸的是,我使用的 PostgreSQL 版本不支持 LATERAL 功能。
【解决方案2】:

这应该适用于 Postgres 9.4 及更高版本:

SELECT val, nextval
FROM
(
  SELECT
    val,
    lag(val) OVER w as prevval,
    lead(val) OVER w as nextval
  FROM UNNEST(ARRAY[
    header_to_node_13.subsetname, header_to_node_12.subsetname,
    header_to_node_11.subsetname, header_to_node_10.subsetname, 
    header_to_node_9.subsetname, header_to_node_8.subsetname, 
    header_to_node_7.subsetname, header_to_node_6.subsetname,   
    header_to_node_5.subsetname, header_to_node_4.subsetname,
    header_to_node_3.subsetname, header_to_node_2.subsetname,     
    header_to_node_1.subsetname, 
    header_to_node.subsetname, header_to_node.setname
  ]) WITH ORDINALITY AS u(val, pos)
  WINDOW w AS (ORDER BY pos)
) t
WHERE
  prevval IS NULL AND
  val IS NOT NULL

【讨论】:

    【解决方案3】:

    为了清楚起见,我简化了列名。

    功能

    对于任何输入类型任意数量的值,将其设为polymorphic 函数可变数量的参数(VARIADIC):

    CREATE OR REPLACE FUNCTION coalesce2(VARIADIC ANYARRAY)
      RETURNS ANYELEMENT AS
    $func$
    BEGIN
    
    FOR i IN 1 .. array_upper($1, 1)
    LOOP
       IF $1[i] IS NOT NULL THEN
          RETURN COALESCE($1[i+1], $1[i]);
       END IF;
    END LOOP;
    
    RETURN NULL;  -- we only come this far if all values are NULL
    
    END
    $func$ LANGUAGE plpgsql IMMUTABLE;
    

    呼叫:

    SELECT coalesce2(h13, h12, h11, h10, ...);
    

    VARIADIC 参数在函数内作为一个数组可见,我们可以用FOREACH 循环:

    或者你可以使用一个普通的 SQL 函数来封装以下之一的逻辑:

    纯 SQL

    SELECT COALESCE(lead(elem) OVER (ORDER BY ord), elem) AS result
    FROM   unnest(ARRAY[h13, h12, h11, h10, ...]) WITH ORDINALITY u(elem, ord)
    ORDER  BY elem IS NULL, ord
    LIMIT  1;
    

    WITH ORDINALITY(如@Nick already displayed)需要 Postgres 9.4 或更高版本。此解决方案按照 SQL 标准的定义工作。不过,它并不是严格需要的。这也有效:

    SELECT COALESCE(lead(elem) OVER (), elem) AS result
    FROM   unnest(ARRAY[h13, h12, h11, h10, ... ]) elem
    ORDER  BY elem IS NULL
    LIMIT  1;
    

    它依赖于保留数组元素顺序的 unnest() 的 Postgres 实现,并且窗口函数 lead() 使用这个给定的顺序,之前后面的 ORDER BY 重新排序行。 SQL 标准没有严格保证,但它适用于所有可用版本的 Postgres。考虑:

    您还可以使用带有手动编号的 VALUES 表达式以确保安全且符合标准,例如 @Giorgos demonstrated 或我的简化查询:

    SELECT COALESCE(lead(elem) OVER (), elem) AS result
    FROM  (VALUES (h13), (h12), (h11), (h10), ... ) t(elem)
    ORDER  BY elem IS NULL
    LIMIT  1;
    

    SQL Fiddle 演示全部。

    【讨论】:

    • 我使用的是 PostgreSQL 8.2,所以我不能使用 ORDINALITY。我尝试了可变参数函数,但出现错误:第 111 行:COALESCE2(vw_header_to_node_13.subsetname, ^ 提示:没有函数与给定的名称和参数类型匹配。您可能需要添加显式类型转换。
    • @user3329160:为什么还要使用过时的 8.2 版?小提琴演示了该功能在现代 Postgres 中的工作原理。
    • 因为它是Greenplum特有的,所以EMC有自己定制的版本和系统打包在一起。
    • @user3329160:Greeplum 有一个标签——它与 Postgres 相关,但不完全是 Postgres。请始终相应地标记您的问题。
    • 哦,我不知道。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 2017-09-07
    • 1970-01-01
    • 2018-03-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多