【问题标题】:MySql, Postgres, Oracle and SQLServer ignoring IS NOT NULL filterMySql、Postgres、Oracle 和 SQLServer 忽略 IS NOT NULL 过滤器
【发布时间】:2014-12-18 13:02:43
【问题描述】:

当我准备回答我们这里的一位同事时,我遇到了一个奇怪的情况,至少对我来说是这样。原来的问题在这里:Pivot Table Omitting Rows that Have Null values

我已将查询修改为使用max 而不是group_concat,以便在所有数据库中显示“问题”。

SELECT 
  id, 
  max(case when colID = 1 then value else '' end) AS fn,
  max(case when colID = 2 then value else '' end) AS ln,
  max(case when colID = 3 then value else '' end) AS jt
FROM tbl 
GROUP BY id

这个查询的结果是这样的:

ID    FN        LN            JT
1    Sampo    Kallinen     Office Manager
2    Jakko    Salovaara    Vice President
3    (null)   Foo          No First Name

用户要求过滤 ID 为 3 的行,因为字段 value 为空。

当它看起来很明显时,只需在该查询上添加WHERE value IS NOT NULL 约束即可实现用户期望。它不会工作。

所以我开始在其他数据库上对其进行测试,看看会发生什么(使用 WHERE CLAUSE 进行查询)

SELECT 
  id, 
  max(case when colID = 1 then value else '' end) AS fn,
  max(case when colID = 2 then value else '' end) AS ln,
  max(case when colID = 3 then value else '' end) AS jt
FROM tbl 
  WHERE value is not null
GROUP BY id

令我惊讶的是,结果是一样的,没有一个起作用。

然后我尝试了同一查询的不同版本:

SELECT * FROM (
    SELECT 
      id, 
      max(case when colID = 1 then value else '' end) AS fn,
      max(case when colID = 2 then value else '' end) AS ln,
      max(case when colID = 3 then value else '' end) AS jt
    FROM tbl 
    GROUP BY id
) T
WHERE fn IS NOT NULL
  AND ln IS NOT NULL
  AND jt IS NOT NULL

我可以让它在所有数据库上工作的唯一方法是使用这个查询:

SELECT 
  id, 
  max(case when colID = 1 then value else '' end) AS fn,
  max(case when colID = 2 then value else '' end) AS ln,
  max(case when colID = 3 then value else '' end) AS jt
FROM tbl 
WHERE NOT EXISTS (SELECT * FROM tbl b WHERE tbl.id=b.id AND value IS NULL)
GROUP BY id

所以我问:
这里发生了什么,除了 Oracle 上的特定情况外,所有其他数据库似乎都忽略了IS NOT NULL 过滤器?

【问题讨论】:

  • 我猜在第一个示例中,即使您排除了带有 null 的项目,您也不会排除该行/组的所有项目,并且 db 将缺失值显示为 null。顺便说一句,如果您想为缺少值的行排除整个组,这也可以; sqlfiddle.com/#!6/78395/10
  • There is no such thing as "Postgre".(好像也没有名为“My”的 RDBMS。)请解决您的问题。
  • 感谢@ErwinBrandstetter,已修复。
  • 顺便说一句,您使用子查询的“不同版本”是一种有效的方法。它只是失败了,因为您将original question 中的NULL 替换为'',即NOT NULL - 除了Oracle,它具有像@Joshua explains 这样的可疑实现。
  • 投反对票的人愿意解释为什么这个问题被投反对票?

标签: mysql sql-server database oracle postgresql


【解决方案1】:

如果同一id 的任何来源 行中的任何行,则省略结果 中的行有value IS NULL,Postgres 中的解决方案是在HAVING 子句中使用聚合函数every() 或(历史原因的同义词)bool_and():

SELECT id
     , max(case when colID = 1 then value else '' end) AS fn
     , max(case when colID = 2 then value else '' end) AS ln
     , max(case when colID = 3 then value else '' end) AS jt
FROM   tbl 
GROUP  BY id
HAVING every(value IS NOT NULL);

SQL Fiddle.

解释

您使用WHERE 子句的尝试只会在您的示例中消除id = 3 的一个 源行(带有colID = 1 的行),为相同的id 留下另外两个。所以我们仍然会在聚合后的结果中得到id = 3 的一行。

但由于我们没有包含colID = 1 的行,因此我们在id = 3 的结果中得到了fn 的空字符串(注意:不是NULL 值!)。

在 Postgres 中更快的解决方案是使用 crosstab()。详情:

其他关系型数据库

虽然EVERY 是在 SQL:2008 标准中定义的,但许多 RDBMS 不支持它,可能是因为其中一些具有布尔类型的可疑实现。 (不删除任何名称,如“MySQL”或“Oracle”......)。您可以在任何地方(包括 Postgres)替换为:

SELECT id
     , max(case when colID = 1 then value else '' end) AS fn
     , max(case when colID = 2 then value else '' end) AS ln
     , max(case when colID = 3 then value else '' end) AS jt
FROM   tbl 
GROUP  BY id
HAVING count(*) = count(value);

因为count() 不计算 NULL 值。在 MySQL 中还有bit_and()。 此相关问题下的更多信息:

【讨论】:

  • 谢谢@ErwinBrandstetter 我也考虑过这种情况,我只消除了一种情况,所以我做了这个测试` FROM (select * from tbl where value is not null) t` 消除了(或它应该)查询的结果将被分组并且仍然具有相同的结果:sqlfiddle.com/#!15/78395/4您的答案是完美的,我会接受它,我只请您回答最后一个疑问。
  • @JorgeCampos:您的新示例仍然执行相同操作:只需删除 id = 3 的三个源行之一。因此结果相同。
  • Ooooooh 当然......我错过了这样一个事实,即这里的 id 被分组而不是 value。现在我明白了。非常感谢。
【解决方案2】:

它在 Oracle 中有效,因为 Oracle 错误地处理 NULL,因为 NULL 和 '' 是相同的。其他数据库不这样做,因为它是错误的。 NULL 是未知的,而 '' 只是一个空白的空字符串。

因此,如果您的 where 子句表示类似 WHERE (fn IS NOT NULL or fn <> '') 的内容,您可能会走得更远。

【讨论】:

  • 你是对的,对于子查询,fn IS NOT NULL or fn <> '' 有效。但我正在寻求更多解释为什么它的行为如此而不是解决方案 +1 :)
  • 我以为我在前两句话中已经解释过了,但感谢您的支持!
  • 在目前的情况下,WHERE fn <> '' 将适用于所有版本(用于带有子选择的查询)。无论哪种方式,您的回答都解释了为什么该查询在 Oracle 中不应该起作用。
【解决方案3】:

我认为这是一个 HAVING 子句可以满足您需要的情况。

SELECT id, max ... (same stuff as before)
FROM tbl
GROUP by id
HAVING  fn IS NOT NULL
    AND ln IS NOT NULL
    AND jt IS NOT NULL

【讨论】:

  • @Jorge:这也将 output 列与 input 列混淆了。在 WHERE 和 HAVING 子句中,您只能引用 input 列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-03
  • 2020-03-22
  • 1970-01-01
  • 2011-03-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多