NULL!= SQL查询中的值的结果(postgres和rails3)

时间:2013-05-10 16:07:31

标签: sql ruby-on-rails postgresql null

我在Rails3应用程序中遇到了意外的postgres查询问题。

我以为我会通过stackoverflow运行它,看看互联网的大脑有什么说法:)

这个结果是预期的行为(以及为什么?!)或者这是一个错误吗?

鉴于我在Postgres 9.1.4数据库中有一个表Orders:

id        state
=====     ======
1                     <-- nil (default value)
2         'success'
3         'failure'

当我运行查询时:

Order.where('orders.state != ?', 'success').map { |order| order.id }
Order Load (3.8ms)  SELECT "orders".* FROM "orders" WHERE (orders.state != 'success')

=> [3]

我期待结果[1,3]。显然有两行(!='成功')满足。

为什么这是nil!=''成功'在这里不正确?是!=只是忽略NULL值?应该吗?

注意:我使用以下查询生成了所需的结果:

Order.where('orders.state IS NULL OR orders.state != ?', 'success').map { |order| order.id }
Order Load (2.3ms) SELECT "orders".* FROM "orders" WHERE (orders.state IS NULL OR orders.state != 'success')

=> [1, 3]

任何意见都将不胜感激。

2 个答案:

答案 0 :(得分:11)

PostgreSQL中有一个IS DISTINCT FROM比较运算符:

  

当任一输入为空时,普通比较运算符产生null(表示“未知”),而不是true或false。例如,7 = NULL产生null,7 <> NULL也是如此。如果此行为不合适,请使用IS [ NOT ] DISTINCT FROM构造:

expression IS DISTINCT FROM expression
expression IS NOT DISTINCT FROM expression
     

对于非空输入,IS DISTINCT FROM<>运算符相同。但是,如果两个输入都为null,则返回false,如果只有一个输入为null,则返回true。类似地,对于非空输入,IS NOT DISTINCT FROM=相同,但是当两个输入都为空时它返回true,而当只有一个输入为空时返回false。因此,这些结构有效地表现为null是正常数据值,而不是“未知”。

例如,给出您的样本数据:

=> select * from orders where state is distinct from 'success';
 id |  state  
----+---------
  1 | 
  3 | failure
(2 rows)

所以你可以这样说:

Order.where('orders.state is distinct from ?', 'success').pluck(:id)

请注意,我也切换到pluck而不是map(&:id),这会将此SQL发送到数据库:

select id from orders where orders.state is distinct from 'success'

而非select orders.* ...使用客户端过滤器来提取id

答案 1 :(得分:1)

这是SQL标准的一部分,其中涉及至少一个NULL的两个值之间的比较既不返回true,也不返回false,但不知道。

来自http://www-cs-students.stanford.edu/~wlam/compsci/sqlnulls

  
      
  • 涉及NULL的两个值之间的布尔比较既不返回true也不返回false,但在SQL的三值逻辑中未知。 [3]例如,NULL既不等于NULL也不是NULL not-equals NULL为真。测试值是否为NULL需要表达式,例如IS NULL或IS NOT NULL。
  •   
  • SQL查询仅选择WHERE表达式计算结果为true的值,以及HAVING子句计算结果为true的组。
  •   

一种解决方案是使用COALESCE:

Order.where('COALESCE(orders.state,"NIL") != ?', 'success').map(&:id)

如果orders.state为NULL,则将其替换为'NIL',然后从比较中返回true或false。

您当然可以使用其他条件:

Order.where('orders.state is null OR orders.state != ?', 'success').map(&:id)