postgresql----唯一索引,表达式索引,部分索引

一.唯一索引

唯一索引字面上理解就是在索引上增加唯一约束,不允许出现索引值相同的行,目前只有Btree索引可以声明唯一索引,唯一键会自动创建唯一索引。

测试表:

test=# create table tbl_unique_index(a int, b int);
CREATE TABLE

示例1.创建唯一索引,相等数据只允许插入一行,NULL除外,因为NULL不等于NULL。

test=# create unique index idx_unq_tbl_unique_index_a_b on tbl_unique_index using btree (a,b);
CREATE INDEX
test=# \d tbl_unique_index
Table "public.tbl_unique_index"
 Column |  Type   | Modifiers
--------+---------+-----------
 a      | integer |
 b      | integer |
Indexes:
    "idx_unq_tbl_unique_index_a_b" UNIQUE, btree (a, b)
test=# insert into tbl_unique_index values (1,1);
INSERT 0 1
test=# insert into tbl_unique_index values (1,1);
ERROR:  duplicate key value violates unique constraint "idx_unq_tbl_unique_index_a_b"
DETAIL:  Key (a, b)=(1, 1) already exists.
test=# insert into tbl_unique_index values (1);
INSERT 0 1
test=# insert into tbl_unique_index values (1);
INSERT 0 1
test=# insert into tbl_unique_index values (1);
INSERT 0 1

示例2.唯一键会自动创建唯一索引

test=# truncate table tbl_unique_index ;
TRUNCATE TABLE
test=# alter table tbl_unique_index add constraint pk_tbl_unique_index_a primary key(a);
ALTER TABLE
test=# alter table tbl_unique_index add constraint uk_tbl_unique_index_b unique(b);
ALTER TABLE
test=# \d tbl_unique_index
Table "public.tbl_unique_index"
 Column |  Type   | Modifiers
--------+---------+-----------
 a      | integer | not null
 b      | integer |
Indexes:
    "pk_tbl_unique_index_a" PRIMARY KEY, btree (a)
    "idx_unq_tbl_unique_index_a_b" UNIQUE, btree (a, b)
    "uk_tbl_unique_index_b" UNIQUE CONSTRAINT, btree (b)

二.表达式索引

除针对表的字段直接创建索引外,还可以对字段进行某种运算之后的结果创建索引。

测试表

test=# create table tbl_expression(a varchar(32), b varchar(32));
CREATE TABLE
test=# insert into tbl_expression select concat(‘test‘,x),concat(‘you‘,x) from generate_series(1,10000) x;
INSERT 0 10000

如果此时分别在a和b字段上各创建一个Btree索引,分别使用a和b字段查询时会进行索引扫描。

test=# create index idx_tbl_expression_a on tbl_expression using btree (a);
CREATE INDEX
test=# create index idx_tbl_expression_b on tbl_expression using btree (b);
CREATE INDEX
test=#
test=# explain analyze select * from tbl_expression where a = ‘TEST‘;
                                                              QUERY PLAN                                                             

-------------------------------------------------------------------------------------------------------------------------------------
-
 Index Scan using idx_tbl_expression_a on tbl_expression  (cost=0.29..8.30 rows=1 width=15) (actual time=0.130..0.130 rows=0 loops=1)
   Index Cond: ((a)::text = ‘TEST‘::text)
 Planning time: 0.667 ms
 Execution time: 0.168 ms
(4 rows)

test=# explain analyze select * from tbl_expression where b = ‘you‘;
                                                              QUERY PLAN                                                             

-------------------------------------------------------------------------------------------------------------------------------------
-
 Index Scan using idx_tbl_expression_b on tbl_expression  (cost=0.29..8.30 rows=1 width=15) (actual time=0.171..0.171 rows=0 loops=1)
   Index Cond: ((b)::text = ‘you‘::text)
 Planning time: 0.126 ms
 Execution time: 0.206 ms
(4 rows)

但是下面的两种查询方式是不会进行索引扫描的

select * from tbl_expression where upper(a) = ‘TEST‘;
select * from tbl_expression where (a || ‘ ‘ ||b) = ‘test you‘;
test=# explain analyze select * from tbl_expression where upper(a) = ‘TEST‘;
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_expression  (cost=0.00..166.00 rows=50 width=15) (actual time=5.957..5.957 rows=0 loops=1)
   Filter: (upper((a)::text) = ‘TEST‘::text)
   Rows Removed by Filter: 10000
 Planning time: 0.140 ms
 Execution time: 6.014 ms
(5 rows)

test=#
test=# explain analyze select * from tbl_expression where (a || ‘ ‘ ||b) = ‘test you‘;
                                                 QUERY PLAN
------------------------------------------------------------------------------------------------------------
 Seq Scan on tbl_expression  (cost=0.00..191.00 rows=50 width=15) (actual time=7.851..7.851 rows=0 loops=1)
   Filter: ((((a)::text || ‘ ‘::text) || (b)::text) = ‘test you‘::text)
   Rows Removed by Filter: 10000
 Planning time: 0.114 ms
 Execution time: 7.883 ms
(5 rows)

此时就可以使用表达式创建索引来解决此类全表扫描问题。

test=# explain analyze select * from tbl_expression where upper(a) = ‘TEST‘;
                                                             QUERY PLAN
------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on tbl_expression  (cost=4.67..21.42 rows=50 width=15) (actual time=0.133..0.133 rows=0 loops=1)
   Recheck Cond: (upper((a)::text) = ‘TEST‘::text)
   ->  Bitmap Index Scan on idx_tbl_expression_upper_a  (cost=0.00..4.66 rows=50 width=0) (actual time=0.129..0.129 rows=0 loops=1)
         Index Cond: (upper((a)::text) = ‘TEST‘::text)
 Planning time: 0.565 ms
 Execution time: 0.175 ms
(6 rows)
test=# create index idx_tbl_expression_a_b on tbl_expression ((a||‘ ‘||b));
CREATE INDEX
test=# explain analyze select * from tbl_expression where (a || ‘ ‘ ||b) = ‘test you‘;
                                                           QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on tbl_expression  (cost=4.67..21.55 rows=50 width=15) (actual time=0.130..0.130 rows=0 loops=1)
   Recheck Cond: ((((a)::text || ‘ ‘::text) || (b)::text) = ‘test you‘::text)
   ->  Bitmap Index Scan on idx_tbl_expression_a_b  (cost=0.00..4.66 rows=50 width=0) (actual time=0.128..0.128 rows=0 loops=1)
         Index Cond: ((((a)::text || ‘ ‘::text) || (b)::text) = ‘test you‘::text)
 Planning time: 0.582 ms
 Execution time: 0.187 ms
(6 rows)

但是还是需要根据实际业务情况仔细评估后决定采用何种索引,因为并不是索引越多越好。

三.部分索引

只在自己感兴趣的那部分数据上创建索引,而不是对每一行数据都创建索引,此种方式创建索引就需要使用WHERE条件了。

创建两个完全相同的表比较部分索引和全索引的区别。

测试表

test=# create table tbl_partial_index(id bigint,alarm_time timestamp without time zone,level varchar(12),alarm_desc varchar(100));
CREATE TABLE
test=# create table tbl_partial_index1(id bigint,alarm_time timestamp without time zone,level varchar(12),alarm_desc varchar(100));
CREATE TABLE

写入完全相同的数据

test=# insert into tbl_partial_index(id,alarm_time,level,alarm_desc) select generate_series(1,9000000),clock_timestamp()::timestamp without time zone,‘green‘,‘正常‘;
INSERT 0 9000000
test=# insert into tbl_partial_index(id,alarm_time,level,alarm_desc) select generate_series(9000000,9000100),clock_timestamp()::timestamp without time zone,‘red‘,‘攻击‘;
INSERT 0 101
test=#
test=#
test=# insert into tbl_partial_index1(id,alarm_time,level,alarm_desc) select generate_series(1,9000000),clock_timestamp()::timestamp without time zone,‘green‘,‘正常‘;
INSERT 0 9000000
test=# insert into tbl_partial_index1(id,alarm_time,level,alarm_desc) select generate_series(9000000,9000100),clock_timestamp()::timestamp without time zone,‘red‘,‘攻击‘;
INSERT 0 101

示例1.在tbl_partial_index表字段level上创建索引

test=# create index idx_tbl_partial_index_level on tbl_partial_index using btree (level);
CREATE INDEX
Time: 31407.356 ms
test=#
test=# explain analyze select * from tbl_partial_index where level = ‘red‘;
                                                                    QUERY PLAN                                                       

-------------------------------------------------------------------------------------------------------------------------------------
-------------
 Index Scan using idx_tbl_partial_index_level on tbl_partial_index  (cost=0.43..4.45 rows=1 width=29) (actual time=0.069..0.087 rows=
101 loops=1)
   Index Cond: ((level)::text = ‘red‘::text)
 Planning time: 0.268 ms
 Execution time: 0.124 ms
(4 rows)

Time: 23.460 ms
test=# select relname,pg_size_pretty(pg_relation_size(oid)) from pg_class where relname=‘idx_tbl_partial_index_level‘;
           relname           | pg_size_pretty
-----------------------------+----------------
 idx_tbl_partial_index_level | 191 MB
(1 row)

Time: 71.799 ms

示例2.在tbl_partial_index1表字段level等于red的行上创建索引

test=# create index idx_tbl_partial_index1_level on tbl_partial_index1(level) where level = ‘red‘;
CREATE INDEX
Time: 5558.905 ms
test=# explain analyze select * from tbl_partial_index1 where level = ‘red‘;
                                                                     QUERY PLAN                                                      

-------------------------------------------------------------------------------------------------------------------------------------
---------------
 Index Scan using idx_tbl_partial_index1_level on tbl_partial_index1  (cost=0.14..4.16 rows=1 width=29) (actual time=0.051..0.082 row
s=101 loops=1)
 Planning time: 18.922 ms
 Execution time: 0.136 ms
(3 rows)

Time: 19.929 ms
test=# select relname,pg_size_pretty(pg_relation_size(oid)) from pg_class where relname=‘idx_tbl_partial_index1_level‘;
           relname            | pg_size_pretty
------------------------------+----------------
 idx_tbl_partial_index1_level | 64 kB
(1 row)

Time: 0.950 ms

比较上面两个示例的结果可知,全表索引在耗时和大小方面要比部分索引消耗更多的资源,查询‘red‘的数据排除环境影响基本相同,数据量更大,‘red‘占比更小时性能可能会有明显差异,但是查询非‘red‘数据时全表索引会有明显的性能优势,因为部分索引并没有‘green‘数据的索引,走的是全表扫描。

综上,根据数据的使用方式创建不同的索引在性能上是有明显差异的。

时间: 2024-10-10 11:08:43

postgresql----唯一索引,表达式索引,部分索引的相关文章

主键,组合主键,聚集索引,非聚集索引,唯一索引

前言: 基于Oracle数据库谈谈索引们的问题,以及在什么情况下使用什么索引, 组合主键,怎么根据实际业务需求来定制自己的索引,主键的应用,来提升系统的性能. 1:主键? 在表中唯一的,也是聚集索引.作用于快速查询.该列唯一. Java代码 复制代码 收藏代码 1.ID NUMBER(38,0) PRIMARY KEY NOT NULL, 2:组合主键? 在表中以多个字段组成在表中是唯一的,也是聚集索引.作用于快速查询.该组合列唯一. Java代码 复制代码 收藏代码 1.CREATE TABL

唯一索引、聚集索引和费聚集索引

1.一个表只能有一个主索引-PRIMARY,且只有是数据库表才有主索引,后缀为.CDX,索引关键字是不可以重复的.哪怕是空记录也只可以有一条.2.候选索引可以有很多个,索引关键字同样不可以重复,同样只存在于数据库表.3.唯一索引,可以存在于自由表,但索引关键字不可以重复.4.普通索引简单的理解就是只起排序作用.索引关键字是可以重复的.可存在于自由表. 主键与唯一索引的区别 主键是一种约束,唯一索引是一种索引,两者在本质上是不同的. 主键创建后一定包含一个唯一性索引,唯一性索引并不一定就是主键.

SQL存储原理及聚集索引、非聚集索引、唯一索引、主键约束的关系(补)

索引类型 1.          唯一索引:唯一索引不允许两行具有相同的索引值 2.          主键索引:为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型.主键索引要求主键中的每个值是唯一的,并且不能为空 3.          聚集索引(Clustered):表中各行的物理顺序与键值的逻辑(索引)顺序相同,每个表只能有一个 4.          非聚集索引(Non-clustered):非聚集索引指定表的逻辑顺序.数据存储在一个位置,索引存储在另一个位置,索引中包含指

唯一索引与主键索引的比较

唯一索引与主键索引的比较 唯一索引唯一索引不允许两行具有相同的索引值.如果现有数据中存在重复的键值,则大多数数据库都不允许将新创建的唯一索引与表一起保存.当新数据将使表中的键值重复时,数据库也拒绝接受此数据.例如,用户表中的身份证(idcard) 列上创建了唯一索引,则所有身份证不能重复 主键索引主键索引是唯一索引的特殊类型.数据库表通常有一列或列组合,其值用来唯一标识表中的每一行.该列称为表的主键.在数据库关系图中为表定义一个主键将自动创建主键索引,主键索引是唯一索引的特殊类型.主键索引要求主

SQL Server索引 - 聚集索引、非聚集索引、非聚集唯一索引 <第八篇>

聚集索引.非聚集索引.非聚集唯一索引 我们都知道建立适当的索引能够提高查询速度,优化查询.先说明一下,无论是聚集索引还是非聚集索引都是B树结构. 聚集索引默认与主键相匹配,在设置主键时,SQL Server会默认在主键列创建聚集索引.但是可以手动更改为在任意一个列创建聚集索引,然后在另一个字段或多个字段上定义主键.这时主键将会被作为一个唯一的非聚集索引(唯一索引)被创建.通过指定NONCLUSTERED关键字就可以做到. CREATE TABLE MyTableKeyExample { Colu

Mysql索引介绍及常见索引(主键索引、唯一索引、普通索引、全文索引、组合索引)的区别

Mysql各种索引区别:普通索引:最基本的索引,没有任何限制唯一索引:与"普通索引"类似,不同的就是:索引列的值必须唯一,但允许有空值.主键索引:它 是一种特殊的唯一索引,不允许有空值. 全文索引:仅可用于 MyISAM 表,针对较大的数据,生成全文索引很耗时好空间.组合索引:为了更多的提高mysql效率可建立组合索引,遵循”最左前缀“原则. Mysql索引概念:说说Mysql索引,看到一个很少比如:索引就好比一本书的目录,它会让你更快的找到内容,显然目录(索引)并不是越多越好,假如这

普通索引、唯一索引、主索引、外键索引、复合索引、非主码索引、聚集主码(聚集索引)、单列索引、多列索引

强烈建议看了第一个参考文献再来看这个篇博文,因为此处不准备讲底层数据结构的实现. 索引:索引(Index)是帮助MySQL高效获取数据的数据结构.提取句子主干,就可以得到索引的本质:索引是数据结构.其表达的是存储引擎的范畴,也就是说只有在存储引擎级别谈索引才有意义.MyISAM.InnoDB.Memory等.此处单纯就InnoDB存储引擎讨论. B-TREE.B+TREE数据结构. 在数据之外,数据库系统还维护着满足特定查找算 法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这

MySQL普通索引、唯一索引、主索引、外键索引、复合索引、全文索引的使用

1.普通索引 普通索引(由关键字KEY或INDEX定义的索引)的唯一任务是加快对数据的访问速度.因此,应该只为那些最经常出现在查询条件(WHERE column = ...)或排序条件(ORDER BY column)中的数据列创建索引.只要有可能,就应该选择一个数据最整齐.最紧凑的数据列(如一个整数类型的数据列)来创建索引. 2.唯一索引 普通索引允许被索引的数据列包含重复的值.比如说,因为人有可能同名,所以同一个姓名在同一个"员工个人资料"数据表里可能出现两次或更多次. 如果能确定

SQL有三个类型的索引,唯一索引 不能有重复,但聚集索引,非聚集索引可以有重复

重要: (1) SQL如果创建时候,不指定类型那么默认是非聚集索引 (2) 聚集索引和非聚集索引都可以有重复记录,唯一索引不能有重复记录. (3) 主键 默认是加了唯一约束的聚集索引,但是也可以在主键创建时,指定为唯一约束的非聚集索引,因此主键仅仅是默认加了唯一约束的聚集索引,不能说主键就是加了唯一约束的聚集索引 有点拗口,可以参考我的博客:主键就是聚集索引吗? 为列创建索引实际上就是为列进行排序,以方便查询.建立一个列的索引,就相当与建立一个列的排序. 主键是唯一的,所以创建了一个主键的同时,

postgresql 9.6 建立多列索引测试

建立测试表结构 CREATE TABLE t_test ( id integer, name text COLLATE pg_catalog."default", address character varying(500) COLLATE pg_catalog."default" ); 插入测试数据 insert into t_test SELECT generate_series(1,10000000) as key, 'name'||(random()*(10