使用NULLIF的MySQL子查询截断结果

时间:2019-01-24 18:46:30

标签: mysql

我有以下查询:

SELECT 
    NULLIF(MAX(t.date),'2019-01-15') AS ended
FROM 
    totals t

此查询正确输出日期:

> 2019-01-01

但是如果我在子查询中引用此查询,如下所示:

SELECT * FROM
    (SELECT 
        NULLIF(MAX(t.date),'2019-01-15') AS ended
    FROM 
        totals t) AS a

此版本错误地产生了截断的结果:

> 201

有人可以帮助我了解这种行为以及如何最好地解决它吗?

附加说明: 我正在运行MySQL版本:“ 5.7.25 MySQL Community Server”

对于想要进行此测试的任何人,下面是受此问题影响的简单测试表的示例:

CREATE TABLE `totals` (
  `date` date NOT NULL,
  `value` decimal(10,0) DEFAULT NULL,
  PRIMARY KEY (`date`)
);
INSERT INTO `totals` VALUES ('2018-01-01',2000000),('2019-01-01',3000000);

2 个答案:

答案 0 :(得分:0)

我的猜测是正在发生隐式数据类型转换。

作为一种解决方法,我将从NULLIF函数中获取返回值并将其转换/广播回DATE数据类型。最简单的方法是将其包装在DATE()函数中。

SELECT 
    DATE( NULLIF(MAX(t.date),'2019-01-15') ) AS ended
    ^^^^^                                  ^

我们还可以尝试转换字符串文字,将其转换为DATE,看看是否可以解决问题:

SELECT
    NULLIF(MAX(t.date), DATE('2019-01-15') ) AS ended
                        ^^^^^            ^

或者我们都可以做:

SELECT 
    DATE( NULLIF(MAX(t.date), DATE('2019-01-15') ) ) AS ended
                              ^^^^^            ^
    ^^^^^                                          ^

我们还可以使用其他表达式进行数据类型转换,例如CAST()CONVERT()STR_TO_DATE()

或者我们可以仅使用简单的+ INTERVAL 0 DAY技巧。例如

SELECT 
    NULLIF(MAX(t.date),'2019-01-15' + INTERVAL 0 DAY ) + INTERVAL 0 DAY AS ended
                                    ^^^^^^^^^^^^^^^^   ^^^^^^^^^^^^^^^^

答案 1 :(得分:0)

这里的问题似乎是在NULLIF内发生的一个微妙的转换/广播问题。首先,这是您的查询实际上可以按预期运行的版本:

SELECT *
FROM
(
    SELECT NULLIF(MAX(t.date), STR_TO_DATE('2019-01-15', '%Y-%m-%d') AS ended
    FROM totals t
) AS a

您当前的查询正在发生的事情是MySQL正在将对MAX(t.date)的调用转换为文本,以匹配文本文字'2015-01-15'。通过确保NULLIF的两个参数都是日期类型,即可获得所需的行为。

关于为什么,我们看到201是调用NULLIF的字符串结果,我没有解释。但是,我可以在这里引用NULLIF的{​​{3}}:

  

如果expr1 = expr2为true,则返回NULL,否则返回expr1。与expr1 = expr2 THEN NULL ELSE expr1 END时的情况相同。

在SQL中,一条通用规则是CASE表达式的if和else分支应始终具有相同的类型。实际上,如果我们违反此规则,则在大多数数据库中,CASE表达式甚至不会编译。将其应用于NULLIF意味着我们应始终确保两个参数都具有相同的类型。违反此规则可能可以在MySQL上运行(类似于在关闭全模式的情况下执行不符合ANSI的GROUP BY),但是如果可以避免的话,这不是我们应该选择的东西。 / p>