PostgreSQL的数据类型

PostgreSQL的数据类型

1. 布尔类型

1.1 布尔值对应表

在Postgresql数据库中Boolean的值除了“true”(真)、“false”(假),还有一个“unknown”(未知)状态。
如果是unknown时用NULL表示。布尔类型在Postgresql中可以用不带引号的TRUE或FALSE表示,也可以用更多表示真和假的带引号的字符表示:

备注
‘TRUE’ FALSE
tRue fAlse
tRue fAlse 不分大小写
t f 单一字符表示
yes no 英文的是和否表示
y n yes和no的简写表示
1 0 1表示true,0表示false

以上表格中值加引号都可以表示真和假,但是在Postgresql中Boolean类型字段里存储的值还是 t (true)和 f(false)。

1.2 布尔类型的操作符

操作符分为逻辑操作符和比较操作符
1.逻辑操作符有:AND、OR、NOT。
SQL标准使用三种值的布尔逻辑:TRUE、FALSE和NULL,NULL代表unknown。

布尔的and、or逻辑运算:

a b a AND b a OR b
TRUE TRUE TRUE TRUE
TRUE FALSE FALSE TRUE
TRUE NULL NULL TRUE
FALSE FALSE FALSE FALSE
FALSE NULL FALSE NULL
NULL NULL NULL NULL

布尔的not运算:

a NOT a
TRUE FALSE
FALSE TRUE
NULL NULL

逻辑操作符的两边值可以互换,结果是相同的。布尔类型使用 IS 作为比较运算符,结果很直观,不多做介绍了。

2. 数值类型(整数、小数、浮点数、序列、货币)

2.1 Postgresql数据库的所有数值类型及其解释:

类型名称 所占空间 描述 取值范围
smallint 2字节 小范围整数,Oracle中使用number代替 -215 ~ 215-1
int或integer 4字节 常用整数,Oracle中也有integer,等效于number(38),与此不同 -231 ~ 231-1
bigint 8字节 大范围整数,Oracle中没有此类型,也是只有number代替 -263 ~ 263-1
numeric或decimal 变长 用户来声明精度 无限制
real 4字节 变精度,不精确 6位十进制数字精度
double precision 8字节 变精度,不精确 15位十进制数字精度
serial 4字节 自增整数 1 ~ 2的31次方 -1
bigserial 8字节 大范围自增整数 1 ~ 2的63次方-1

2.2 数值类型的详细解释

2.2.1 整数类型

整数类型分三种:smallint、int、bigint。
Postgresql中最常用的是int类型,它提供了在范围,存储空间、性能之间的平衡。一般磁盘紧张会使用smallint,在int范围不足时用bigint类型。
SQL语句中 int、integer和int4是等效的,smallint和int2是等效的,bigint和int8是等效的,也就是说在创建表时,指定字段的类型可以用简写表示,便于编写SQL语句。

2.2.2 精确的小数类型

精确的小数类型可以用numeric、numeric(m)、numeric(m,n)表示。
numeric和decimal是等效的。可以存储1000位精度的数字,它的语法是:
numeric(precision, scale)
—— precision必须位正数,规定了小数点前有几位。
—— scale可以是零或者正数,规定了小数点后有几位。
numeric(m)表示了scale是0,与numeric(m,0)的含义相同。
而numeric表示precision和scale没有指定,可以存储任意精度和标度的数值。

postgres=# create table tb_int(id1 numeric(3),id2 numeric(3,0),id3 numeric(3,2),id4 numeric);
CREATE TABLE
postgres=# \d tb_int 
                 Table "public.tb_int"
 Column |     Type     | Collation | Nullable | Default 
--------+--------------+-----------+----------+---------
 id1    | numeric(3,0) |           |          | 
 id2    | numeric(3,0) |           |          | 
 id3    | numeric(3,2) |           |          | 
 id4    | numeric      |           |          | 

postgres=# insert into tb_int values(3.126,3.126,3.126,3.126);
INSERT 0 1
postgres=# select * from tb_int ;
 id1 | id2 | id3  |  id4  
-----+-----+------+-------
   3 |   3 | 3.13 | 3.126
(1 row)
postgres=# insert into tb_int values(3.126,1311.126,3.126,3.126);
2020-02-16 20:33:05.601 CST [27416] ERROR:  numeric field overflow
2020-02-16 20:33:05.601 CST [27416] DETAIL:  A field with precision 3, scale 0 must round to an absolute value less than 10^3.
2020-02-16 20:33:05.601 CST [27416] STATEMENT:  insert into tb_int values(3.126,1311.126,3.126,3.126);
ERROR:  numeric field overflow
DETAIL:  A field with precision 3, scale 0 must round to an absolute value less than 10^3.

PS:声明了精度(precision)没有声明标度(scale),超出精度的会按照四舍五入取值。
同样,声明了标度时,超出标度的会按照四舍五入取值。

2.2.3 浮点数类型

数据类型real和double precision 是不精确的、变精度的数字类型。
对于浮点数,需要注意:
(1)如果需要精确计算,应用numeric类型。
(2)两个浮点数类型的值做比较时,结果可能不是所想象的那样运行。
(3)infinity、-infinity、NaN分别表示正无穷大、负无穷大、非数字。

2.2.4 序列类型

序列类型中,serial和bigserial与MySQL中的自增字段是同样性质,在Postgresql中是通过序列(sequence)来实现的。
比如:

postgres=# create table tb_serial(id serial);
CREATE TABLE
postgres=# \d+ tb_serial
                                                Table "public.tb_serial"
 Column |  Type   | Collation | Nullable |                Default                | Storage | Stats target | Description 
--------+---------+-----------+----------+---------------------------------------+---------+--------------+-------------
 id     | integer |           | not null | nextval('tb_serial_id_seq'::regclass) | plain   |              | 
Access method: heap

例子中创建了一个表 tb_serial ,它的id列位serial类型,创建后查询表属性,显示id默认的是通过序列赋值,并且可以看到除了表之外还同时创建了一个名字位tb_serial_id_seq的序列。
所以create语句就相当于一下几个语句同时执行:

create sequence tb_serial_id_seq;
create table tb_serial(
	id integer NOT NULL DEFAULT nextval('tb_serial_id_seq')
	);
alter sequence tb_serial_id_seq OWNED BY tb_serial.id;

2.2.5 货币类型

货币类型(money type)可以存储固定小数的货币数目,精度准确。查询时输出的格式跟数据库中的参数:lc_monetary的设置有关,不同国家的货币输出的格式时不一样的。例如:

postgres=# select '12.14'::money;
  money  
---------
 ¥12.14
(1 row)

postgres=# select '1214.13'::money;
   money    
------------
 ¥1,214.13
(1 row)

postgres=# show lc_monetary;
 lc_monetary 
-------------
 zh_CN.UTF-8
(1 row)

postgres=# set lc_monetary='en_US.UTF-8';
SET
postgres=# select '12.14'::money;
 money  
--------
 $12.14
(1 row)

postgres=# select '1214.13'::money;
   money   
-----------
 $1,214.13
(1 row)

postgres=# show lc_monetary;
 lc_monetary 
-------------
 en_US.UTF-8
(1 row)

2.3 数学函数和操作符

数学操作符:

操作符 描述 例子 结果
+ 4+7 11
- 4-7 -3
* 4*7 28
/ 除(只保留整数部分,不会四舍五入) 3/2 1
% 模(求余数) 6%4 2
^ 次幂(指数运算) 3^3 27
l/ 平方根 l/4.0 2
ll/ 立方根 ll/8.0 2
阶乘 5! 120
!! 前缀方式的阶乘 !!5 120
@ 绝对值 @-5.0 5
& 二进制AND 31&15 15
l 二进制OR 31 l 15 31
# 二进制XOR 31 # 15 16
~ 二进制NOT ~1 -2
<< 二进制左移 1 << 8 256
>> 二进制右移 16 >> 3 2

数学函数:

函数 描述 例子 结果
abs(x) 绝对值 abs(-23.7) 23.7
cbrt(dp) 立方根 cbrt(8.0) 2
ceil(dp或numeric) 别名:ceiling 不小于参数的最小整数 ceil(-38.8),ceil(38.1),ceiling(38.1) -38,39,39
degrees(dp) 把弧度转化为角度 degrees(1.0) 57.295779513
exp(dp或numeric) 自然指数 exp(1.0) 2.71828182845905
floor(dp或numeric) 不大于参数的最大整数 floor(-38.8),floor(38.8) -38,38
ln(dp或numeric) 自然对数 ln(2.71828) 0.999999
log(dp或numeric) 以10为底的对数 log(1000.0) 3
log(b numeric,x numeric) 以b为底的对数 log(2.0,32.0) 5.00000
mod(y,x) y/x的余数(模) mod(7,3) 1
pi() Π的常量 pi() 3.14159265358979
power(a dp,b dp ) a的b次幂 power(2.0,3.0) 8
power(a numeric,b numeric) a的b次幂 power(2.0,3.0) 8
radians(dp) 把角度转换为弧度 radians(45.0) 0.785398163397
random() 0.0-1.0之间的随机数 random() 随机返回一个小数
round(dp或numeric) 圆整为最接近的整数(四舍五入) round(36.5) 37
round(v numeric, s int) 圆整为s位小数(四舍五入) round(36.5252, 2) 36.53
setseed(dp) 为随后的random()调用设置种子(0到1.0之间) setseed(0.2); random() 先设置setseed后,后续的random取值是可以被预见的,后三次取值将会是固定值
sign(dp 或 numeric) 返回参数的符号:-1代表负数;0代表0;1代表正数 sign(-1.5) -1
sqrt(dp 或 numeric) 平方根 sqrt(9.0) 3
trunc(dp 或 numeric) 截断小数,不四舍五入 trunc(2.3) 2
trunc(v numeric, s int) 截断 s 位小数 trunc(12.356, 3) 12.35

数学函数之三角函数:

函数 描述 例子 结果
acos(x) 反余弦 acos(1) , acos(-1) 0 , 3.14159265358979
asin(x) 反正弦 asin(0) , asin(1)*2 0, 3.14159265358979
atan(x) 反正切 atan(1)
atan2(x,y) x/y的反正切
cos(x) 余弦
cot(x) 余切
sin(x) 正弦
tan(x) 正切

3. 字符串类型

3.1 Postgresql数据库字符串类型

类型 描述
character varying(n) 或 varchar(n) 变长字符串类型,最大空间1GB,存储空间4+实际的字符串长度。与MySQL中的varchar(n)或text(n),以及Oracle中的varchar2(n)类似,但是在MySQL中varchar最多只有64KB,Oracle中的varchar2最多只有4000字节。
character(n)或char(n) 定长字符串类型,不足空白补充。最大1GB,存储空间4+n
text 变长字符串类型。无长度限制,与MySQL中的LONGTEXT类似

varchar(n) 在没有指定 n 的值时,表示可以接受1GB内的任何长度的字符串,建议加上 n,
char(n) 在没有指定 n 值时,表示 char(1) ,
不管varchar还是char类型最小存储空间时4字节。而在Postgresql数据库中varchar和char指定长度过长时,实际在字段中存储的可能只是一个指针,具体内容会存储在toast表中,指针便于对字段值的快速访问。
在一些其他种类的数据库中定长的char有一定的优势。但是在postgresql中没有什么差别。大多数还是会用varchar或text。

2.2 字符串函数和操作符

支持标准的SQL字符串的函数和操作符:

函数 返回类型 描述 例子 结果
string ll string text 字符串链接 selec ‘abc’ ll ‘EFG’; ‘abcEFG’
length(string) int string 中字符的个数,主要区分的是中文和英文不同 select length(‘abc’) 3
bit_length(string) int 字符串二进制位长度(个数) select bit_length(‘jack’); 32
char_length(string)或character_length(string) int 字符串中字符的个数 select char_length(‘jack’); 4
octet_length(string) int 字符串中的字节数 select octet_length(‘jack’); 4
convert(string using conversion_name) test 使用指定的转换名字改变编码。转换可以通过CREATE CONVERSION定义,系统中也有一些预定义的转换名字 select convert(‘Postgresql’ using iso_8859_1_to_utf8); UTF8编码的Postgresql
convert( string text, [ src_encoding name, ], dest_encoding name ) text 把字符串原来编码位 src_encoding 转成 dest_encoding 编码。如果没有指定src_encoding,则src_encoding为数据库编码 select convert( ‘abc’, ‘UTF8’, ‘GBK’ ) 以GBK编码的abc
lower(string) text 把所有的字符串转成小写 select lower(‘TOM’); tom
upper(string) text 把所有字符串转成大写 select upper(‘tom’); TOM
initcap(string) text 将字符串中每个单词的第一个字符转为大写,其他变为小写(字符串中区分单词:非字符数字分割) initcap(‘hi jack’) Hi Jack
overlay(string placing string from int[for int]) text 指定子字符串替换的范围,例子里时用 hom 字符串替换 Txxxxas 中从第2个字符开始的4个字符,如果没有指定 for int ,int 是子字符串的长度 select overlay(‘Txxxxas’ placing ‘hom’ from 2 for 4); Thomas
position( substring in string ) int 查找substring 在 string 中的起始位置 select position( ‘om’ in ‘Thomas’); 3
strpos(string , substring) int 查找指定的子字符串substring在string中的起始位置 select strpos(‘helloworld’ ,‘llo’); 3
substring(string, from [, count] ) text 截取string中指定位置from的指定长度count的子字符串 select substring ( ‘jack’ , 2, 2) ac
substring(string from int for int ) text 抽取指定位置(from int )开始指定个数(for int)的字符串,如果没有指定 for int 则从指定位置一直到结束全部截取 select substring( ’ Thomas’ from 2 for 3 ); hom
substring(string from pattern) text 截取匹配posix正则表达式的字符串 select substring(‘hello world’ from ‘…$’); rld
substring(string from pattern for escape) text 截取匹配posix正则表达式的字符串,for为转移字符 select substring(‘Thomas’ from ‘%#“o_a#”_’ for ‘#’); oma
replace(string text, from text, to text ) text 把字符串string中所有出现的from 都替换成 to select replace(‘abcddggdd’,‘d’,‘5’) abc55gg55
ascii(string) int 字符串中第一个字符的ASCII码 select ascii(‘abc’) 97
chr(int) text 给出ASCII码所对应的字符 select chr(97) a
decode(string text, type text ) bytea 把早先用encode编码的string里面的二进制数据解码,支持类型:base64、hex、escape
encode(data bytea, type text) text 把二进制数据编码位只包含ASCII形式的数据,支持类型与decode类似
lpad(string text, length int [, fill text] ) text 通过在指定的string左边填充字符 fill (不指定默认空白),把string填充为指定的length长度,如果string长度比指定的length长,则将其尾部截断,例子里是将字符串 OK 左边使用 1 填充直到长度为5 select lpad( ‘OK’, ‘5’, ‘1’) 111OK
rpad(string text, length int [, fill text] ) text 通过在指定的string右边填充字符 fill (不指定默认空白),把string填充为指定的length长度,如果string长度比指定的length长,则将其尾部截断,例子里是将字符串 OK 右边使用 1 填充直到长度为5 select rpad( ‘OK’, ‘5’, ‘1’) OK111
btrim( string text [ , character text ] ) text 从string的开头和结尾删除含有 character 中的字符,不指定character默认空格 select btrim( ‘aajackaaaa’, ‘aa’ ) jack
ltrim( string text [ , character text ] ) text 从string的开头(左边)删除含有 character 中的字符,不指定character默认空格 select btrim( ‘aajackaaaa’, ‘aa’ ) jackaaaa
rtrim( string text [ , character text ] ) text 从string的结尾(右边)删除含有 character 中的字符,不指定character默认空格 select btrim( ‘aajackaaaa’, ‘aa’ ) aajack
quote_ident(string) text 返回适用于SQL语句的标识符形式(使用适当的引号进行界定),只有在必要的时候才会添加引号(字符串包含非标识符字符或者会转换大小写的字符),嵌入的引号会被恰当的写双份
quote_literal(string) text 返回适用于在SQL语句里当作文本的形式,嵌入的引号和反斜杠被恰当的写双份
pg_client_encoding() name 当前客户端编码名称 select pg_client_encoding(); UTF8
repeat(string text, number int ) text 将string重复number次 select repeat( ‘abc’, 2); abcabc
split_part(string text, delimiter text, field int) text 根据delimiter分隔string,返回生成第 field 个字符串 split_part( ‘123#456#789’, ‘#’, 2 ) 456
regexp_replace(string text, pattern text, replacement text [ , flages text ]) text 替换匹配POSIX正则表达式的子字符串

3.3 特别注释

字符串length类似函数用法:

postgres=# select length('abcd');
 length 
--------
      4
(1 row)

postgres=# select octet_length('abcd');
 octet_length 
--------------
            4
(1 row)

postgres=# select bit_length('abcd');
 bit_length 
------------
         32
(1 row)

postgres=# select length('测试');
 length 
--------
      2
(1 row)

postgres=# select octet_length('测试');
 octet_length 
--------------
            6
(1 row)

postgres=# select bit_length('测试');
 bit_length 
------------
         48
(1 row)

4. 日期类型

4.1 日期和时间的类型如下

名称 存储空间 描述 例子
timestamp [ p ] [ without time zone ] 8字节 显示日期加时间 2020-02-23 12:00:00
timestamp [ p ] with time zone 8字节 显示日期、时间加时区
interval [ § ] 12字节 时间间隔
date 4字节 只用于日期
time [ p ] [ without time zone ] 8字节 只用于1日内时间,不带时区
time [ p ] with time zone 8字节 只用于1日内时间,带有时区

4.2 日期输入

在SQL中,任何日期或时间的文本输入需要有 ’ 日期/时间 ‘ 类型加单引号包围的字符串组成,语法:
type [ ( p ) ] ’ value ’
日期和时间的输入几乎可以是任何合理的形式,在Postgresql中有系统参数 datestyle 决定是什么格式或形式,如下:

postgres=# show datestyle;
 DateStyle 
-----------
 ISO, MDY
(1 row)

其中 “ MDY ” 表示 月-日-年
在数据库中参数可以设置成 YMD 的形式,但在使用语句插入时,用了date转换格式输入,参数设置那种格式没有影响,都可以进行插入成功。

postgres=# show datestyle;
 DateStyle 
-----------
 ISO, MDY
(1 row)

postgres=# insert into tb_date values(date '02-23-2020');
INSERT 0 1
postgres=# insert into tb_date values(date '2020-02-23');
INSERT 0 1
postgres=# select * from tb_date ;
    col1    
------------
 2020-02-23
 2020-02-23
(2 rows)

但是有时候日期输入习惯使用诸如此类 ’ 1/8/2020 ‘ 的格式,类似这种各位月或日没有0做前缀时要注意参数 datestyle 的值,防止日期混乱或者插入错误。
日期输入示例:

例子 描述
date ‘April 26,2020’ 在任何datestyle参数值下都没有输入问题
date ‘2019-01-08’ ISO 8601格式,任何方式输入没有问题
date ‘1/8/2019’ 在datestyle为 MDY 时,表示2019年1月8日,在datestyle为 DMY 时,表示2019年8月1日,类似输入日期文本时没有在一位数月份或日前加前缀0的输入方式一定要注意 datestyle 参数的格式,凡是日期混乱
date ‘1/18/2020’ 在datestyle参数值为 MDY 时,表示2020年1月18日,其他格式会出现输入错误
date ‘03/04/11’ 在 MDY 格式下表示2011年3月4日,在DMY 格式下表示2011年4月3日,在YMD格式下表示2003年4月11日,所以建议在输入日期文本时要输入完整的日期文本,尽量不要用简写导致日期混乱。
date ‘2019-Apr-08’ 在任何格式下都是2019年4月8日
date ‘Apr-08-2019’ 在任何格式下都是2019年4月8日
date ‘08-Apr-2019’ 在任何格式下都是2019年4月8日
date ‘20190405’ ISO 8601格式,任何格式下都是2019年4月5日
date ‘110405’ ISO 8601 格式,在任何格式下都是2011年4月5日
date ‘2019.098’ 2019年的第98天,即2019年4月8日
date ‘J2455886’ 儒略日,即从公元前4713年1月1日期到今天经过的天数,多为天文学家使用,2455886天,就是2011年11月20日
date ‘April 26,202 BC’ 公元前202年4月26日

PS: 对于国内的程序员来说,避免使用 ’/’ 进行日期的输入,,最好使用 ‘-’ 来进行日期文本分割,然后使用 YMD 也就是 年-月-日
的格式输入日期。

3.4 时间输入

时间输入时,要注意时区的输入,time 被认为时 time without time zone 的类型,所以即使输入的字符串中有时区,也会被忽略,例如:

postgres=# select time '12:59:59';
   time   
----------
 12:59:59
(1 row)

postgres=# select time '12:59:59 PST';
   time   
----------
 12:59:59
(1 row)

postgres=# select time with time zone  '12:59:59 PST';
   timetz    
-------------
 12:59:59-08
(1 row)
1234567891011121314151617

时间输入时字符串之间使用冒号作为分隔符,输入格式为: ‘ hh:mm:ss ’,如:‘ 12:59:59 ’,也可以不使用分隔符,如:‘125959’ 表示 12时59分59秒。
更多的时间输入示例如下:

例子 描述
time ‘12:59:59.789’ ISO 8601
time ‘12:59:59’ ISO 8601
time ‘12:59’ ISO 8601
time ‘125959’ ISO 8601
time ‘10:59 AM’ 与 10:59 一样,AM不影响数值
time ‘10:59 PM’ 与 22:59一样,使用PM时输入的小时的值小于等于12
time with time zone ‘12:59:59.789+8’ 带有时区
time with time zone ‘12:59:59+08:00’ ISO 8601
time with time zone ‘12:59+08:00’ ISO 8601
time with time zone ‘125959+08:00’ ISO 8601
time with time zone ‘12:59:59 CCT’ 带有缩写的时区,北京时间12:59:59
select time with time zone ‘2020-02-24 12:59:59 Asia/Beijing’ 用具体名字声明时区

PS: 建议在输入时区时不适用缩写表示,相同的缩写代表的可能时不同的时区。

4.4 特殊的时间字符串

在Postgresql中有一些特殊的字符串输入表示了特别的意义,如下:

字符串 使用类型 描述
epoch date,timestamp 1970-01-01 00:00:00+00(Unix 系统的零时)
infinity timestamp 时间戳的最大值,比任何其他时间戳都晚
-infinity timestamp 时间戳的最小值,比任何其他时间戳都早
now date,time,timestamp 当前事务的开始时间(当前时间)
today date,timestamp 今日午夜
tomorrow date,timstamp 明日午夜
yesterday date,timestamp 昨日午夜
allballs time 00:00:00.00 UTC

4.5 时间函数和操作符

日期、时间和interval类型之间可以进行加减乘除运算,具体如下:

操作符:

操作符 例子 结果
+ date ‘2020-02-01’ + integer ‘7’ date ‘2020-02-08’
+ date ‘2020-02-01’ + interval ‘1 hour’ timestamp ‘2020-0201 01:00:00’
+ date ‘2020-02-01’ + time ‘03:00’ timestamp ‘2020-02-01 03:00:00’
+ interval ‘1 day’ + interval ‘1 hour’ interval ‘1 day 01:00:00’
+ timestamp ‘2020-02-01 01:00’ + interval ‘23 hours’ timestamp ‘2020-02-02 00:00:00’
+ time ‘01:00’ + interval ‘2 hours’ time ‘03:00:00’
- date ‘2020-02-23’ - date ‘2020-02-20’ 3
- date ‘2020-02-23’ - integer ‘3’ date ‘2020-02-20’
- date ‘2020-02-23’ - interval ‘1 hour’ timestamp ‘2020-02-22 23:00:00’
- time ‘07:00’ - time ‘03:00’ interval ‘04:00:00’
- time ‘07:00’ - interval ‘4 hours’ time ‘03:00:00’
- timestamp ‘2020-02-23 22:00’ - interval ‘22 hour’ timestamp ‘2020-02-23 00:00:00’
- interval ‘a day’ - interval ‘1 hour’ interval ‘1 day -01:00:00’
- timestamp ‘2020-02-23 03:00:00’ - timestamp ‘2020-02-20 12:00’ interval ‘2 days 15:00:00’
* 600*interval ‘1 second’ interval ‘00:01:00’
* 15*interval ‘1 day’ interval ‘15 days’
* 2.5*interval ‘1 hour’ interval ‘02:30:00’
/ interval ‘2 hour’ / 1.5 interval ‘01:22:00’

函数如下:

函数 返回类型 描述 例子 结果
age(timestamp,timestamp) interval 两个时间段之间的时间间隔,前者时间一般为较新时间,结果为正数的时间间隔,否则为负数的时间间隔 age(timestamp ‘2020-02-01’,timestamp ‘2019-01-01’); age(timestamp ‘2019-02-01’,timestamp ‘2020-01-01’); 1 year 1 mon ;-11 mons
age(timestamp) interval 从当前时间减去参数时间的结果(时间间隔) select age(timestamp ‘2000-01-01’) 20 years 1 mon 23 days
clock_timestamp() timestamp with time zone 实时时钟的当前时间戳,带有时区 select clock_timestamp();
current_date date 当前日期,注意使用时不用加括号 select current_date
current_time time with time zone 当前时间,带有时区 select current_time;
current_timestamp timestamp with time zone 当前事务开始时的时间戳,带有时区 select current_timestamp
date_part(text, timestamp) double precision 获取时间timestamp中指定年月日时分秒等,text的值包括了year(简写 y ),month(m),day(d),hour(h),minute(m),second(s) select date_part(‘day’,timestamp ‘2020-02-23 12:59:30’); 23
date_part(text, interval) double precision 获取interval时间类型中的指定的年月日时分秒等,text的值包括了year(简写 y ),month(m),day(d),hour(h),minute(m),second(s) select date_part(‘hour’,interval ‘2 years 3 months 12 days 10 hours 30 minutes 10 seconds’); 10
date_trunc(text, timestamp) timestamp 将时间timestamp截断成text指定的精度,text的值包括了year(简写 y ),month(m),day(d),hour(h),minute(m),second(s) select date_trunc(‘hour’,timestamp’2020-02-23 12:59:30’); 2020-02-23 12:00:00
extract(field from timstamp) double precision 获取timestamp中指定的text,类似date_part select extract(‘hour’ from timestamp ‘2020-02-23 12:59:30’); 12
extract(field from interval) double pracision 获取interval中指定的text,类似date_part select extract(‘hour’ from interval ‘2 days 12 hours’) 12
isfinite(timestamp) boolean 测试是否为有穷时间戳 isfinite(timestamp ‘2020-02-25 20:12:30’) true
isfinite(interval) boolean 测试是否为有穷时间隔 isfinite(interval ‘5 hours’) true
justify_days(interval) interval 按照每天30天调整时间间隔,每个30天会变为一个月,不够显示真是天数,超出显示减去30天后的天数 select justify_days(interval ‘35 days’); 1 mon 5 days
justify_hours(interval) interval 按照每24小时调整为1天,满24小时会以day显示,如果输入的是day,只会显示day select justify_hours(interval ‘35 hours’); 1 day 11:00:00
justify_interval(interval) interval 同时具备justify_days与justify_hours的功能,而interval参数可以进行加减计算 select justify_interval(interval ‘1 mon - 2 days’); 28 days
localtime time 当日时间 select localtime; 20:39:09.384134
localtimestamp timestamp 当前事务开始时的时间戳 select localtimestamp; 2020-02-25 20:40:17.168613
now() timestamp with time zone 当前事务开始时的时间戳并带有时区 select now(); 2020-02-25 20:41:28.969894+08
statement_timestamp() timestamp with time zone 与now()类似,实时时钟的当前时间戳 select statement_timestamp(); 2020-02-25 20:42:24.416925+08
timeofday() text 与statment_timestamp类似,但是返回的类型是text字符串,而非时间类型,显示的格式也不一样 select timeofday(); Tue Feb 25 20:44:17.330426 2020 CST
transaction_timestamp() timestamp with time zone 当前事务开始时的时间戳 select transaction_timestamp() 2020-02-25 20:46:02.107255+08

除了以上的函数外,Postgresql还支持SQL的OVERLAPS操作符,如:
(start1,end1)OVERLAPS(start2,end2)
(start1,length1)OVERLAPS(start2,length2)
以上两个表达式表示在左边的时间段与右边的时间段是否有重叠,如果重叠返回true,如果没有重叠返回false。终点参数(end1、end2)可以是日期,时间,时间戳。或者后面(length1、length2)是一个时间间隔的日期、时间、时间戳。如:

postgres=# select (date '2019-01-01',date '2019-09-25')overlaps(date '2019-09-01',date '2020-02-25');
 overlaps 
----------
 t
(1 row)

postgres=# select (date '2019-01-01',date '2019-08-31')overlaps(date '2019-09-01',interval '25 days');
 overlaps 
----------
 f
(1 row)

4.6 时间函数

Postgresql数据库中内置了很多返回当前日期和时间的函数,一下函数是按照当前事务的开始时间返回结果:

current_date
current_time
current_timestamp
current_time(precision)
current_timestamp(precision’)
localtime
localstamp
localtime(precision)
localtimestamp(precision)
now()
transacion_timestamp()

其中precision代表的是精度,该精度会导致结果的秒数域会四舍五入到指定的小数位,如果没有精度参数,将给予全部得到的精度。

PS: 特别介绍一下,当前时间获取的函数分为事务开始时的时间戳和实时时钟(当前时间)的时间两类,分别如下:
1.当前事务开始时的时间戳。开始事务时也就是begin;执行时表示开始了一个事务,而当前事务开始时的当前时间,表示执行begin时的时间,在end结束事务前,获取的当前时间时不变的,都是相同一个时间。主要函数包括:current_date、current_time、current_timestamp、localtime、localstamp、transaction_timestamp().。Postgresql这么做的目的是为了允许一个事务在当前时间上有连贯的概念,同一个事务里的多个修改可以保持同样的时间戳。
2.实时时钟时间表示即便执行begin开始了事务,获取的当前时间仍是随着时间而改变的,类似直接获取的是系统时间,函数有:clock_timestamp()、statement_timestamp()、stimeofday()。

所有的日期或时间还接受特殊的文本值:now。用于声明当前的时间和日期(注意:是当前事务开始的开始时间),所以,一下三个语句返回结果是相同的:

postgres=# begin;
BEGIN
postgres=# select current_timestamp;
       current_timestamp       
-------------------------------
 2020-02-25 21:24:32.964255+08
(1 row)

postgres=# select now();
              now              
-------------------------------
 2020-02-25 21:24:32.964255+08
(1 row)

postgres=# select timestamp with time zone 'now';
          timestamptz          
-------------------------------
 2020-02-25 21:24:32.964255+08
(1 row)

postgres=# end;
COMMIT

4.7 extract函数详解

extract函数格式:

extract(field from source)

extract函数从日期或时间数值中抽取子域,比如年分、小时等,返回类型为 double precision 的数值,source参数必须是一个timestamp、time、interval类型的值的表达式,此外,类型是date的表达式自动转换为timestamp,所以source也可用date类型,field参数是一个标识符或者字符串,指定了从源数据中抽取的域,下表列举了field可以取得值:

field值 说明 例子 结果
century 世纪 select extract(century from timestamp ‘2020-02-25 12:59:59’); 21
year 年份 select extract(year from timestamp ‘2020-02-25 12:59:59’); 2020
decade 得到年份除以10后的值 select extract(decade from timestamp ‘2020-02-25 12:59:59’); 202
millennium 得到当前是第几个千年(0-1000是第一个,1001-2000是第二个,2001-3000是第三个) select extract(millennium from timestamp ‘2020-02-25 12:59:59’); 3
quarter 是第几季度 select extract(quarter from timestamp ‘2020-02-25 12:59:59’); 1
month 当source是timestamp得到的是月份,当source是interval得到是月的数目 select extract(month from timestamp ‘2020-02-25 12:59:59’); l select extract(month from interval ‘2 years 8 months’); 2 l 8
week 得到所给的日期是这一年的第几个星期 select extract(week from timestamp ‘2020-02-25 12:59:59’); 9
dow 得到所给的日期是星期几,0是星期天,1是星期一 。。。 select extract(dow from timestamp ‘2020-02-25 12:59:59’); 2
day 本月的第几天 select extract(day from timestamp ‘2020-02-25 12:59:59’); 25
doy 本年的第几天 select extract(doy from timestamp ‘2020-02-25 12:59:59’); 56
hour 得到时间中的小时(0-23) select extract(hour from timestamp ‘2020-02-25 12:59:59’); 12
minute 得到时间中的分钟 select extract(minute from timestamp ‘2020-02-25 12:59:59’); 59
second 得到时间中的秒,包括小数部分 select extract(second from timestamp ‘2020-02-25 12:59:59.7521’); 59.7521
epoch 对于date和timestamp值来说,得到的是自1970-01-01 00:00:00以来的秒数;对于interval值来说,得到时间间隔的总秒数 select extract(epoch from timestamp ‘2020-02-25 12:59:59.7521’); l select extract(epoch from interval ‘2 days 12 hours’); 1582635599.7521 l 216000
milliseconds 秒域(包括小数)乘以 1000,即秒域的毫秒级 select extract(milliseconds from timestamp ‘2020-02-25 12:59:59.7521’); 59752.1
microseconds 秒域(包括小数部分)乘以 1000000,即秒域的微秒级 select extract(microseconds from timestamp ‘2020-02-25 12:59:59.7521’); 59752100
timezone 与UTC的时区偏移量,以秒记录。例如中国是 +8区,返回的是3600x8=28800 select extract(timezone from timestamp with time zone ‘2020-02-25 12:59:59.7521’); 28800
timezone_hour 时区偏移量的小时部分 select extract(timezone_hour from timestamp with time zone ‘2020-02-25 12:59:59.7521’); 8
timezone_minute 时区偏移量分钟部分,整数时区返回0 select extract(timezone_minute from timestamp with time zone ‘2020-02-25 12:59:59.7521’); 0

5. 枚举类型

5.1 枚举类型介绍

Postgresql数据库中的枚举类型不能直接使用,在使用前要先创建枚举类型,create type 命令创建枚举类型。
例如:

postgres=# create type week as enum('Sun','Mon','Tues','Wed','Thur','Fri','Sat');
CREATE TYPE
postgres=# create table tb_duty(person text, weekday week);
CREATE TABLE
postgres=# insert into tb_duty values('Zhang','Sun');
INSERT 0 1
postgres=# insert into tb_duty values('Li','Mon');
INSERT 0 1
postgres=# select * from tb_duty ;
 person | weekday 
--------+---------
 Zhang  | Sun
 Li     | Mon
(2 rows)

如果插入tb_duty表的weekday列中的值没有在枚举类型week中则会报错:

postgres=# insert into tb_duty values('Li','MON');
2020-02-26 09:25:32.727 CST [49927] ERROR:  invalid input value for enum week: "MON" at character 33
2020-02-26 09:25:32.727 CST [49927] STATEMENT:  insert into tb_duty values('Li','MON');
ERROR:  invalid input value for enum week: "MON"
LINE 1: insert into tb_duty values('Li','MON');

注意:枚举类型中指定的值大小写敏感!!!匹配时值必须完全一致!!

如果想查询tb_duty表中枚举类型的定义,如下:

postgres=# \dT+ week
                                      List of data types
 Schema | Name | Internal name | Size | Elements |  Owner   | Access privileges | Description 
--------+------+---------------+------+----------+----------+-------------------+-------------
 public | week | week          | 4    | Sun     +| postgres |                   | 
        |      |               |      | Mon     +|          |                   | 
        |      |               |      | Tues    +|          |                   | 
        |      |               |      | Wed     +|          |                   | 
        |      |               |      | Thur    +|          |                   | 
        |      |               |      | Fri     +|          |                   | 
        |      |               |      | Sat      |          |                   | 
(1 row)

直接查询系统表pg_enum也可以查看枚举类型的定义:

postgres=# select * from pg_enum;
  oid  | enumtypid | enumsortorder | enumlabel 
-------+-----------+---------------+-----------
 16416 |     16414 |             1 | Sun
 16418 |     16414 |             2 | Mon
 16420 |     16414 |             3 | Tues
 16422 |     16414 |             4 | Wed
 16424 |     16414 |             5 | Thur
 16426 |     16414 |             6 | Fri
 16428 |     16414 |             7 | Sat

5.2 枚举类型说明

在枚举类型中,定义的值的顺序是在创建此枚举类型week时已经定义好的顺序,而标准的运算符或者相关的聚集函数都可以对枚举类型就行操作。例如:

postgres=# select * from tb_duty ;
 person | weekday 
--------+---------
 Zhang  | Sun
 Li     | Mon
 Feng   | Tues
 Qian   | Wed
 Sun    | Thur
 Wu     | Fri
 Wang   | Sat
(7 rows)

postgres=# select min(weekday),max(weekday) from tb_duty ;
 min | max 
-----+-----
 Sun | Sat
(1 row)

每个枚举类型都是独立存储的,一个枚举类型占4个字节。

5.3 枚举类型的函数

函数 描述 例子 结果
enum_first(anyenum) anyenum代表一个创建的枚举类型,函数返回此枚举类型的第一个值 select enum_first(‘Mon’::week); Sun
enum_last(anyenul) 返回枚举类型的最后一个值 select enum_last(‘Mon’::week); Sat
enum_range(anyenum) 以一个有序的数组形式返回输入枚举类型的所有值 select enum_range(‘Mon’::week);
enum_range(anyenum,anyenum) 以一个有序数组返回在给定的两个枚举类型之间的范围,两个参数值必须时相同的枚举类型,如果第一个参数为null,其结果时从枚举类型的第一个值开始到给定的结束值,如果第二个参数为null,其结果将以枚举类型的给定的值开始到枚举类型的最后一个值结束(注意:给定的值也会包含在结果中 ) 例1:select enum_range(‘Mon’::week , ‘Fri’::week); 例2:select enum_range(null , ‘Fri’::week); 例3:select enum_range(‘Mon’::week , null); 例1:{Mon,Tues,Wed,Thur,Fri};例2:{Sun,Mon,Tues,Wed,Thur,Fri};例3:

表格中的函数enum_first、enum_last中,函数会忽略传入的参数值 Mon ,它只对声明的数据类型返回值,即使使用null也会得到相同的结果,例如:

 postgres=# select enum_first(null::week),enum_first('Mon'::week),enum_last(null::week),enum_last('Mon'::week);
 enum_first | enum_first | enum_last | enum_last 
------------+------------+-----------+-----------
 Sun        | Sun        | Sat       | Sat
(1 row)

6. 几何类型

6.1 几何类型概况

Postgresql主要支持一些二维的几何数据类型,最基本的时 “point” ,它是其他类型的基础。

几何类型如下:

类型名称 存储空间 描述 表现形式
point 16字节 平面中的点 (x,y)
line 32字节 直线 ((x1,y1),(x2,y2))
lseg 32字节 线段(有限长度) [(x1,y1),(x2,y2)]
box 32字节 矩形 ((x1,y1),(x2,y2))
path 16+16*n 闭合路径(与多边形类似) ((x1,y1),(x2,y2),…)
path 16+16*n 开放路径 [(x1,y1),(x2,y2),…]
polygon 40+16n字节 多边形(与闭合路径相似) ((x1,y1),(x2,y2),…)
circle 24字节 <(x,y),r>

6.2 几何类型的输入

6.2.1 输入格式:

类型名称 ‘值’ 或者 ‘值’::类型名称

示例:

点:

postgres=# select point '1,2';
 point 
-------
 (1,2)
(1 row)

postgres=# select '(1,2)'::point;
 point 
-------
 (1,2)
(1 row)

线段 :

postgres=# select lseg '1,2,3,2';
     lseg      
---------------
 [(1,2),(3,2)]
(1 row)

postgres=# select lseg '(1,2),(3,2)';
  ?column?   
-------------
 [(1,2),(3,2)]
(1 row)

postgres=# select lseg '[(1,2),(3,2)]';
   ?column?    
---------------
 [(1,2),(3,2)]
(1 row)

postgres=# select '1,2,3,2'::lseg;
     lseg      
---------------
 [(1,2),(3,2)]
(1 row)

postgres=# select '(1,2),(3,2)'::lseg;
     lseg      
---------------
 [(1,2),(3,2)]
(1 row)

postgres=# select '[(1,2),(3,2)]'::lseg;
     lseg      
---------------
 [(1,2),(3,2)]
(1 row)

矩形:

postgres=# select box '1,1,2,2';
     box     
-------------
 (2,2),(1,1)
(1 row)

postgres=# select box '(1,1),(2,2)';
     box     
-------------
 (2,2),(1,1)
(1 row)

postgres=# select box '((1,1),(2,2))';
     box     
-------------
 (2,2),(1,1)
(1 row)

postgres=# select '((1,1),(2,2))'::box;
     box     
-------------
 (2,2),(1,1)
(1 row)

postgres=# select '1,1,2,2'::box;
     box     
-------------
 (2,2),(1,1)
(1 row)

注意:矩形不可以使用类似线段那种中括号输入方式,如:

postgres=# select box '[(1,1),(2,2)]';
2020-02-26 15:17:08.646 CST [49927] ERROR:  invalid input syntax for type box: "[(1,1),(2,2)]" at character 12
2020-02-26 15:17:08.646 CST [49927] STATEMENT:  select box '[(1,1),(2,2)]';
ERROR:  invalid input syntax for type box: "[(1,1),(2,2)]"
LINE 1: select box '[(1,1),(2,2)]';

路径:

postgres=# select path '1,1,2,2,3,3,4,4';
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '1,1,2,2,3,3,4,4'::path;
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select path '(1,1),(2,2),(3,3),(4,4)';
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '(1,1),(2,2),(3,3),(4,4)'::path;
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select path '((1,1),(2,2),(3,3),(4,4))';
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '((1,1),(2,2),(3,3),(4,4))'::path;
           path            
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select path '[(1,1),(2,2),(3,3),(4,4)]';
           path            
---------------------------
 [(1,1),(2,2),(3,3),(4,4)]
(1 row)

postgres=# select '[(1,1),(2,2),(3,3),(4,4)]'::path;
           path            
---------------------------
 [(1,1),(2,2),(3,3),(4,4)]
(1 row)

注意:路径中使用方括号 “[ ]” 表示开放路径,使用圆括号 “( )" 表示闭合路径,闭合路径指最后一个点与第一个点时连接在一起的。

多边形:

postgres=# select polygon '1,1,2,2,3,3,4,4';
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '1,1,2,2,3,3,4,4'::polygon;
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select polygon '(1,1),(2,2),(3,3),(4,4)';
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '(1,1),(2,2),(3,3),(4,4)'::polygon;
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select polygon '((1,1),(2,2),(3,3),(4,4))';
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

postgres=# select '((1,1),(2,2),(3,3),(4,4))'::polygon;
          polygon          
---------------------------
 ((1,1),(2,2),(3,3),(4,4))
(1 row)

注意:多边形类型输入中不能使用中括号,例如:

postgres=# select '[(1,1),(2,2),(3,3),(4,4)]'::polygon;
2020-02-26 15:31:51.449 CST [49927] ERROR:  invalid input syntax for type polygon: "[(1,1),(2,2),(3,3),(4,4)]" at character 8
2020-02-26 15:31:51.449 CST [49927] STATEMENT:  select '[(1,1),(2,2),(3,3),(4,4)]'::polygon;
ERROR:  invalid input syntax for type polygon: "[(1,1),(2,2),(3,3),(4,4)]"
LINE 1: select '[(1,1),(2,2),(3,3),(4,4)]'::polygon;

圆形:

postgres=# select circle '1,1,5';
  circle   
-----------
 <(1,1),5>
(1 row)

postgres=# select circle '((1,1)5)';
  circle   
-----------
 <(1,1),5>
(1 row)

postgres=# select circle '<(1,1)5>';
  circle   
-----------
 <(1,1),5>
(1 row)

postgres=# select '1,1,5'::circle;
  circle   
-----------
 <(1,1),5>
(1 row)

postgres=# select '((1,1)5)'::circle;
  circle   
-----------
 <(1,1),5>
(1 row)

postgres=# select '<(1,1)5>'::circle;
  circle   
-----------
 <(1,1),5>
(1 row)

注意:圆形不能使用一下输入方式:

postgres=# select '(1,1),5'::circle;
2020-02-26 15:35:05.073 CST [49927] ERROR:  invalid input syntax for type circle: "(1,1),5" at character 8
2020-02-26 15:35:05.073 CST [49927] STATEMENT:  select '(1,1),5'::circle;
ERROR:  invalid input syntax for type circle: "(1,1),5"
LINE 1: select '(1,1),5'::circle;
               ^
postgres=# select circle '(1,1),5';
2020-02-26 15:35:16.025 CST [49927] ERROR:  invalid input syntax for type circle: "(1,1),5" at character 15
2020-02-26 15:35:16.025 CST [49927] STATEMENT:  select circle '(1,1),5';
ERROR:  invalid input syntax for type circle: "(1,1),5"
LINE 1: select circle '(1,1),5';

6.3 几何类型的操作符

Postgresql提供了丰富的几何类型的操作符,如下:

6.3.1 平移运算符:”+“、”-“

平移运算符:”+“、”-“ , 缩放 / 旋转运算符:” * “、” / “

这四个运算符都是二次元运算符,运算符左侧值的类型可以时point、box、path、circle。运算符右侧值的类型只能时 point ,例如:

点和点之间的加减乘除相当于两个复数之间的加减乘除:

postgres=# select point '(1,2)' + point '(10,20)';
 ?column? 
----------
 (11,22)
(1 row)

postgres=# select point '(1,2)' - point '(10,20)';
 ?column? 
----------
 (-9,-18)
(1 row)

postgres=# select point '(1,2)' * point '(10,20)';
 ?column? 
----------
 (-30,40)
(1 row)

postgres=# select point '(1,2)' / point '(10,20)';
 ?column? 
----------
 (0.1,0)
(1 row)

矩形与点之间的运算:

postgres=# select box '((0,0),(1,1))' + point '(2,2)';
  ?column?   
-------------
 (3,3),(2,2)
(1 row)

postgres=# select box '((0,0),(1,1))' - point '(2,2)';
    ?column?     
-----------------
 (-1,-1),(-2,-2)
(1 row)

路径与点之间的运算:

postgres=# select path '(0,0),(1,1),(2,2)' + point '(2,2)';
      ?column?       
---------------------
 ((2,2),(3,3),(4,4))
(1 row)

postgres=# select path '(0,0),(1,1),(2,2)' - point '(2,2)';
        ?column?         
-------------------------
 ((-2,-2),(-1,-1),(0,0))
(1 row)

圆与点的运算:

postgres=# select circle '((0,0),1)' + point '(10,20)';
  ?column?   
-------------
 <(10,20),1>
(1 row)

postgres=# select circle '((0,0),1)' - point '(10,20)';
   ?column?    
---------------
 <(-10,-20),1>
(1 row)

对于乘法,如果乘数的y值为0,比如 point(x,0),则相当于几何对象缩放了x倍,具体如下:

postgres=# select circle '((0,0),1)' - point '(10,20)';
   ?column?    
---------------
 <(-10,-20),1>
(1 row)

postgres=# select point '(1,2)' * point '(2,0)';
 ?column? 
----------
 (2,4)
(1 row)

postgres=# select point '(1,2)' * point '(3,0)';
 ?column? 
----------
 (3,6)
(1 row)

postgres=# select circle '((1,2),1)' * point '(3,0)';
 ?column?  
-----------
 <(3,6),3>
(1 row)

postgres=# select circle '((0,0),1)' * point '(3,0)';
 ?column?  
-----------
 <(0,0),3>
(1 row)

如果乘法为point(0,1),则相当于几何对象逆时针旋转90度,如果乘数为point(0,-1),则表示顺时针旋转90度,例如:

postgres=# select point '(1,2)' * point '(0,1)';
 ?column? 
----------
 (-2,1)
(1 row)

postgres=# select point '(1,2)' * point '(0,-1)';
 ?column? 
----------
 (2,-1)
(1 row)

postgres=# select circle '((0,0),1)' * point '(0,1)';
 ?column?  
-----------
 <(0,0),1>
(1 row)

postgres=# select circle '((1,1),1)' * point '(0,-1)';
  ?column?  
------------
 <(1,-1),1>
(1 row)

6.3.2 运算符 ”#“

用法:
(1)对于两个线段,计算出线段交点。
(2)对于两个矩形,计算出相交的矩形。
(3)对于路径或多边形,则计算出顶点数。

两个线段:

postgres=# select lseg '(0,0),(2,2)' # lseg '(0,2),(2,0)';
 ?column? 
----------
 (1,1)
(1 row)

如果两个线段不相交,返回空:

postgres=# select lseg '(0,0),(2,2)' # lseg '(3,2),(4,5)';
 ?column? 
----------
 
(1 row)

两个矩形:

postgres=# select box '(0,0),(2,2)' # box '(1,0),(3,1)';
  ?column?   
-------------
 (2,1),(1,0)
(1 row)

6.3.3 运算符 ”@-@“

一元运算符,参数类型只能是lseg(线段)、path(路径)。一般用于计算几何对象的长度,例如:

postgres=# select @-@ lseg '(1,0),(2,3)';
      ?column?      
--------------------
 3.1622776601683795
(1 row)

postgres=# select @-@ path '(1,0),(2,3),(5,6)';
      ?column?      
--------------------
 14.616020898215645
(1 row)

注意:开放式路径和闭合式路径的长度时不一样的,例如:

postgres=# select @-@ path '((1,0),(2,3),(5,6))';
      ?column?      
--------------------
 14.616020898215645
(1 row)

postgres=# select @-@ path '[(1,0),(2,3),(5,6)]';
     ?column?      
-------------------
 7.404918347287666
(1 row)

6.3.4 运算符 ”@@“

运算符 ”@@“ 为一元运算符,用于计算中心点,例如:

# 圆
postgres=# select @@ circle '((1,1),4)';
 ?column? 
----------
 (1,1)
(1 row)

# 矩形
postgres=# select @@ box '(0,0),(1,1)';
 ?column?  
-----------
 (0.5,0.5)
(1 row)

# 线段
postgres=# select @@ lseg '(0,0),(2,2)';
 ?column? 
----------
 (1,1)
(1 row)

6.3.5 运算符 ”##“

运算符 ”##“为二元运算符,用于计算两个几何对象上离的最近的点,例如:

postgres=# select point '(0,0)' ## lseg '(2,0),(0,2)';
 ?column? 
----------
 (1,1)
(1 row)

postgres=# select lseg '(0,0),(2,4)' ## lseg '(1,3),(4,5)';
  ?column?  
------------
 (1.75,3.5)
(1 row)

6.3.6 运算符 ”<->“

运算符 ”<->“为二元运算符,用于计算两个几何对象之间的间距,例如:

postgres=# select lseg '(0,0),(2,2)' <-> lseg '(2,3),(4,5)';
 ?column? 
----------
        1
(1 row)

postgres=# select circle '<(0,0),4>' <-> circle '<(6,7),2>';
     ?column?      
-------------------
 3.219544457292887
(1 row)

6.3.7 运算符 ”&&“

运算符 ”&&“ 为二元运算符,用于计算两个几何对象之间是否重叠,只要有一个共同点,则为真,否则返回false。例如:

postgres=# select box '(0,0),(2,2)' && box '(1,0),(4,3)';
 ?column? 
----------
 t
(1 row)

postgres=# select box '(0,0),(2,2)' && box '(3,4),(4,3)';
 ?column? 
----------
 f
(1 row)

6.3.8 判断两个对象相对位置的运算符

判断左右位置的4个运算符:
1.<< : 是否严格在左
\2. >> :是否严格在右
\3. &< :没有延展到左边
\4. &> :没有延展到右边

判断上下位置的6个运算符:
1.<<| :严格在下
2.|>> :严格在上
3.&<| :没有延展到上面
4.|&> :没有延展到下面
5.<^ :在下面(允许接触)
6.>^ :在上面(允许接触)

额外一些运算符:
1.?# :是否相交
2.?- :是否水平或水平对齐
3.?| :是否竖直或竖直对齐
4.?-| :两个对象是否垂直
5.?|| :两个对象是否平行
6.@> :是否包含
7.<@ :包含或在其上

6.3.9 判断两个几何对象是否相同的运算符 ”~=“

对于多边形,如果表示的七点不一样,但是实现它们的是相同的两个多边形,只是位置不同而已,那么对应的判断方式如下:

postgres=# select polygon '((1,1),(0,0))' ~= polygon '((2,2),(3,3))';
 ?column? 
----------
 f
(1 row)

postgres=# select polygon '((1,1),(0,0))' ~= polygon '((0,0),(1,1))';
 ?column? 
----------
 t
(1 row)

postgres=# select box '(0,0),(1,1)' ~= box '(1,1),(0,0)'
postgres-# ;
 ?column? 
----------
 t
(1 row)

6.4 几何类型的函数

函数如下:

函数 返回类型 描述 例子 结果
area(object) double precision 计算几何对象面积 select area(box ‘(2,2),(4,4)’); 4
center(object) point 中心 select center(box ‘(2,2),(4,4)’); 3
diameter(circle) double precision 计算圆的直径 select diameter(circle ‘<(1,1),2>’) 4
height(box) double precision 矩形的高度 select height(box ‘(2,4),(5,9)’); 5
witdth(box) double precision 矩形的宽度 select width(box ‘(2,4),(5,9)’); 3
isclosed(path) blooean 是否是闭合路径 select isclosed(path ‘((1,1),(2,2),(3,3))’); t
isopen(path) blooean 是否是开发路径 select isclosed(path ‘((1,1),(2,2),(3,3))’); f
length(object) double precision 长度 select length(path ‘((1,1),(2,2),(3,3))’); 5.656854
npoints(path) int 计算路径上的点 select npoints(path ‘((1,1),(2,2),(3,3))’); 3
npoints(polygon) int 计算多边形上的点 select npoints(polygon ‘((1,1),(2,2),(3,3),(4,4))’); 4
pclose(path) path 把路径转换为闭合路径(不管参数路径是否是闭合) select pclose(path ‘[(1,1),(2,2),(3,3)]’); ((1,1),(2,2),(3,3))
popen(path) path 把路径转换为开放路径(不管参数路径是否是开放) select popen(path ‘((1,1),(2,2),(3,3))’); [(1,1),(2,2),(3,3)]
radius(circle) double precision 圆的半径 select radius(circle ‘<(1,1),3>’); 3

不同的几何类型还可以相互转换,转换函数如下:

函数 返回类型 描述
box(circle) box 将圆形转换为矩形
box(point,point) box 将两个点转换成矩形
box(polygon) box 将多边形转换成矩形
circle(box) circle 将矩形转换成圆
circle(point,double precision) circle 将圆心和半径转换为圆
circle(polygon) circle 将多边形转换成圆
lseg(box) lseg 将矩形转换为对角线线段
lseg(point,point) lseg 将两个点转化成线段
path(polygon) path 将多边形转换成路径
point(double precision,double precision) point 构建一个点
point(box) point 矩形的中心点
point(circle) point 圆的圆心
point(lseg) point 线段的中心点
point(polygon) point 多边形的中心点
polygon(box) polygon 将矩形转换成4个点的多边形
ploygon(circle) polygon 将圆形转换成12个点的多边形
polygon(npts,circle) polygon 将圆形转换成 npts 个点的多边形
polygon(path) polygon 将路径转换成多边形

7. 复合类型

在Postgresql中可以像C语言中的结构体一样定义一个复合型

7.1 复合类型的定义

如定义一个名为 “person” 的复合类型:

postgres=# create type person as (name text,age integer,sex boolean);
CREATE TYPE

通过以上语句可以看出复合类型的语法类似于create table,但是只能声明字段和类型,不能声明约束(如NOT NULL)。
其中关键词 AS 是关键词,没有 AS ,会出现语法错误。
复合类型创建完后就可以用此类型来创建表的列,例如:

postgres=# create table tb_complex(id int, people person);
CREATE TABLE

也可以在创建的函数中使用创建的复合类型作为参数。

7.2 复合类型的输入

例如:

postgres=# create type person as (name text,age integer,sex boolean);
CREATE TYPE

postgres=# create table tb_complex(id int, people person);
CREATE TABLE

postgres=# create table tb_complex(id int, people person);
CREATE TABLE
postgres=# insert into tb_complex values(1,'("张三",30,true)');
INSERT 0 1
postgres=# insert into tb_complex values(1,'("Alia",27,false)');
INSERT 0 1
postgres=# select * from tb_complex ;
 id |   people    
----+-------------
  1 | (张三,30,t)
  2 | (Alia,27,f)
(2 rows)

可见复合类型的值格式类似: ‘(val1,val2,…)’
使用的是单引号加圆括号的方式,在此格式括号里,可以在复合类型定义的字段值上放双引号进行却别(如字符类型),尤其是值本身包含逗号或者圆括号,则必须加双引号。
如果在复合类型的字段里放一个空值(NULL),当空值的是字符串类型的,要写一对双引号,如果是其他类型(如int或Boolean),就在列表里不写任何字符,例如:

postgres=# insert into tb_complex values(1,'("无名",,true)');
INSERT 0 1
postgres=# insert into tb_complex values(1,'("",20,false)');
INSERT 0 1

当然,也可以用关键词ROW表达式语法来构造复合类型的值,大多数场合,这种方法比直接使用字符串单引号的方法更为简单,不用操心多重引号需要转义导致的各种问题,例如:

postgres=# insert into tb_complex values(1,row('无',40,true));
INSERT 0 1

注意:使用ROW表达式来构造复合类型的值时,括号内的字符串类型就不需要双引号表示,不然插入会报语法错误。

7.3 复合类型的访问

访问复合类型,使用点和域从复合类型中选出一个字段显示,但是在字段前应该加上括号将复合类型名称括起来,避免SQL解析器混淆报错,例如:

postgres=# insert into tb_complex values(1,row('无',40,true));
INSERT 0 1
postgres=# select * from tb_complex ;
 id |   people    
----+-------------
  1 | (张三,30,t)
  2 | (Alia,27,f)
  3 | (无名,,t)
  4 | ("",20,f)
  5 | (无,40,t)
(5 rows)
               ^
postgres=# select (people).name from tb_complex ;
 name 
------
 张三
 Alia
 无名
 
 无
(5 rows)

或者加上表明,如下:

postgres=# select (tb_complex.people).name from tb_complex ;
 name 
------
 张三
 Alia
 无名
 
 无
(5 rows)

类似的查询方式适用任何需要从一个复合类型值中查询一个域,另外,要从一个返回复合类型值的函数中选取一个字段展示,则需要如下方式:

select (my_func(...)).field from table_name

注意:函数外的括号一定要加,不然会产生语法错误!!

7.4 复合类型的修改

1.按照一般表数据的更新方式:

postgres=# select * from tb_complex ;
 id |   people    
----+-------------
  1 | (张三,30,t)
  2 | (Alia,27,f)
  3 | (无名,,t)
  4 | ("",20,f)
  5 | (无,40,t)
(5 rows)

postgres=# update tb_complex set people = ROW('Lina',22,false) where (people).name='Alia';
UPDATE 1
postgres=# select * from tb_complex ;
 id |   people    
----+-------------
  1 | (张三,30,t)
  2 | (无名,,t)
  3 | ("",20,f)
  4 | (无,40,t)
  5 | (Lina,22,f)
(5 rows)

2.单独更新复合类型中的某一个字段:

postgres=# update tb_complex set people.age = 35 where (people).name='张三';
UPDATE 1

注意:在更新语句中指定更新复合类型列中单个字段,复合类型列名不需要加括号。但是在where条件中使用复合类型中单个列查询时必须加括号,不然会报语法错误。

3.既然单个列可以更新,那么复合类型中单个列也可以进行单独插入,如果没指定的列则为空值,例如:

postgres=# insert into tb_complex(id,people.name,people.sex) values (6,'scott',true);
INSERT 0 1

建议在进行复合类型值插入时使用ROW表达式,避免需要输入类似 双引号(“)或者反斜杠时要使用多层转义的麻烦,避免出入出错。

8. 特殊类型(UUID)

8.1 UUID类型

UUID(Universally Unique Identifiers)用于存储一个UUID,UUID定义是一个128bit的数字。
Postgresql提供了UUID类型的比较运算符,例如:

postgres=# select uuid '1b34eaba-0d59-11e4-bf51-dc85de4d74f3' < uuid '1e0e95b0-0d59-11e4-bf51-dc85de4d74f3';
 ?column? 
----------
 t
(1 row)

虽然在Postgresql中没有提供直接的UUID生成函数,但是在contrib下的uuid-ossp模块提供了产生UUID函数。可以通过一下方式来生成UUID:
1.先安装系统os依赖包uuid、uuid-devel:

yum -y install uuid uuid-devel

安装完成可能 uuid-devel包没有,我使用Centos 6.5配置本地yum源(没有互联网)在使用yum安装时没有uuid-devel依赖包,因此我从网上下载了一个rpm包(参考网址:https://centos.pkgs.org/6/centos-x86_64/uuid-devel-1.6.1-10.el6.x86_64.rpm.html),然后使用rpm进行安装:

rpm -ivh uuid-devel-1.6.1-10.el6.x86_64.rpm

2.依赖包安装完成后,进行预编译(postgresql的源码包我还留着,进到源码包后再次进行编译):

./configure --prefix=/data/pg12.1 --with-uuid=ossp

注意:我PG安装目录是/data/pg12.1

3.进入源码包的contrb文件夹下的uuid-ossp目录下,进行gmake && gmake install :

cd contrib/uuid-ossp
gmake clean
gmake
gmake install

注意:我在安装PG时用的make install-world方式安装了contrib下所有插件,因为没有依赖包uuid、uuid-devel,所以没有成功安装uuid-ossp,所以我需要gmake clean清除一下。顺便说一下,如果在安装PG时安装了依赖包,那么就不需要在进行1~3步骤。直接进行第四步。如果在进行第4步时报如下错误,则说明没有成功安装uuid-ossp插件,还需要从第1步开始:
postgres=# create extension “uuid-ossp”;
ERROR: could not open extension control file “/data/pg12.1/share/postgresql/extension/uuid-ossp.control”: No such file or directory

4.登录postgresql,执行create extension,然后可以通过uuid_generate_v4()生成uuid:

[postgres@local130 ~]$ psql -U postgres -W -d postgres
psql (12.7)
Type "help" for help.

xzxt=# create extension "uuid-ossp";
CREATE EXTENSION
xzxt=# select uuid_generate_v4();
           uuid_generate_v4           
--------------------------------------
 b1cd147b-d39e-4805-8c09-dcfb7cf19737
(1 row)

注意:需要在哪个库下使用uuid就在登陆哪个库下执行,一般登陆时指定-d参数到指定的库下执行create extension,会在指定的库下的public模式下创建,并可以使用uuid,执行后会在指定的库的public模式的function中看到uuid开头的函数:

xzxt=# \df
                                                                         List of functions
 Schema |        Name        | Result data type  |                                           Argument data types                                            | Type 
--------+--------------------+-------------------+----------------------------------------------------------------------------------------------------------+------
 public | fn_xxcj_ngzbhzh    | character varying | unitcode character varying, tblname character varying, lrsj character varying, headstr character varying | func
 public | fn_xxcj_xxzjbh     | character varying | unitcode character varying, strdate character varying                                                    | func
 public | uuid_generate_v1   | uuid              |                                                                                                          | func
 public | uuid_generate_v1mc | uuid              |                                                                                                          | func
 public | uuid_generate_v3   | uuid              | namespace uuid, name text                                                                                | func
 public | uuid_generate_v4   | uuid              |                                                                                                          | func
 public | uuid_generate_v5   | uuid              | namespace uuid, name text                                                                                | func
 public | uuid_nil           | uuid              |                                                                                                          | func
 public | uuid_ns_dns        | uuid              |                                                                                                          | func
 public | uuid_ns_oid        | uuid              |                                                                                                          | func
 public | uuid_ns_url        | uuid              |                                                                                                          | func
 public | uuid_ns_x500       | uuid              |                                                                                                          | func
(12 rows)

8.2 pg_lsn类型

pg_lsn类型时PG9.1之后提供的表示LSN(Log Squence Number)的一种数据类型,LSN表示WAL日志的位置,在一些记录WAL日志信息的系统表中某些字段类型可以用pg_lsn类型。

9. Range类型

9.1 Range类型介绍

Range类型是PG 9.2之后开始出现的一种特有的类型,用于表现范围,如一个整数的范围、一个时间的范围,而范围底下的基本类型(如整数、时间)则被成为Range类型的subtype。
Range数据类型可以更快的在范围条件查询中检索到数据。
例如:
某个IP地址库记录了每个地区的IP地址的范围,现在需要查询指定IP地址在那个地区,IP库记录表为如下结构:

create table tb_ip_range(
begin_ip inet,
end_ip inet,
area text,
sp text);

现在查询IP地址192.188.17.120在那个地区,对应的查询语句如下:
select * from tb_ip_range where begin_ip <= ‘192.188.17.120’::inet and end_id >= ‘192.188.17.120’::inet
虽然在表上的begin_ip和end_ip列都创建了索引,但是在Postgresql中,虽然SQL语句会走索引,但是查询方式时分别扫描两个索引建位图,然后通过位图进行and操作,对比SQL语句的执行计划,索引的范围扫描还不是最高效的。
Range类型时通过创建空间索引的方式执行SQL语句,例如:
创建类似的IP地址库表:

现在查询IP地址192.188.17.120在那个地区,对应的查询语句如下:
select * from tb_ip_range where begin_ip <= ‘192.188.17.120’::inet and end_id >= ‘192.188.17.120’::inet
虽然在表上的begin_ip和end_ip列都创建了索引,但是在Postgresql中,虽然SQL语句会走索引,但是查询方式时分别扫描两个索引建位图,然后通过位图进行and操作,对比SQL语句的执行计划,索引的范围扫描还不是最高效的。
Range类型时通过创建空间索引的方式执行SQL语句,例如:
创建类似的IP地址库表:

1.创建Range类型:

create type inetrange as Range (subtype=inet);

2.创建IP范围表:

create table tb_ip_range(
ip_range inetrange,
area text,
sp text);

3.在ip_range列上创建索引,然后通过包含运算符 ”@>“ 查找对应的数据:

select * from tb_ip_range where ip_range @> ‘192.188.17.120’::inet

查看对应的SQL语句的执行计划可以明显看到性能提高很多。

9.2 Range类型的创建

在Postgresql中已经内置了一些常用的Range类型,可以不用执行create type xxx as range来创建:

int4range : 4字节整数的范围类型
int8range : 8字节大整数的范围类型
numrange : numeric的范围类型
tsrange : 无时区的时间戳范围类型
tstzrange : 带时区的时间戳范围类型
daterange : 日期的范围类型

示例:

# 时间范围
CREATE TABLE reservation (room int, during tsrange);
INSERT INTO reservation VALUES
    (1108, '[2010-01-01 14:30, 2010-01-01 15:30)');

# 整数是否包含在范围内
postgres=# select int4range(10,20) @> 3;
 ?column? 
----------
 f
(1 row)

postgres=# select int4range(10,20) @> 11;
 ?column? 
----------
 t
(1 row)

# 判断两个范围是否有交集,有true,没有false
postgres=# select numrange(11.1,22.2) && numrange(20.0,30.0);
 ?column? 
----------
 t
(1 row)

postgres=# select numrange(11.1,22.2) && numrange(23.0,30.0);
 ?column? 
----------
 f
(1 row)

# 使用uper提取范围的最大边界
postgres=# select upper(int8range(15,30));
 upper 
-------
    30
(1 row)

postgres=# select upper(int8range(15,35));
 upper 
-------
    35
(1 row)

# 提取两个范围共同的交集
postgres=# select int4range(10, 20) * int4range(15, 25);
 ?column? 
----------
 [15,20)
(1 row)

postgres=# select int4range(10, 30) * int4range(15, 25);
 ?column? 
----------
 [15,25)
(1 row)


postgres=# select isempty(numrange(1,5));
 isempty 
---------
 f
(1 row)

如果以上内置的range类型不符合你的要求,可以使用CREATE TYPE来创建自定义的Range类型,语法如下:

CREATE TYPE name AS RANGE(
SUBTYPE = subtype
[ , SUBYTPE_OPCLASS = subtype_operator_class ]
[ , CLLATION = collation ]
[ , CANONICAL = canonical_function ]
[ , SUBTYPE_DIFF = subtype_diff_function ]

说明:
1.SUBTYPE = subtype:指定子类型
2.SUBYTPE_OPCLASS = subtype_operator_class:指定子类型的操作符
3.CLLATION = collation:指定排序规则
4.CANONICAL = canonical_function:如果要创建一个稀疏的Range类型,而不是一个连续的Range类型,那就定义此函数
5.SUBTYPE_DIFF = subtype_diff_function:定义子类型的差别函数

示例:

CREATE TYPE floatrange AS RANGE(
SUBTYPE=float8;
SUBTYPE_DIFF=float8mi
);

9.3 Range类型的输入

Range类型的输入格式:
’(lower-bound,upper-bound)‘
‘(lower-bound,upper-bound]’
’[lower-bound,upper-bound)‘
‘[lower-bound,upper-bound]’
’empty‘

其中,”(“ 和 ”)“ 表示定义的范围不包括此元素,”[“ 和 ”]“ 表示定义的范围包括此元素,’empty‘ 表示空,空表示范围内不包含任何东西。
示例:

-- includes 3, does not include 7, and does include all points in between
SELECT '[3,7)'::int4range;

-- does not include either 3 or 7, but includes all points in between
SELECT '(3,7)'::int4range;

-- includes only the single point 4
SELECT '[4,4]'::int4range;

-- includes no points (and will be normalized to 'empty')
SELECT '[4,4)'::int4range;

每个范围类型都有一个与范围类型同名的构造函数。使用构造函数通常比编写范围文字常量更方便,因为它避免了对绑定值的额外引用。构造函数接受两个或三个参数。两个参数的形式以标准形式构造一个范围(下界包括,上界排除),而三个参数的形式构造一个范围,其边界由第三个参数指定。第三个参数必须是字符串“()”、“()”、“()”或“[]”中的一个。例如:

-- The full form is: lower bound, upper bound, and text argument indicating inclusivity/exclusivity of bounds.
postgres=# select numrange(1.0, 14.0, '(]');
  numrange  
------------
 (1.0,14.0]
(1 row)

-- If the third argument is omitted, '[)' is assumed.
postgres=# select numrange(1.0, 14.0);
  numrange  
------------
 [1.0,14.0)
(1 row)

-- Although '(]' is specified here, on display the value will be converted to
-- canonical form, since int8range is a discrete range type (see below).
postgres=# select int8range(1, 14, '(]');
 int8range 
-----------
 [2,15)
(1 row)

Range类型还可以表示极值的区间,例如:

1.表示从1开始到int4可以表示的最大值:

postgres=# select '[1,)'::int4range;
 int4range 
-----------
 [1,)
(1 row)

2.表示从int4可以表示的最小值到1的范围:

postgres=# select '[,1)'::int4range;
 int4range 
-----------
 (,1)
(1 row)

3.对于numrange范围,可以表示无穷大或者无穷小,例如,从1到无穷大与负无穷到1:

postgres=# select '[1,)'::numrange;
 numrange 
----------
 [1,)
(1 row)

postgres=# select '[,1)'::numrange;
 numrange 
----------
 (,1)
(1 row)

注意:与numrange不同的是,int4range里不是无穷大或者负无穷,因为int4有具体的范围。

9.4 Range类型的操作符和函数

1.Range类型支持的操作符:

操作符 描述 例子 结果
= 等于 select int4range’[1,5)’ = ‘[1,4]’::int4range; t
<> 不等于 select numrange(1.1,1.2) <> numrange(1.1,1.3); t
< 小于 select int4range ‘[1,10)’ < int4range’[2,3)’; t
> 大于 select int4range ‘[2,3)’ > int4range’[1,100)’; t
<= 小于等于 select int4range’[2,3)’<= int4range’[1,2)’; f
>= 大于等于 select int4range’[2,3)’>= int4range’[1,2)’; t
@> 包含(左边包含了右边) select int4range’[1,3)’ @> int4range’[1,2)’; t
<@ 被包含(右边包含左边) select int4range’[1,2)’ <@ int4range’[1,4)’; t
&& 重叠(两个范围有交集) select int4range’[1,2)’ && int4range’[1,4)’; t
<< 严格在左(没有重叠值) select int4range’[1,2)’ << int4range’[2,4)’; t
>> 严格在右 select int4range’[2,4)’ >> int4range’[1,2)’; t
&< 没有扩展到右边 select int4range’[1,2)’ &< int4range’[1,4)’; t
&> 没有扩展到左边 select int4range’[1,2)’ &> int4range’[1,4)’; t
-l- 链接在一起(值没有交集) select int4range’[1,2)’ - - int4range’[2,4)’;
+ union(将两个范围合并在一起) select int4range’[1,2)’ + int4range’[2,4)’; [1,4)
* intersection select int4range’[1,4)’ * int4range’[2,5)’; [2,4)
- difference select int4range’[1,4)’ - int4range’[2,5)’; [1,2)

2.Range类型的函数:

1.lower(anyrange) : 获得范围的起始值

postgres=# select lower(int4range '[11,22)');
 lower 
-------
    11
(1 row)

postgres=# select lower(int4range '[11,22)') is null;;
 ?column? 
----------
 f
(1 row)

postgres=# select lower(int4range '[11,22)') is not null;
 ?column? 
----------
 t
(1 row)

2.upper(anyrange):获得范围的结束值

postgres=# select upper(int4range '[11,22)');
 upper 
-------
    22
(1 row)

postgres=# select upper(int4range '[11,34]');
 upper 
-------
    35
(1 row)

注意:获取的范围结束值是不包含在范围内的最大值!

3.isempty(anyrange):是否是空范围

postgres=# select isempty(int4range'empty');
 isempty 
---------
 t
(1 row)

postgres=# select isempty(int4range'(,)');
 isempty 
---------
 f
(1 row)

postgres=# select isempty(int4range'(1,1)');
 isempty 
---------
 t
(1 row)

postgres=# select isempty(int4range'[1,2)');
 isempty 
---------
 f
(1 row)

4.lower_inc(anyrange):起始值是否在范围内

postgres=# select lower_inc(int4range'(1,2)');
 lower_inc 
-----------
 f
(1 row)

postgres=# select lower_inc(int4range'[1,2)');
 lower_inc 
-----------
 t
(1 row)

postgres=# select lower_inc(int4range'(1,4]');
 lower_inc 
-----------
 t
(1 row)

5.upper_inc(anyrange):结束值是否在范围内

postgres=# select upper_inc(int4range'(1,2)');
 upper_inc 
-----------
 f
(1 row)

postgres=# select upper_inc(int4range'(1,3]');
 upper_inc 
-----------
 f
(1 row)

6.lower_inf(anyrange):起始值是否是一个无穷值
7.upper_inf(anyrange):结束值是否是一个无穷值

9.5 Range类型的索引和约束

1.索引:

可以为范围类型的表列创建GiST和SP-GiST索引。例如,创建一个GiST索引:

CREATE INDEX reservation_idx ON reservation USING GIST (during);

索引创建后可以在SQL语句中使用Range类型的操作符来进行索引数据检索。

此外,可以为范围类型的表列创建B-tree和hash索引。对于这些索引类型,基本上惟一有用的范围操作是相等操作。有一个为范围值定义的B-tree排序,具有相应的<和>操作符,但是排序是相当随意的,在现实世界中通常没有用处。Range类型的b -树和散列支持主要是为了允许在查询内部进行排序和散列,而不是创建实际的索引。

2.约束

在Range类型的列上一般创建排除约束,让其范围总是不重叠的,例如:

CREATE TABLE reservation (
    during tsrange,
    EXCLUDE USING GIST (during WITH &&)
);

INSERT INTO reservation VALUES
    ('[2010-01-01 11:30, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO reservation VALUES
    ('[2010-01-01 14:45, 2010-01-01 15:45)');
ERROR:  conflicting key value violates exclusion constraint "reservation_during_excl"
DETAIL:  Key (during)=(["2010-01-01 14:45:00","2010-01-01 15:45:00")) conflicts
with existing key (during)=(["2010-01-01 11:30:00","2010-01-01 15:00:00")).

由以上例子看出,列上创建了排他约束,插入数据时,如果与原有数据范围有重叠时就会报错。

如果有一个两个列的表,第一个列时普通类型是 id ,第二个时Range类型是 during ,插入数据时判断,如果 id 值相等时,如果Range列范围有重叠,插入报错,如果 id 值不相等,那么Range列即使范围重叠也不报错,可以进行数据插入。这时就需要Postgresql的扩展模块 btree_gist 来实现:

CREATE EXTENSION btree_gist;
CREATE TABLE room_reservation (
    id intege,
    during tsrange,
    EXCLUDE USING GIST (room WITH =, during WITH &&)
);

INSERT INTO room_reservation VALUES
    (1, '[2010-01-01 14:00, 2010-01-01 15:00)');
INSERT 0 1

INSERT INTO room_reservation VALUES
    (1, '[2010-01-01 14:30, 2010-01-01 15:30)');
ERROR:  conflicting key value violates exclusion constraint "room_reservation_room_during_excl"
DETAIL:  Key (room, during)=(123A, ["2010-01-01 14:30:00","2010-01-01 15:30:00")) conflicts
with existing key (room, during)=(123A, ["2010-01-01 14:00:00","2010-01-01 15:00:00")).

INSERT INTO room_reservation VALUES
    (2, '[2010-01-01 14:30, 2010-01-01 15:30)');
INSERT 0 1
posted @ 2024-08-11 15:19  零の守墓人  阅读(39)  评论(0编辑  收藏  举报