AD:2013云计算架构师峰会课程资料下载 安全
【51CTO经典译文】#10. 搜索一个“NULL”值 架构
- SELECT *
- FROM a
- WHERE a.column = NULL
在SQL中,NULL什么也不等于,并且NULL也不等于NULL。这个查询不会返回任何结果的,实际上,当构建那个plan的时候,优化器会把这样的语句优化掉。函数
当搜索NULL值的时候,应该使用这样的查询:优化
- SELECT *
- FROM a
- WHERE a.column IS NULL
#9. 使用附加条件的LEFT JOIN云计算
- SELECT *
- FROM a
- LEFT JOIN
- b
- ON b.a = a.id
- WHERE b.column = 'something'
除了从a返回每一个记录(至少一次),当没有真正匹配的记录的时候,用NULL值代替缺失的字段以外,LEFT JOIN和INNER JOIN都是同样的。spa
可是,在LEFT JOIN以后才会检查WHERE条件,因此,上面这个查询在链接以后才会检查column。就像咱们刚才了解到的那样,非NULL值才能够知足相等条件,因此,在a的记录中,那些在b中没有对应的条目的记录不可避免地要被过滤掉。code
从本质上来讲,这个查询是一个INNER JOIN,只是效率要低一些。排序
为了真正地匹配知足b.column = 'something'条件的记录(这时要返回a中的所有记录,也就是说,不过滤掉那些在b中没有对应的条目的记录),这个条件应该放在ON子句中:索引
- SELECT *
- FROM a
- LEFT JOIN
- b
- ON b.a = a.id
- AND b.column = 'something'
#8. 小于一个值,可是不为NULL文档
我常常看到这样的查询:
- SELECT *
- FROM b
- WHERE b.column < 'something'
- AND b.column IS NOT NULL
实际上,这并非一个错误:这个查询是有效的,是故意这样作的。可是,这里的IS NOT NULL是冗余的。
若是b.column是NULL,那么没法知足b.column < 'something'这个条件,由于任何一个和NULL进行的比较都会被断定为布尔NULL,是不会经过过滤器的。
有趣的是,这个附加的NULL检查不能和“大于”查询(例如:b.column > 'something')一块儿使用。
这是由于,在MySQL中,在ORDER BY的时候,NULL会排在前面,所以,一些人错误地认为NULL比任何其余的值都要小。
这个查询能够被简化:
- SELECT *
- FROM b
- WHERE b.column < 'something'
在b.column中,不可能返回NULL
#7. 按照NULL来进行链接
- SELECT *
- FROM a
- JOIN b
- ON a.column = b.column
在两个表中,当column是nullable的时候,这个查询不会返回两个字段都是NULL的记录,缘由如上所述:两个NULL并不相等。
这个查询应该这样来写:
- SELECT *
- FROM a
- JOIN b
- ON a.column = b.column
- OR (a.column IS NULL AND b.column IS NULL)
MySQL的优化器会把这个查询当成一个“等值链接”,而后提供一个特殊的链接条件:ref_or_null
#6. NOT IN和NULL值
- SELECT a.*
- FROM a
- WHERE a.column NOT IN
- (
- SELECT column
- FROM b
- )
若是在b.column中有一个NULL值,那么这个查询是不会返回任何结果的。和其余谓词同样,IN 和 NOT IN 遇到NULL也会被断定为NULL。
你应该使用NOT EXISTS重写这个查询:
- SELECT a.*
- FROM a
- WHERE NOT EXISTS
- (
- SELECT NULL
- FROM b
- WHERE b.column = a.column
- )
不像IN,EXISTS老是被断定为true或false的。
#5. 对随机的样本进行排序
- SELECT *
- FROM a
- ORDER BY
- RAND(), column
- LIMIT 10
这个查询试图选出10个随机的记录,按照column来排序。
ORDER BY会按照天然顺序来对输出结果进行排序:这就是说,当第一个表达式的值相等的时候,这些记录才会按照第二个表达式来排序。
可是,RAND()的结果是随机的。要让RAND()的值相等是行不通的,因此,按照RAND()排序之后,再按照column来排序也是没有意义的。
要对随机的样本记录进行排序,可使用这个查询:
- SELECT *
- FROM (
- SELECT *
- FROM mytable
- ORDER BY
- RAND()
- LIMIT 10
- ) q
- ORDER BY
- column
#4. 经过一个组来选取任意的记录
这个查询打算经过某个组(定义为grouper来)来选出一些记录
- SELECT DISTINCT(grouper), a.*
- FROM a
DISTINCT不是一个函数,它是SELECT子句的一部分。它会应用到SELECT列表中的全部列,实际上,这里的括号是能够省略的。因此,这个查询可能会选出grouper中的值都相同的记录(若是在其余列中,至少有一个列的值是不一样的)。
有时,这个查询能够正常地使用( 这主要依赖于MySQL对GROUP BY的扩展):
- SELECT a.*
- FROM a
- GROUP BY
- grouper
在某个组中返回的非聚合的列能够被任意地使用。
首先,这彷佛是一个很好的解决方案,可是,它存在着一个很严重的缺陷。它依赖于这样一个假设:虽然能够经过组来任意地获取,可是返回的全部值都要属于一条记录。
虽然当前的实现彷佛就是这样的,可是它并无文档化,不管什么时候,它都有可能被改变(尤为是,当MySQL学会了在GROUP BY的后面使用index_union的时候)。因此依赖于这个行为并不安全。
若是MySQL支持分析函数的话,这个查询能够很容易地用另外一种更清晰的方式来重写。可是,若是这张表拥有一个PRIMARY KEY的话,即便不使用分析函数,也能够作到这一点:
- SELECT a.*
- FROM (
- SELECT DISTINCT grouper
- FROM a
- ) ao
- JOIN a
- ON a.id =
- (
- SELECT id
- FROM a ai
- WHERE ai.grouper = ao.grouper
- LIMIT 1
- )
#3. 经过一个组来选取第一条记录
把前面那个查询稍微变化一下:
- SELECT a.*
- FROM a
- GROUP BY
- grouper
- ORDER BY
- MIN(id) DESC
和前面那个查询不一样,这个查询试图选出id值最小的记录。
一样:没法保证经过a.*返回的非聚合的值都属于id值最小的那条记录(或者任意一条记录)
这样作会更清晰一些:
- SELECT a.*
- FROM (
- SELECT DISTINCT grouper
- FROM a
- ) ao
- JOIN a
- ON a.id =
- (
- SELECT id
- FROM a ai
- WHERE ai.grouper = ao.grouper
- ORDER BY
- ai.grouper, ai.id
- LIMIT 1
- )
这个查询和前面那个查询相似,可是使用额外的ORDER BY能够确保按id来排序的第一条记录会被返回。
#2. IN和‘,’——值的分隔列表
这个查询试图让column的值匹配用‘,’分隔的字符串中的任意一个值:
- SELECT *
- FROM a
- WHERE column IN ('1, 2, 3')
这不会正常发挥做用的,由于在IN列表中,那个字符串并不会被展开。
若是列column是一个VARCHAR,那么它(做为一个字符串)会和整个列表(也做为一个字符串)进行比较,固然,这不可能匹配。若是 column是某个数值类型,那么这个列表会被强制转换为那种数值类型(在最好的状况下,只有第一项会匹配)
处理这个查询的正确方法应该是使用合适的IN列表来重写它:
- SELECT *
- FROM a
- WHERE column IN (1, 2, 3)
或者,也可使用内联:
- SELECT *
- FROM (
- SELECT 1 AS id
- UNION ALL
- SELECT 2 AS id
- UNION ALL
- SELECT 3 AS id
- ) q
- JOIN a
- ON a.column = q.id
可是,有时这是不可能的。
若是不想改变那个查询的参数,可使用FIND_IN_SET:
- SELECT *
- FROM a
- WHERE FIND_IN_SET(column, '1,2,3')
可是,这个函数不能够利用索引从表中检索行,会在a上执行全表扫描。
#1. LEFT JOIN和COUNT(*)
- SELECT a.id, COUNT(*)
- FROM a
- LEFT JOIN
- b
- ON b.a = a.id
- GROUP BY
- a.id
这个查询试图统计出对于a中的每条记录来讲,在b中匹配的记录的数目。
问题是,在这样一个查询中,COUNT(*)永远不会返回一个0。对于a中某条记录来讲,若是没有匹配的记录,那么那条记录仍是会被返回和计数。
只有须要统计b中的记录数目的时候才应该使用COUNT。既然可使用COUNT(*),那么咱们也可使用一个参数来调用它(忽略掉NULL),咱们能够把b.a传递给它。在这个例子中,做为一个链接主键,它不能够为空,可是若是不想匹配,它也能够为空。
原文标题:10 things in MySQL (that won’t work as expected)