如何获得多个字段的最大/最小有效值,其中某些字段可以为“null”

时间:2016-11-16 14:13:58

标签: sql aggregate-functions firebird

我有一个查询,其中包含多个相同类型的列。它们源自left join的详细信息表 - 多次使用其主表。

根据结果记录,我需要这些列的最大值和最小值。这不应该考虑null值。

Firebird中有MAX()MIN()个聚合函数。不幸的是,他们只接受一个字段作为参数。

此外,还有maxvalue()minvalue()个函数,它们接受多个参数。不幸的是,当至少一个值为null时,这些函数会返回null。我只想忽略null - 值,并仅在所有值均为null时返回null

以下SQL Fiddle只是一个返回多个列null的查询示例。由于SQL Fiddle不支持Firebird,我使用MySQL 5.6仅用于演示目的。但最后,我需要Firebird的解决方案。

MySQL 5.6架构设置

CREATE TABLE Master
    (`ID` int)
;

INSERT INTO Master
    (`ID`)
VALUES
    (1),
    (2),
    (3)
;


CREATE TABLE Detail
    (`MREF` int,
     `MYVALUE` int)
;

INSERT INTO Detail
    (`MREF`, `MYVALUE`)
VALUES
    (1, 1),
    (2, 2),
    (3, 3)
;

查询1

select m.ID, d1.MYVALUE, d2.MYVALUE, d3.MYVALUE
  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3

Results

| ID | MYVALUE | MYVALUE | MYVALUE |
|----|---------|---------|---------|
|  1 |       1 |  (null) |  (null) |
|  2 |  (null) |       2 |  (null) |
|  3 |  (null) |  (null) |       3 |

上述非常简单的示例所需的输出将是:

| ID | MYVALUE | MYVALUE | MYVALUE | MAX | MIN |
|----|---------|---------|---------|-----|-----|
|  1 |       1 |  (null) |  (null) |   1 |   1 |
|  2 |  (null) |       2 |  (null) |   2 |   2 |
|  3 |  (null) |  (null) |       3 |   3 |   3 |

3 个答案:

答案 0 :(得分:1)

火鸟

select m.ID
       ,nullif(maxvalue(coalesce(d1.MYVALUE,-999999999),coalesce(d2.MYVALUE,-999999999),coalesce(d3.MYVALUE,-999999999)),-999999999) as max_val
       ,nullif(minvalue(coalesce(d1.MYVALUE, 999999999),coalesce(d2.MYVALUE, 999999999),coalesce(d3.MYVALUE, 999999999)), 999999999) as min_val
  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3

的MySQL

select m.ID
       ,nullif(greatest(coalesce(d1.MYVALUE,-999999999),coalesce(d2.MYVALUE,-999999999),coalesce(d3.MYVALUE,-999999999)),-999999999) as max_val
       ,nullif(least   (coalesce(d1.MYVALUE, 999999999),coalesce(d2.MYVALUE, 999999999),coalesce(d3.MYVALUE, 999999999)), 999999999) as min_val
  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3

答案 1 :(得分:0)

maxvalue()minvalue()的等效的Mysql是GREATEST()LEAST(),但只接受两个参数,因此更复杂。但同样的逻辑

SQL DEMO

select m.ID, d1.MYVALUE, d2.MYVALUE, d3.MYVALUE,
       NULLIF(
               GREATEST( 
                        GREATEST( COALESCE(d1.MYVALUE,-99999),
                                  COALESCE(d2.MYVALUE,-99999)),
                        COALESCE(d3.MYVALUE,-99999)
                       ),
               -99999
             ) as max_value,
       NULLIF(
               LEAST( 
                        LEAST( COALESCE(d1.MYVALUE,99999),
                               COALESCE(d2.MYVALUE,99999)),
                        COALESCE(d3.MYVALUE,99999)
                       ),
               99999
             ) as min_value             


  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3

<强>输出

enter image description here

编辑:只有CASE版本,不需要-99999虚拟代码,但不会获得5列的预测

<强> SQL Demo

select m.ID, d1.MYVALUE, d2.MYVALUE, d3.MYVALUE,
       CASE WHEN d1.MYVALUE IS NULL AND d2.MYVALUE IS NULL AND d3.MYVALUE IS NULL  THEN NULL
            WHEN d1.MYVALUE IS NULL THEN CASE WHEN d2.MYVALUE IS NULL THEN d3.MYVALUE
                                              WHEN d3.MYVALUE IS NULL THEN d2.MYVALUE
                                              ELSE GREATEST(d2.MYVALUE, d3.MYVALUE)
                                         END
            WHEN d2.MYVALUE IS NULL THEN CASE WHEN d1.MYVALUE IS NULL THEN d3.MYVALUE
                                              WHEN d3.MYVALUE IS NULL THEN d1.MYVALUE
                                              ELSE GREATEST(d1.MYVALUE, d3.MYVALUE)
                                         END
            WHEN d3.MYVALUE IS NULL THEN CASE WHEN d1.MYVALUE IS NULL THEN d2.MYVALUE
                                              WHEN d2.MYVALUE IS NULL THEN d1.MYVALUE
                                              ELSE GREATEST(d1.MYVALUE, d2.MYVALUE)
                                         END                                         
            ELSE GREATEST(GREATEST(d1.MYVALUE,d2.MYVALUE), d3.MYVALUE)
       END as t,


  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3

答案 2 :(得分:-1)

看看其他答案,我得出结论,必须有封装条款。由于那些产生了复杂的查询,我看了 UDF (用户定义的函数)。

我在FreeAdhocUDF's date functions中找到了F_MAXDATE()F_MINDATE()。不幸的是,如果任何输入参数为maxvalue(),它们的行为类似于Firebird的内部函数minvalue()null,因为它们返回null

由于似乎没有其他UDF符合我的需求,我自己编写了它们。 起初,它似乎是返回模糊值。如果null传递给他们,则在某些情况下他们会返回17-11-1898 00:00:00。也就是说,因为Firebird默认情况下不会将null传递给UDF,而是传递null - 等效值。如果是TIMESTAMP,则为17-11-1898 00:00:00

要启用null - 传递给UDF,必须extend the UDFs params declaration with NULL

因此,当我将UDF的声明更改为类似的内容时,它起作用了:

DECLARE EXTERNAL FUNCTION MAX_TIMESTAMP
    TIMESTAMP NULL,
    TIMESTAMP NULL
RETURNS TIMESTAMP
ENTRY_POINT 'MAX_TIMESTAMP' MODULE_NAME 'HKSCommonUDF';

使用我新编写的UDF,我的示例查询看起来像这样(对于TIMESTAMP值):

select m.ID, d1.MYVALUE, d2.MYVALUE, d3.MYVALUE,
       max_timestamp(max_timestamp(d1.MYVALUE, d2.MYVALUE), d3.MYVALUE),
       min_timestamp(min_timestamp(d1.MYVALUE, d2.MYVALUE), d3.MYVALUE)
  from Master m
  left join Detail d1
    on d1.MREF = m.ID and d1.MYVALUE = 1
  left join Detail d2
    on d2.MREF = m.ID and d2.MYVALUE = 2
  left join Detail d3
    on d3.MREF = m.ID and d3.MYVALUE = 3