PostgreSQL 数据库NULL值的默认排序行为与查询、索引定义规范 - nulls first\last, asc\desc
背景
在数据库中NULL值是指UNKNOWN的值,不存储任何值,在排序时,它排在有值的行前面还是后面通过语法来指定。
例如
-- 表示null排在有值行的前面
select * from tbl order by id nulls first;
-- 表示null排在有值行的后面
select * from tbl order by id nulls last;
同时对于有值行,可以指定顺序排还是倒序排。
-- 表示按ID列顺序排
select * from tbl order by id [asc];
-- 表示按ID列倒序排
select * from tbl order by id desc;
默认的排序规则如下:
desc nulls first : null large small
asc nulls last : small large null
当nulls [first|last]与asc|desc组合起来用时,是这样的。
值的顺序如下:
1、DEFAULT:(认为NULL比任意值都大)
desc nulls first : 顺序:null large small
asc nulls last : 顺序:small large null
2、NON DEFAULT: (认为NULL比任意值都小)
desc nulls last : 顺序:large small null
asc nulls first : 顺序:null small large
由于索引是固定的,当输入排序条件时,如果排序条件与索引的排序规则不匹配时,会导致无法使用索引的实惠(顺序扫描)。导致一些不必要的麻烦。
索引定义与扫描定义不一致引发的问题
1、建表,输入测试数据
create table cc(id int not null);
insert into cc select generate_series(1,1000000);
2、建立索引(使用非默认配置,null比任意值小)
create index idx_cc on cc (id asc nulls first);
或
create index idx_cc on cc (id desc nulls last);
3、查询,与索引定义的顺序(指NULL的相对位置)不一致时,即使使用索引,也需要重新SORT。
select * from table order by id desc nulls first limit 1;
select * from table order by id [asc] nulls last limit 1;
用到了额外的SORT
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from cc order by id limit 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=27969.43..27969.43 rows=1 width=4) (actual time=263.972..263.972 rows=1 loops=1)
Output: id
Buffers: shared hit=7160
-> Sort (cost=27969.43..30469.43 rows=1000000 width=4) (actual time=263.970..263.970 rows=1 loops=1)
Output: id
Sort Key: cc.id
Sort Method: top-N heapsort Memory: 25kB
Buffers: shared hit=7160
-> Bitmap Heap Scan on public.cc (cost=8544.42..22969.42 rows=1000000 width=4) (actual time=29.927..148.733 rows=1000000 loops=1)
Output: id
Heap Blocks: exact=4425
Buffers: shared hit=7160
-> Bitmap Index Scan on idx_cc (cost=0.00..8294.42 rows=1000000 width=0) (actual time=29.380..29.380 rows=1000000 loops=1)
Buffers: shared hit=2735
Planning time: 0.098 ms
Execution time: 264.009 ms
(16 rows)
3、查询,与索引定义一致(指NULL的相对位置)时,索引有效,不需要额外SORT。
select * from table order by id desc nulls last limit 1;
select * from table order by id [asc] nulls first limit 1;
不需要额外SORT
postgres=# explain (analyze,verbose,timing,costs,buffers) select * from cc order by id nulls first limit 1;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------
Limit (cost=0.42..0.45 rows=1 width=4) (actual time=0.014..0.014 rows=1 loops=1)
Output: id
Buffers: shared hit=4
-> Index Only Scan using idx_cc on public.cc (cost=0.42..22719.62 rows=1000000 width=4) (actual time=0.013..0.013 rows=1 loops=1)
Output: id
Heap Fetches: 1
Buffers: shared hit=4
Planning time: 0.026 ms
Execution time: 0.022 ms
(9 rows)
小结
在PostgreSQL中顺序、倒序索引是通用的。不同的是null的相对位置。
因此在创建索引时,务必与业务的需求对齐,使用一致的NULL相对顺序(nulls first 或 nulls last 与asc,desc的搭配)(即NULL挨着large value还是small value),而至于值的asc, desc实际上是无所谓的。
如果业务需求的顺序与索引的顺序不一致(指null的相对顺序),那么会导致索引需要全扫,重新SORT的问题。
内核改进
1、当约束设置了not null时,应该可以不care null的相对位置,因为都没有NULL值了,优化器应该可以不管NULL的相对位置是否与业务请求的SQL的一致性,都选择非Sort模式扫描。
2、改进索引扫描方法,支持环形扫描。
参考:
https://github.com/digoal/blog/blob/master/201711/20171111_02.md
注:
- 如果创建索引时,没有指定null的内容,但where条件部分又使用到了null的排序,那么要将asc|desc 与 last|first对应好,默认对应的操作是:
desc nulls first : null large small
asc nulls last : small large null
在没有指定null的索引中,按照上面方法对应好即可。
下面是几个测试:
swrd=# \d cc
Table "swrd.cc"
Column | Type | Modifiers
--------+---------+-----------
id | integer | not null
Indexes:
"cc_id_idx" btree (id)
swrd=# explain (analyze,verbose,timing,costs,buffers) select * from cc order by id desc nulls first;
QUERY PLAN
---------------------------------------------------------------------------------------------------------------------------------------------------
Index Only Scan Backward using cc_id_idx on swrd.cc (cost=0.42..30408.42 rows=1000000 width=4) (actual time=0.044..297.796 rows=1000000 loops=1)
Output: id
Heap Fetches: 1000000
Buffers: shared hit=7159 read=1
Planning time: 0.113 ms
Execution time: 387.645 ms
(6 rows)
Time: 388.438 ms
swrd=# explain (analyze,verbose,timing,costs,buffers) select * from cc order by id desc nulls last;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
Sort (cost=127757.34..130257.34 rows=1000000 width=4) (actual time=666.996..926.348 rows=1000000 loops=1)
Output: id
Sort Key: cc.id DESC NULLS LAST
Sort Method: external merge Disk: 13640kB
Buffers: shared hit=4425, temp read=2334 written=2334
-> Seq Scan on swrd.cc (cost=0.00..14425.00 rows=1000000 width=4) (actual time=0.020..147.384 rows=1000000 loops=1)
Output: id
Buffers: shared hit=4425
Planning time: 0.110 ms
Execution time: 1027.649 ms
(10 rows)
会发现默认使用没有配置null的索引,但是在where条件中使用到了null,如果不是按照默认的对应顺序使用,则数据库会额外排序,无法使用到索引本身的排序功能。
- 而对于在创建索引时,指定了null选项,则在where条件中和索引指定的null一致即可。
PostgreSQL 数据库NULL值的默认排序行为与查询、索引定义规范 - nulls first\last, asc\desc的更多相关文章
- 关于数据库NULL值的几个问题思考
最近在写项目,拼接SQL时,发现好多关于NULL值的问题,现在把这些问题整理出来,以供日后参考. 对于Oracle数据库: 一.排序 Oracle对于null值的排序,有一个函数可以进行操作: 在默认 ...
- mysq对存在null值的字段排序
1.建立学生表,建表sql如下: ),age int); 2.插入几条数据,包括id字段值为null的 ,),(,),(,),(),(); 3.我们查询下,可以看到存在id字段为空的值: 4.对学生表 ...
- MYSQL数据库性能调优之四:解决慢查询--索引
为什么索引能够提高查询速度?没有索引 检索数据的方式是从头到尾一条一条挨着匹配,这是慢的根本原因:索引类型BTREE:二叉树类型,原理图如下:对表创建一个二叉树,记录中间数据的物理磁盘地址,二叉树检索 ...
- SQL Server 排序的时候使 null 值排在最后
https://www.cnblogs.com/Brambling/p/7046148.html 最近遇到一个 SQL Server 排序的问题,以前也没了解过,然后这次碰到了. 才发现 SQL Se ...
- postgreSQL数据库的初探
kali是黑客的强大武器,还有一个也是哦——Metasploit postgreSQL数据库是Metasploit的默认数据库哦! 启动postgresql: service postgresql s ...
- MYSQL NULL值特性
NULL是一种“没有类型”的值,通常表示“无值”,“未知值”,“缺失值”,“超界”,“不在其中”等,我们在日常运用中很容易和NULL字符串混淆,这里大致整理了下NULL值的一些特性,以便能够正确使用N ...
- 扩展我们的分析处理服务(Smartly.io):使用 Citus 对 PostgreSQL 数据库进行分片
原文:Scaling Our Analytical Processing Service: Sharding a PostgreSQL Database with Citus 在线广告商正在根据绩效数 ...
- Oracle中NULL值与索引
NULL值是关系数据库系统布尔型(true,false,unknown)中比较特殊类型的一种值,通常称为UNKNOWN或空值,即是未知的,不确定的.由于NULL存在着无数的可能,因此NULL值也不等于 ...
- Oracle NULL值
NULL值,用来描述记录中没有定义内容的字段值.在Oracle中,判断某个条件的值时,返回值可能是TRUE.FALSE或UNKNOWN. 如果查询一个列的值是否等于20,而该列的值为NULL,那么就是 ...
随机推荐
- git 上传本地项目
一.下载和安装git 官网下载,默认一直点下一步安装. https://git-scm.com/ 二.在www.gitee.com 注册一个账号 1.点击加号“+”在gitee中新建一个项目 2.下面 ...
- 先装IIS后装.Net Framework
1.动态页面和静态页面的区别 动态页面(动态网站):通过C#代码(或别的语言)与服务器的交互的实现(比如新建一个ashx一般处理程序中的C#代码就可以和服务器实现交互,修改数据库,上传图片等都属于和服 ...
- linux系统mysql主从配置
一.原理 mysql主从配置的流程大体如图: 1)master会将变动记录到二进制日志里面: 2)master有一个I/O线程将二进制日志发送到slave; 3) slave有一个I/O线程把mast ...
- JavaScript中的三种弹窗
---恢复内容开始--- 1.警告窗口(alert) <script type="text/javascript"> alert("我是警告弹框!" ...
- mapper加载的3种方法
<!-- mapper加载有3种方法: 1:通过resource或url加载单个mapper 2:通过mapper接口类名加载单个mapper 3:通过package批量加载多个mapper(推 ...
- js 返回小数点后几位
function fmoney(s, n) //s:传入的float数字 ,n:希望返回小数点几位 { n = n > 0 && n <= 20 ? n : 2; s = ...
- CentOS-7.2安装Ambari-2.6.1
CentOS-7.2安装Ambari-2.6.1 一.Ambari 是什么? Ambari 跟 Hadoop 等开源软件一样,也是 Apache Software Foundation 中的一个项目, ...
- java程序存入数据库中文乱码解决方案
一.问题描述 背景:代码迁移,ssm框架在插入数据到mysql数据库时,中文乱码.代码中的编码配置没有问题,因为该项目代码以前使用过,没有问题.现在换了数据库,数据库配置也做了修改,统一使用utf8, ...
- 三星450R5J windows8.1系统重装小结
本人一台三星450R5J,到今年也差不多五六年了.虽然颜值很高,但是用久了真的不行,毕竟是属于商务型笔记本,这里我就不晒配置了. 比较一下四五年前的三星与现在使用的华硕,三星看起来更鲜. 准 ...
- python 爬虫爬取内容时, \xa0 、 \u3000 的含义
最近用 scrapy 爬某网站,发现拿到的内容里面含有 \xa0 . \u3000 这样的字符,起初还以为是编码不对,搜了一下才知道是见识太少 233 . \xa0 是不间断空白符 我们通常所用的 ...