如何获得 maximal/minimal 多个字段的显着值,其中某些字段可以是 'null'
How to get maximal/minimal significant value of multiple fields where some fields can be 'null'
我有一个包含多个相同类型列的查询。它们源自一个细节 table,它与它的主人 table 多次 left join
-ed。
每个结果记录,我需要这些列的有效值的最大值和最小值。也就是说,不应考虑 null
个值。
Firebird 中有 MAX()
and MIN()
个聚合函数。不幸的是,他们只接受一个字段作为参数。
此外,还有 maxvalue()
and minvalue()
个接受多个参数的函数。不幸的是,当至少一个值是 null
时,这些函数 return null
。我只想忽略 null
-values 和 return null
只有当所有值都是 null
.
下面的SQL Fiddle只是一个查询的例子,其中return的多个列可以是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
| 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 |
火鸟
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
Mysql 等价于 maxvalue()
和 minvalue()
是 GREATEST()
和 LEAST()
但只接受两个参数,所以有点复杂。但同样的逻辑
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
输出
编辑:只有 CASE
版本,不需要 -99999
伪代码,但没有 5 列
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
看了其他的答案,我的结论是必须有封装术语。由于这些会产生复杂的查询,我查看了 UDFs(用户定义的函数)。
我在 FreeAdhocUDF's date functions 中找到了 F_MAXDATE()
和 F_MINDATE()
。不幸的是,它们的行为类似于 Firebird 的内部函数 maxvalue()
和 minvalue()
因为它们 return null
,如果任何输入参数是 null
.
好像没有其他的UDF符合我的需要,就自己写了。
起初,似乎是它们 return 模糊值。如果 null
被传递给他们,他们在某些情况下 returned 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
我有一个包含多个相同类型列的查询。它们源自一个细节 table,它与它的主人 table 多次 left join
-ed。
每个结果记录,我需要这些列的有效值的最大值和最小值。也就是说,不应考虑 null
个值。
Firebird 中有 MAX()
and MIN()
个聚合函数。不幸的是,他们只接受一个字段作为参数。
此外,还有 maxvalue()
and minvalue()
个接受多个参数的函数。不幸的是,当至少一个值是 null
时,这些函数 return null
。我只想忽略 null
-values 和 return null
只有当所有值都是 null
.
下面的SQL Fiddle只是一个查询的例子,其中return的多个列可以是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
| 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 |
火鸟
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
Mysql 等价于 maxvalue()
和 minvalue()
是 GREATEST()
和 LEAST()
但只接受两个参数,所以有点复杂。但同样的逻辑
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
输出
编辑:只有 CASE
版本,不需要 -99999
伪代码,但没有 5 列
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
看了其他的答案,我的结论是必须有封装术语。由于这些会产生复杂的查询,我查看了 UDFs(用户定义的函数)。
我在 FreeAdhocUDF's date functions 中找到了 F_MAXDATE()
和 F_MINDATE()
。不幸的是,它们的行为类似于 Firebird 的内部函数 maxvalue()
和 minvalue()
因为它们 return null
,如果任何输入参数是 null
.
好像没有其他的UDF符合我的需要,就自己写了。
起初,似乎是它们 return 模糊值。如果 null
被传递给他们,他们在某些情况下 returned 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