- <h4>数据类型的转换</h4>
- 使用“类型名”加上单引号转换
- select bit '11110011';
- select int '1' + int '2';
- 支持标准SQL类型转换函数CAST进行类型转换
- select CAST('5' as int),CAST('2014-07-17',date);
- 更简洁的类型转换方式,双冒号的方式
- select '5'::int,'2014-07-17'::date;
- <h4>复合类型的使用</h4>
在PostgreSQL中可以如C语言中的结构体一样定义一个符合类型。
- 复合类型的定义
- CREATE TYPE complex AS (
r double precision,
i double precision
);
- 用复合类型创建表
- CREATE TABLE test_table (
id int,
coltage complex,
current complex,
remark text
);
- 用复合类型做函数的参数
```
CREATE FUNCTION complex_multi(complex, complex) RETURNS complex
AS
SELECTROW($1.r∗2.r−$1.i∗$2.i−$1.i∗$2.r)::complex
LANGUAGE SOL;
```
- 新增表数据
- insert into test_table(1,ROW(11.213,11.345),ROW(22.213,22.345),'测试1');
- insert into test_table(2,(33.213,33.345),(44.213,44.345),'测试2');
- insert into test_table(id, coltage.r, coltage.i, current.r, current.i,remark)
values(3,333.213,333.345,444.213,444.345,'测试3');
- 访问复合类型
- select coltage.r from test_table;
- 修改复合类型
- update test_table set coltage=(55.213,55.345) where id = 1;
- 自定义创建:使用“CREATE TYPE”创建一些Range类型,预发如下:
CREATE TYPE name AS RANGE(
SUBTYPE = subtype
[,SUBTYPE_OPCLASS = subtype_operator_class]
...
)
举例:
CREATE TYPE floatrange AS RANGE(
SUBTYPE = float8,
SUBTYPE_DIFF = float8mi
);
- 使用场景举例:IP地址查询
CREATE TABLE ipdb1(
ip_begin inet,
ip_end inet,
area text,
sp text
);
查询某个IP所属区域:select * from ipdb1 where ip_begin <= '115.195.180.105'::inet and ip_end >= '115.195.180.105'::inet;
- 使用Range类型<br>
CREATE TYPE inetrange AS RANGE (subtype = inet);<br>
CREATE TABLE ipdb2(
ip_range inetrange,
area text,
sp text
);<br>
insert into ipdb2 select ('['||ip_begin||','||ip_end||']')::inetrange,area,sp from ipdb1;<br>
创建gist索引:
CREATE INDEX idx_ipdb2_ip_range ON ipdb2 USING gist(ip_range);
查询:
select * from ipdb2 where ip_range @> '115.195.180.105'::inet;
- <h4>数组类型的使用</h4>
支持表的字段使用定长或可变长度的一维或多维数组,数组的类型可以是任何数据库内建的类型、用户自定义的类型、枚举类型,以及组合类型。
- 数组类型的声明<br>
CREATE TABLE testtab04(id int, col1 int[], col2 int[10], col3 text[][]);
- 输入数组值<br>
CREATE TABLE testtab05(id int, col1 int[]);<br>
insert into testtab05 values(1, '{1,2,3}');<br>
insert into testtab05 values(2, '{4,5,6}');<br>
CREATE TABLE testtab06(id int, col1 text[]);<br>
insert into testtab06 values(1, '{how,howe,howl}');<br>
insert into testtab05 values(2,'[2:4]={1,2,3}');指定数组下标
- 访问数组<br>
select id,col1[1] from testtab05;<br>
select id,col1[1:2] from testtab05;指定数组1、2小标数据
- 修改数组<br>
update testtab05 set col1 = '{7,8,9}' where id = 1;<br>
update testtab05 set col1[1] = 10 where id = 1;
- 数组聚合函数array_agg
- 附数组操作符:
- <h4>SIMILAR TO正则表达式</h4>
SIMILAR TO除下划线和百分号的使用与LIKE相同,还支持正则表达式查询。<br>
| 表示选择(二选一,如a|b,类似or)<br>
\* 表示重复前面项0次或多次,如'6\*1','(66)\*1'<br>
\+ 表示重复前面项1次或多次,如'6+1','(66)+1'<br>
[] 表示方括号内字符集中的任意一个字符,如[123]表示1或2或3中的1个,可以是1,也可以是2,还可以是3,但是只能是单个字符。
- 查询c字段值是'abc'或'ABc'的行 select * from tbl_insert where c similar to '(ab|AB)c';
- 查询c字段中以'1'结尾,前面有0个或多个'6'的行 select * from tbl_insert where c similar to '6*1';
- 查询字段c中以'1'结尾,前面是0到9之间任意一个数字的行 select * from tbl_insert where c similar to '[0-9]1';
- <h4>POSIX正则表达式</h4>
POSIX 正则表达式提供了比 LIKE 和 SIMILAR TO 操作符 更强大的模式匹配的方法。许多 Unix 工具,比如 egrep, sed,或 awk 使用一种与我们这里描述的类似的模式匹配语言。
- 正则表达式匹配操作符<br>
- <h4>索引创建</h4>
举例:CREATE TABLE contacts(
id int primary key,
name varchar(40),
phone varchar(32)[],
address text
);<br>
按name快速查询,新建btree索引
CREATE INDEX idx_contacts_name on contacts(name);<br>
按phone快速查询,由于是该字段是数组,btree索引不起作用,可以创建一个GIN索引
CREATE INDEX idx_contscts_phone on contacts using gin(phone);<br>
查询:select * from contacts where phone @> array['13333333333'::varchar(32)];
测试:
- <h4>并发索引创建</h4>
通常,创建索引的时候会锁定表以防止写入,然后对表做全面扫描,从而完成创建索引操作。在此过程中用户仍然可以读取表,但是写操作是会被阻塞的。如果数据量巨大,则创建索引可能需要持续几十分钟,在一般业务场景中是不可接受的。
PostgreSQL支持不阻塞创建索引方式,即通过在CREATE INDEX中加CONCURRENTLY选项来实现的。<br>
举例:
CREATE TABLE testtab01(id int primary key, note text, test varchar(32));<br>
常规索引,创建索引过程中会阻塞表写操作:
CREATE INDEX idx_testtab01_note on testtab01(note);
并发索引,创建索引的过程中可以对表进行写操作:
DROP INDEX idx_testtab01_note;
CREATE INDEX CONCURRENTLY idx_testtab01_note on testtab01(note);
- <h4>索引的特色</h4>
- 表达式上的索引<br>
PostgreSQL支持函数索引,索引的键可以是一个函数,还可以是从一个或多个字段甲酸出来的标量表达式。<br>
举例:CREATE TABLE indextest(id int, note text);<br>
select * from indextest where lower(note) = 'hello world';<br>
因为使用了函数,无法利用到note字段上的普通索引,所以这时需要建一个函数索引,如下:<br>
CREATE INDEX idx_ indextest_note ON indextest(lower(note));<br>
表达式上的索引不是在进行索引查找的时候计算表达式的,而是在插入数据行或更新数据行时进行计算的。<br>
如果把表达式上的索引声明为UNIQUE,如下:<br>
CREATE UNIQUE INDEX indextest_note ON indextest(lower(note));<br>
那么会禁止往note列中插入只有大小写区别而内容相同的数据行。因此,在表达式上的索引可以实现简单唯一约束无法实现的一些约束。
- 部分索引<br>
只在自己感兴趣的那部分数据上创建索引,而不是对每一行数据都创建索引,此种方式创建索引就需要使用WHERE条件了。<br>
举例:create index idx_tbl_partial_index1_level on tbl_partial_index1(level) where level = 'red';<br>
select * from tbl_partial_index1 where level = 'red';
- GiST索引<br>
几何类型检索,内置的SP-GiST索引操作类:box,circle,inet,point,range,tsquery,tsvector
- SP-GiST索引<br>
内置的SP-GiST索引操作类:point,range,text
- GIN索引<br>
GIN所以通常用于全文检索。PostgreSQL数据库已经对一些内置的数组类型实现了GIN索引操作类。插入更新是,GIN索引比较慢,如果要向一张表中插入大量的数据,最好把GIN索引删除掉,插入好之后再重建索引。
- 关于索引的扩展 https://yq.aliyun.com/articles/111793
#### 1.6 分区表
PostgresSQL分区的意思是把逻辑上的一个大表分割成物理上的几块儿。分区不仅能带来访问速度的提升,关键的是,它能带来管理和维护上的方便。<br>
分区的具体好处是:<br>
某些类型的查询性能可以得到极大提升。<br>
更新的性能也可以得到提升,因为表的每块的索引要比在整个数据集上的索引要小。如果索引不能全部放在内存里,那么在索引上的读和写都会产生更多的磁盘访问。<br>
批量删除可以用简单的删除某个分区来实现。<br>
可以将很少用的数据移动到便宜的、转速慢的存储介质上。<br>
在PG里表分区是通过表继承来实现的,一般都是建立一个主表,里面是空,然后每个分区都去继承它。无论何时,都应保证主表里面是空的。<br>
小表分区不实际,表在多大情况下才考虑分区呢?PostgresSQL官方给出的建议是:当表本身大小超过了机器物理内存的实际大小时(the size of the table should exceed the physical memory of the database server),可以考虑分区。
插入测试数据:<br>
insert into fenqu01 select generate\_series(1,20000000), 'text'||generate\_series(1,20000000),now();<br>
insert into fenqu02 select generate\_series(20000001,40000000), 'text'||generate\_series(20000001,40000000),now();<br>
insert into fenqu03 select generate\_series(40000001,60000000), 'text'||generate\_series(40000001,60000000),now();<br>
insert into fenqu04 select generate\_series(60000001,80000000), 'text'||generate\_series(60000001,80000000),now();<br>
insert into fenqu05 select generate\_series(80000001,100000000), 'text'||generate\_series(80000001,100000000),now();<br>
insert into fenqu06 select generate\_series(100000001,120000000), 'text'||generate\_series(100000001,120000000),now();<br>
insert into fenqu07 select generate\_series(120000001,140000000), 'text'||generate\_series(120000001,140000000),now();<br>
insert into fenqu08 select generate\_series(140000001,160000000), 'text'||generate\_series(140000001,160000000),now();<br>
insert into fenqu09 select generate\_series(160000001,180000000), 'text'||generate\_series(160000001,180000000),now();<br>
insert into fenqu10 select generate\_series(180000001,200000000), 'text'||generate\_series(180000001,200000000),now();<br>
分表创建索引:<br>
create index idx_fenqu01_id on fenqu01(id);<br>
create index idx_fenqu02_id on fenqu02(id);<br>
create index idx_fenqu03_id on fenqu03(id);<br>
create index idx_fenqu04_id on fenqu04(id);<br>
create index idx_fenqu05_id on fenqu05(id);<br>
create index idx_fenqu06_id on fenqu06(id);<br>
create index idx_fenqu07_id on fenqu07(id);<br>
create index idx_fenqu08_id on fenqu08(id);<br>
create index idx_fenqu09_id on fenqu09(id);<br>
create index idx_fenqu10_id on fenqu10(id);<br>
create index idx_fenqu01_info on fenqu01(info);<br>
create index idx_fenqu02_info on fenqu02(info);<br>
create index idx_fenqu03_info on fenqu03(info);<br>
create index idx_fenqu04_info on fenqu04(info);<br>
create index idx_fenqu05_info on fenqu05(info);<br>
create index idx_fenqu06_info on fenqu06(info);<br>
create index idx_fenqu07_info on fenqu07(info);<br>
create index idx_fenqu08_info on fenqu08(info);<br>
create index idx_fenqu09_info on fenqu09(info);<br>
create index idx_fenqu10_info on fenqu10(info);<br>
数据查询:<br>
explain analyze select * from test01 where info = 'text53';<br>
| Index Scan using idx_test01_text on test01 (cost=0.57..8.59 rows=1 width=28) (actual time=0.707..0.708 rows=1 loops=1) |
| Index Cond: (info = 'text53'::text)
| Planning time: 0.826 ms
| Execution time: 0.724 ms
共返回 4 行记录,花费 5.00 ms.
- <h4>数组测试</h4>
建测试数组表:<br>
create table arraytest(id int, namestr text, phone text[]);<br>
建索引:<br>
create index idx_arraytest_id on arraytest(id);
create index idx_arraytest_namestr on arraytest(namestr);
create index idx_arraytest_phone on arraytest using gin(phone);
导入测试数据:<br>
insert into arraytest select generate_series(1,10000000), 'name'||generate_series(1,10000000), regexp_split_to_array(generate_series(1,10000000)||','||(random()\*(2\*10^9))::integer,E'\\\,')<br
查询测试:<br>
explain analyze select * from arraytest where id = 234234;<br>
explain analyze select * from arraytest where phone @> array[1191838269]::text[];<br>
psql=#select * from arraytest where phone @> array[1191838269]::text[];<br>
+--------------+-------------------+---------------------+<br>
| ID | NAMESTR | PHONE |<br>
+--------------+-------------------+---------------------+<br>
| 234234 | name234234 | {234234,1191838269} |<br>
+--------------+-------------------+---------------------+<br>
共返回 1 行记录,花费 3.00 ms.<br>
explain analyze select * from arraytest where phone[2] = '1191838269';<br>
psql=#select * from arraytest where phone[2] = '1191838269'<br>
+--------------+-------------------+---------------------+<br>
| ID | NAMESTR | PHONE |<br>
+--------------+-------------------+---------------------+<br>
| 234234 | name234234 | {234234,1191838269} |<br>
+--------------+-------------------+---------------------+<br>
共返回 1 行记录,花费 8934.00 ms.<br>
GIN索引用=号性能就很差了。