SQL中的Null深入研究分析

虽然熟练掌握SQL的人对于Null不会有什么疑问,但总结得很全的文章还是很难找,看到一篇英文版的, 感觉还不错。

Tony Hoare 在1965年发明了 null 引用, 并认为这是他犯下的“几十亿美元的错误”. 即便是50年后的今天, SQL中的 null 值还是导致许多常见错误的罪魁祸首.

我们一起来看那些最令人震惊的情况。

Null不支持大小/相等判断

下面的2个查询,不管表 users 中有多少条记录,返回的记录都是0行:

select * from users where deleted_at = null;

– result: 0 rows

select * from users where deleted_at != null;

– result: 0 rows

怎么会这样子? 一切只因为 null 是表示一种“未知”的类型。也就是说,用常规的比较操作符(normal conditional operators)来将 null 与其他值比较是没有意义的。 Null 也不等于 Null(近似理解: 未知的值不能等于未知的值,两者间的关系也是未知,否则数学和逻辑上就乱套了)。

注意: 下面的SQL适合于MySQL,如果是Oracle,你需要加上 … from dual;

select null > 0;

– result: null

select null < 0;

– result: null

select null = 0;

– result: null

select null = null;

– result: null

select null != null;

– result: null

将某个值与 null 进行比较的正确方法是使用 is 关键字, 以及 is not 操作符:

select * from users

where deleted_at is null;

– result: 所有被标记为删除的 users

如果想要判断两列的值是否不相同,则可以使用 is distinct from:

select * from users

where has_address is distinct from has_photo

– result: 地址(address)或照片(photo)两者只有其一的用户

not in 与 Null

子查询(subselect)是一种很方便的过滤数据的方法。例如,如果想要查询没有任何包的用户,可以编写下面这样一个查询:

select * from users 

where id not in (select user_id from packages)

但此时假若 packages 表中某一行的 user_id 是 null 的话,问题就来了: 返回结果是空的! 要理解为什么会发生这种古怪的事情, 我们需要理解SQL编译器究竟干了些什么. 下面是一个更简单的示例:

select * from users 

where id not in (1, 2, null)

这个SQL语句会被转换为:

select * from users 

where id != 1 and id != 2 and id != null

我们知道,id != null 结果是个未知值, null. 而任意值和 null 进行 and 运算的结果都是 null, 所以相当于没有其他条件. 那么出这种结果的原因就是 null 的逻辑值不为 true.

如果条件调换过来, 查询结果就没有问题。 现在我们查询有package的用户.

select * from users 

where id in (select user_id from packages)

同样我们可以使用简单的例子:

select * from users

where id in (1, 2, null)

这条SQL被转换为:

select * from users 

where id = 1 or id = 2 or id = null

因为 where 子句中是一串的 or 条件,所以其中某个的结果为 null 也是无关紧要的。非真(non-true)值并不影响子句中其他部分的计算结果,相当于被忽略了。

Null与排序

在排序时, null 值被认为是最大的. 在降序排序时(descending)这会让你非常头大,因为 null值排在了最前面。

下面这个查询是为了根据得分显示用户排名, 但它将没有得分的用户排到了最前面!

select name, points

from users

order by 2 desc;

– points 为 null 的记录排在所有记录之前!

解决这类问题有两种思路。最简单的一种是用 coalesce 消除 null的影响:

– 在输出时将 null 转换为 0 :

select name, coalesce(points, 0)

from users

order by 2 desc;

– 输出时保留 null, 但排序时转换为 0 :

select name, points

from users

order by coalesce(points, 0) desc;

还有一种方式需要数据库的支持,指定排序时将 null 值放在最前面还是最后面:

select name, coalesce(points, 0)

from users

order by 2 desc nulls last;

当然, null 也可以用来防止错误的发生,比如处理除数为0的数学运算错误。

被 0 除

除数为0是一个非常 egg-painfull 的错误。昨天还运行得好好的SQL,突然被0除一下子就出错了。一个常用的解决方法是先用 case 语句判断分母(denominator)是否为0,再进行除法运算。

select case when num_users = 0 then 0 

else total_sales/num_users end;

ase 语句的方式其实很难看,而且分母被重复使用了。如果是简单的情况还好,如果分母是个很复杂的表达式,那么悲剧就来了: 很难读,很难维护和修改,一不小心就是一堆BUG.

这时候我们可以看看 null 的好处. 使用 nullif 使得分母为0时变成 null. 这样就不再报错, num_users = 0 时返回结果变为 null.

select total_sales/nullif(num_users, 0);

nullif 是将其他值转为 null, 而Oracle的 nvl 是将 null 转换为其他值。

如果不想要 null,而是希望转换为 0 或者其他数, 则可以在前一个SQL的基础上使用 coalesce函数:

select coalesce(total_sales/nullif(num_users, 0), 0);

null 再转换回0

总结

Tony Hoare 也许会后悔自己的错误, 但至少 null 存在的问题很容易地就解决了. 那么快去练练新的大招吧,从此远离 null 挖出来的无效大坑(nullifying)!

来源:http://www.codeceo.com/article/sql-null.html

SQL中的Null深入研究分析的更多相关文章

  1. SQL中的NULL值

    除is [not] null之外,空值不满足任何查找条件.–如果null参与算术运算,则该算术表达式的值为null.–如果null参与比较运算,则结果可视为false.在SQL-92中可看成unkno ...

  2. 深入详解SQL中的Null

    深入详解SQL中的Null NULL 在计算机和编程世界中表示的是未知,不确定.虽然中文翻译为 “空”, 但此空(null)非彼空(empty). Null表示的是一种未知状态,未来状态,比如小明兜里 ...

  3. 深入具体解释SQL中的Null

    NULL 在计算机和编程世界中表示的是未知,不确定.尽管中文翻译为 "空", 但此空(null)非彼空(empty). Null表示的是一种未知状态.未来状态,比方小明兜里有多少钱 ...

  4. (转) SQL 中的 NULL 你真的懂了吗?【数据库|SQL】

    注:转载自下面链接 https://blog.csdn.net/lnotime/article/details/104847946 SQL 中的 NULL (译自 NULL Values in SQL ...

  5. sql 中的null值

    1.包含null的表达式都为空 select salary*12+nvl(bonus,0) nvl是虑空函数 2. null值永远!=null select  * from emp  where bo ...

  6. 关于动态SQL中的NULL

    declare v_sql ); v_c1 number; v_c2 number; begin v_c2 :; v_sql := 'begin '; v_sql := v_sql||'update ...

  7. SQL 中【NULL】和【无】烦躁的问题

    很烦躁,烦躁的很,总结一下. 先简单的说下: NULL   : 不确定的东西 无       :没有东西 复杂的见下文....... 一 .null值 下面举个最简单的例子,平常工作当中肯定比这个sq ...

  8. sql 中 '' 与 null 的区别

    SELECT * from t_company where content = '' SELECT * from t_company where content is null update t_co ...

  9. 【原创】6. 在MYSQL++中实现SQL语法中的NULL

    这次要说明的是在MYSQL++中为了实现SQL中的NULL而做出的一系列的举措.我的感觉是Null<T, B>类型通常出现在SSQLS和template Query中比较多. 1. 什么是 ...

随机推荐

  1. Sprint计划会议1

    会议时间:4.15.晚9点 会议地点:学一食堂2楼 会议进程 • 首先我们讨论了实验第一个Sprint1要实现的功能(用户登录及信息录入).•  之后对任务进行了认领.•  最后每个人对自己的任务进行 ...

  2. nodejs笔记三--url处理、Query String;

    URL--该模块包含用以 URL 解析的实用函数. 使用 require('url') 来调用该模块. 一.parse函数的基础用法 parse函数的作用是解析url,返回一个json格式的数组,请看 ...

  3. pxe+preseed安装配置(2)

    1.tftp使用独立daemon,这样不用再安装xinet, apt-get install tftp-hpa tftpd-hpa cat /etc/default/tftpd-hpa TFTP_US ...

  4. Mac下安装及配置Eclipse

    1.安装Eclipse前先确认你的Mac上是否已安装java运行环境.进入终端,输入”java -version”,如果返回了java版本号则说明已安装,否则,请先安装java运行环境: 2.访问ec ...

  5. c语言编程之二叉排序树

    二叉排序树,又称为二叉查找树.它是一颗空树,或者是具有下面的性质的二叉树: 1.若它的左子树不空,则左子树上所有节点的值均小于它的根结构的值: 2.若它的右子树不空,则右子树上所有节点的值均大于它的根 ...

  6. Entity Framework技术导游系列开篇与热身

    在微软平台写程序有年头了,随着微软数据存取技术的持续演化,我在程序中先后使用过ODBC.DAO.ADO.ADO.NET.LINQ to SQL. Entity Framework这些技术. 近几年来, ...

  7. 【Longest Valid Parentheses】cpp

    题目: Given a string containing just the characters '(' and ')', find the length of the longest valid ...

  8. 【BZOJ】【2648】SJY摆棋子&【BZOJ】【2716】【Violet 3】天使玩偶

    KD-Tree 传说中的kd树...前去膜拜了一下……写道模板题>_< 写kdtree的一些感想: 插入的时候是像可持久化线段树一样直接在最后开新节点,然后更新它所在的块.. 然而其实也是 ...

  9. 【BZOJ】【1027】【JSOI2007】合金

    计算几何/凸包/Floyd Orz rausen大爷太强辣 计算几何题目果然不会做>_> 这个题……虽然他给了3个坐标,但实际上是个二维的计算几何题= =因为第三维坐标可以直接用前两维坐标 ...

  10. Poj 1050 分类: Translation Mode 2014-04-04 09:31 103人阅读 评论(0) 收藏

    To the Max Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 39058   Accepted: 20629 Desc ...