修改COALESCE函数以考虑结果的两个相邻值

时间:2015-10-22 20:03:48

标签: postgresql null case coalesce greenplum

我目前正在使用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)为空,我想要第一个值(header_to_node_5.subsetname)。如果第二个值是NOT NULL,我想使用第二个值。

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

3 个答案:

答案 0 :(得分:3)

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

在这种情况下,您可以使用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)  

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

设置此内联表后,您可以在所有字段上轻松执行ORDER BYLIMIT 1,以便找到第一个非空字段。 LEAD窗口函数用于获取下一个字段。

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

Demo here

答案 1 :(得分:1)

这应该适用于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

答案 2 :(得分:1)

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

功能

对于 任何输入类型 任意数量的值 ,请将其设为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()使用此给定顺序,之前更晚{{ 1}}重新排序行。 SQL标准没有严格保证,但它适用于Postgres的所有可用版本。考虑:

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

VALUES

SQL Fiddle展示所有。