弄好pgsql的环境后,我大概玩了一下,并且数了下里面的数据类型,发现至少有一百多种,WTF?
PostgreSQL 有着丰富的内置数据类型可用。用户还可以使用 CREATE TYPE 命令增加新的数据类型(摘抄官网)。
PostgreSQL文档地址
https://www.yiibai.com/manual/postgresql/index.html
查看官方文档后发现很多都是因历史原因 PostgreSQL 在内部使用的名字。另外,还有一些内部使用的或者废弃的类型也可以使用,但没有再这类列出,在使用pgAdmin创建表的时候可以看到。
普通数据类型
名字 | 别名 | 描述 |
---|---|---|
bigint | int8 | 有符号 8 字节整数 |
bigserial | serial8 | 自增 8 字节整数 |
bit [ (n) ] | 定长位串 | |
bit varying [ (n) ] | varbit | 变长位串 |
boolean | bool | 逻辑布尔值(真/假) |
box | 平面中的矩形 | |
bytea | 二进制数据("字节数组") | |
character varying [ (n) ] | varchar [ (n) ] | 变长字符串 |
character [ (n) ] | char [ (n) ] | 定长字符串 |
cidr | IPv4 或 IPv6 网络地址 | |
circle | 平面中的圆 | |
date | 日历日期(年, 月, 日) | |
double precision | float8 | 双精度浮点数字 |
inet | IPv4 或 IPv6 网络地址 | |
integer | int, int4 | 有符号 4 字节整数 |
interval [ (p) ] | 时间间隔 | |
line | 平面中的无限长直线 | |
lseg | 平面中的线段 | |
macaddr | MAC 地址 | |
money | 货币金额 | |
numeric [ (p, s) ] | decimal [ (p, s) ] | 可选精度的准确数字 |
path | 平面中的几何路径 | |
point | 平面中的点 | |
polygon | 平面中的封闭几何路径 | |
real | float4 | 单精度浮点数 |
smallint | int2 | 有符号 2 字节整数 |
serial | serial4 | 自增 4 字节整数 |
text | 变长字符串 | |
time [ (p) ] [ without time zone ] | 一天中的时间 | |
time [ (p) ] with time zone | timetz | 一天里的时间,包括时区 |
timestamp [ (p) ] [ without time zone ] | 日期和时间 | |
timestamp [ (p) ] with time zone | timestamptz | 日期和时间,包括时区 |
数值类型
名字 | 存储空间 | 描述 | 范围 |
---|---|---|---|
smallint | 2 字节 | 小范围整数 | -32768 到 +32767 |
integer | 4 字节 | 常用的整数 | -2147483648 到 +2147483647 |
bigint | 8 字节 | 大范围的整数 | -9223372036854775808 到 9223372036854775807 |
decimal | 变长 | 用户声明精度,精确 | 无限制 |
numeric | 变长 | 用户声明精度,精确 | 无限制 |
real | 4 字节 | 变精度,不精确 | 6 位十进制数字精度 |
double precision | 8 字节 | 变精度,不精确 | 15 位十进制数字精度 |
serial | 4 字节 | 自增整数 | 1 到 2147483647 |
bigserial | 8 字节 | 大范围的自增整数 | 1 到 9223372036854775807 |
字符类型
名字 | 描述 |
---|---|
character varying(n), varchar(n) | 变长,有长度限制 |
character(n), char(n) | 定长,不足补空白 |
text | 变长,无长度限制 |
特殊字符类型
名字 | 存储空间 | 描述 |
---|---|---|
"char" | 1 字节 | 单字节内部类型 |
name | 64 字节 | 用于对象名的内部类型 |
二进制数据类型
名字 | 存储空间 | 描述 |
---|---|---|
bytea | 4 字节加上实际的二进制字符串 | 变长的二进制字符串 |
bytea 文本逃逸八进制
十进制数值 | 描述 | 输入逃逸形式 | 例子 | 输出形式 |
---|---|---|---|---|
0 | 八进制的零 | E'\\000' | SELECT E'\\000'::bytea; | \000 |
39 | 单引号 | '''' 或 E'\\047' | SELECT E'\''::bytea; | ' |
92 | 反斜杠 | E'\\\\' 或 E'\\134' | SELECT E'\\\\'::bytea; | \\ |
0 到 31 以及 127 到 255 | "不可打印"字节 | E'\\xxx'(八进制值) | SELECT E'\\001'::bytea; | \001 |
bytea 输出逃逸序列
字节的十进制值 | 描述 | 逃逸的输出形式 | 例子 | 输出结果 |
---|---|---|---|---|
92 | 反斜杠 | \\ | SELECT E'\\134'::bytea; | \\ |
0 到 31 以及 127 到 255 | "不可打印"八进制字符 | \xxx(八进制值) | SELECT E'\\001'::bytea; | \001 |
32 到 126 | "可打印"八进制字符 | 客户端字符集表现形式 | SELECT E'\\176'::bytea; | ~ |
日期/时间类型
名字 | 存储空间 | 描述 | 最低值 | 最高值 | 分辨率 |
---|---|---|---|---|---|
timestamp [ (p) ] [ without time zone ] | 8 字节 | 日期和时间 | 4713 BC | 5874897 AD | 1 毫秒 / 14 位 |
timestamp [ (p) ] with time zone | 8 字节 | 日期和时间,带时区 | 4713 BC | 5874897 AD | 1 毫秒 / 14 位 |
interval [ (p) ] | 12 字节 | 时间间隔 | -178000000 年 | 178000000 年 | 1 毫秒 / 14 位 |
date | 4 字节 | 只用于日期 | 4713 BC | 5874897 AD | 1 天 |
time [ (p) ] [ without time zone ] | 8 字节 | 只用于一日内时间 | 00:00:00 | 24:00:00 | 1 毫秒 / 14 位 |
time [ (p) ] with time zone | 12 字节 | 只用于一日内时间,带时区 | 00:00:00+1459 | 24:00:00-1459 | 1 毫秒 / 14 位 |
日期输入(date)
例子 | 描述 |
---|---|
January 8, 1999 | 在任何 DateStyle 输入模式下都无歧义 |
1999-01-08 | ISO 8601 格式(建议格式),任何方式下都是 1999 年 1 月 8 号 |
1/8/1999 | 有歧义,在 MDY 下是一月八号;在 DMY 模式下是八月一日 |
1/18/1999 | MDY 模式下是一月十八日,其它模式下被拒绝 |
01/02/03 | MDY 模式下的 2003 年 1 月 2 日;DMY 模式下的 2003 年 2 月 1 日;YMD 模式下的 2001 年 2 月 3 日 |
1999-Jan-08 | 任何模式下都是 1 月 8 日 |
Jan-08-1999 | 任何模式下都是 1 月 8 日 |
08-Jan-1999 | 任何模式下都是 1 月 8 日 |
99-Jan-08 | YMD 模式下是 1 月 8 日,否则错误 |
08-Jan-99 | 一月八日,除了在 YMD 模式下是错误的之外 |
Jan-08-99 | 一月八日,除了在 YMD 模式下是错误的之外 |
19990108 | ISO 8601;任何模式下都是 1999 年 1 月 8 日 |
990108 | ISO 8601;任何模式下都是 1999 年 1 月 8 日 |
1999.008 | 年和年里的第几天 |
J2451187 | 儒略日 |
January 8, 99 BC | 公元前 99 年 |
时间输入(time)
例子 | 描述 |
---|---|
04:05:06.789 | ISO 8601 |
04:05:06 | ISO 8601 |
04:05 | ISO 8601 |
040506 | ISO 8601 |
04:05 AM | 与 04:05 一样;AM 不影响数值 |
04:05 PM | 与 16:05 一样;输入小时数必须 <= 12 |
04:05:06.789-8 | ISO 8601 |
04:05:06-08:00 | ISO 8601 |
04:05-08:00 | ISO 8601 |
040506-08 | ISO 8601 |
04:05:06 PST | 缩写的时区 |
2003-04-12 04:05:06 America/New_York | 用名字声明的时区 |
日期/时间输出风格
风格 | 描述 | 例子 |
---|---|---|
ISO | ISO 8601/SQL 标准 | 1997-12-17 07:37:16-08 |
SQL | 传统风格 | 12/17/1997 07:37:16.00 PST |
POSTGRES | 原始风格 | Wed Dec 17 07:37:16 1997 PST |
German | 地区风格 | 17.12.1997 07:37:16.00 PST |
PostgreSQL支持 SQL 标准的 boolean 数据类型。boolean 只能有"true"(真)或"false"(假)两个状态之一,第三种"unknown"(未知)状态,用 NULL 表示。
"真"值的有效文本值是:
TRUE |
't' |
'true' |
'y' |
'yes' |
'1' |
对于"假",你可以使用下面这些:
FALSE |
'f' |
'false' |
'n' |
'no' |
'0' |
使用 TRUE 和 FALSE 这样的字眼比较好(也是 SQL 兼容的用法)。
例8-2. 使用 boolean 类型
CREATE TABLE test1 (a boolean, b text); INSERT INTO test1 VALUES (TRUE, 'sic est'); INSERT INTO test1 VALUES (FALSE, 'non est'); SELECT * FROM test1; a | b ---+--------- t | sic est f | non est SELECT * FROM test1 WHERE a; a | b ---+--------- t | sic est
boolean 使用 1 字节存储空间。
几何类型
名字 | 存储空间 | 说明 | 表现形式 |
---|---|---|---|
point | 16 字节 | 平面中的点 | (x,y) |
line | 32 字节 | (无穷)直线(未完全实现) | ((x1,y1),(x2,y2)) |
lseg | 32 字节 | (有限)线段 | ((x1,y1),(x2,y2)) |
box | 32 字节 | 矩形 | ((x1,y1),(x2,y2)) |
path | 16+16n 字节 | 闭合路径(与多边形类似) | ((x1,y1),...) |
path | 16+16n 字节 | 开放路径 | [(x1,y1),...] |
polygon | 40+16n 字节 | 多边形(与闭合路径相似) | ((x1,y1),...) |
circle | 24 字节 | 圆 | <(x,y),r> (圆心和半径) |
点是几何类型的基本二维构造单位。用下面语法描述 point 的数值:
( x, y ) x, y
线段(lseg)是用一对点来代表的。lseg 的值用下面语法声明:
( ( x1, y1 ) , ( x2, y2 ) ) ( x1, y1 ) , ( x2, y2 ) x1, y1, x2, y2
这里的 (x1, y1) 和 (x2, y2) 是线段的端点。
矩形是用一对对角点来表示的。box 的值用下面语法声明:
( ( x1, y1 ) , ( x2, y2 ) ) ( x1, y1 ) , ( x2, y2 ) x1, y1, x2, y2
这里的 (x1, y1) 和 (x2, y2) 是矩形的一对对角点。
矩形的输出使用第一种语法。在输入时将按先右上角后左下角的顺序重新排列。你也可以输入另外一对对角点,但存储时将计算出左下角和右上角然后再存储。
路径由一系列连接的点组成。路径可能是开放的,也就是认为列表中第一个点和最后一个点没有连接,也可能是闭合的,这时认为第一个和最后一个点连接起来。
path的数值用下面语法声明:
( ( x1, y1 ) , ... , ( xn, yn ) ) [ ( x1, y1 ) , ... , ( xn, yn ) ] ( x1, y1 ) , ... , ( xn, yn ) ( x1, y1 , ... , xn, yn ) x1, y1 , ... , xn, yn
这里的点是组成路径的线段的端点。方括弧([])表明一个开放的路径,圆括弧(())表明一个闭合的路径。
路径的输出使用第一种语法输出。
多边形由一系列点代表(多边形的顶点)。多边形可以认为与闭合路径一样,但是存储方式不一样而且有自己的一套支持函数。
polygon 的数值用下列语法声明:
( ( x1, y1 ) , ... , ( xn, yn ) ) ( x1, y1 ) , ... , ( xn, yn ) ( x1, y1 , ... , xn, yn ) x1, y1 , ... , xn, yn
这里的点是多边形的端点。
多边形输出使用第一种语法。
圆由一个圆心和一个半径标识。circle 的数值用下面语法表示:
< ( x, y ) , r > ( ( x, y ) , r ) ( x, y ) , r x, y, r
这里的 (x, y) 是圆心,r 是半径。
圆的输出用第一种格式。
网络地址类型
名字 | 存储空间 | 描述 |
---|---|---|
cidr | 12 或 24 字节 | IPv4 或 IPv6 网络 |
inet | 12 或 24 字节 | IPv4 或 IPv6 网络和主机 |
macaddr | 6 字节 | MAC 地址 |
在对 inet 或 cidr 数据类型进行排序的时候,IPv4 地址总是排在 IPv6 地址前面,包括那些封装或者是映射在 IPv6 地址里的 IPv4 地址,比如 ::10.2.3.4 或 ::ffff::10.4.3.2
inet 和 cidr 类型之间的基本区别是 inet 接受子网掩码,而 cidr 不接受。
【提示】如果你不喜欢 inet 或 cidr 值的输出格式,请试一下
host
,text
,abbrev
函数。
位串就是一串 1 和 0 的字符串。它们可以用于存储和直观化位掩码。我们有两种 SQL 位类型:bit(n) 和 bit varying(n) ,这里的 n 是一个正整数。
bit 类型的数据必须准确匹配长度 n ,试图存储短些或者长一些的数据都是错误的。bit varying 类型数据是最长 n 的变长类型;更长的串会被拒绝。写一个没有长度的 bit 等效于 bit(1) ,没有长度的 bit varying 意思是没有长度限制。
【注意】如果我们明确地把一个位串值转换成 bit(n) ,那么它的右边将被截断或者在右边补齐零,直到刚好 n 位,而不会抛出任何错误。类似地,如果我们明确地把一个位串数值转换成 bit varying(n) ,如果它超过了 n 位,那么它的右边将被截断。
请参考节4.1.2.3获取有关位串常量的语法信息。还有一些位逻辑操作符和位处理函数可用;参见节9.6。
例 使用位串类型
CREATE TABLE test (a BIT(3), b BIT VARYING(5)); INSERT INTO test VALUES (B'101', B'00'); INSERT INTO test VALUES (B'10', B'101'); ERROR: bit string length 2 does not match type bit(3) INSERT INTO test VALUES (B'10'::bit(3), B'101'); SELECT * FROM test; a | b -----+----- 101 | 00 100 | 101
为说明这些用法,我们先创建一个由基本类型数组构成的表:
PostgreSQL允许将字段定义成定长或变长的一维或多维数组。数组类型可以是任何基本类型或用户定义类型。不支持复合类型和域的数组。
CREATE TABLE sal_emp ( name text, pay_by_quarter integer[], schedule text[][] );
如上所示,一个数组类型是通过在数组元素类型名后面附加方括弧([])来命名的。上面的命令将创建一个叫 sal_emp 的表,表示雇员名字的 name 字段是一个 text 类型字符串,表示雇员季度薪水的 pay_by_quarter 字段是一个一维 integer 数组,表示雇员周计划的 schedule 字段是一个两维 text 数组。
CREATE TABLE 的语法允许声明数组的确切大小,比如:
CREATE TABLE tictactoe ( squares integer[3][3] );
不过,目前的实现并不强制数组尺寸限制(等价于未声明长度的数组)。实际上,目前的实现也不强制数组维数。特定元素类型的数组都被认为是相同的类型,不管他们的大小或者维数。因此,在 CREATE TABLE 里定义数字或者维数都不影响运行时的行为。
另外还有一种语法,它遵循 SQL 标准,可以用于声明一维数组。pay_by_quarter 可以定义为:
pay_by_quarter integer ARRAY[4],
这个语法要求一个整数常量表示数组尺寸。不过,如前所述,PostgreSQL 并不强制这个尺寸限制。
下面是两个定义复合类型的简单例子:
CREATE TYPE complex AS ( r double precision, i double precision ); CREATE TYPE inventory_item AS ( name text, supplier_id integer, price numeric );
语法类似于 CREATE TABLE ,只是这里只可以声明字段名字和类型;目前不能声明约束(比如 NOT NULL)。请注意 AS 关键字是很重要的;没有它,系统会认为这是完全不同的 CREATE TYPE 命令,因此你会看到奇怪的语法错误。
定义了类型,我们就可以用它创建表:
CREATE TABLE on_hand ( item inventory_item, count integer ); INSERT INTO on_hand VALUES (ROW('fuzzy dice', 42, 1.99), 1000);
PostgreSQL在内部使用对象标识符(OID)作为各种系统表的主键。同时,系统不会给用户创建的表增加一个 OID 系统字段(除非在建表时声明了 WITH OIDS 或者配置参数 default_with_oids 设置为开启)。oid 类型代表一个对象标识符。除此以外 oid 还有几个别名:regproc, regprocedure, regoper, regoperator, regclass, regtype 。表8-19显示了概览。
目前 oid 类型用一个四字节的无符号整数实现。因此,它不够提供大数据库范围内的唯一性保证,甚至在单个的大表中也不行。因此,我们不鼓励在用户创建的表中使用 OID 字段做主键。OID 最好只是用于系统表。
oid 类型本身除了比较之外还有几个操作。不过,它可以转换为整数,然后用标准的整数操作符操作。如果你这么干,请注意可能的有符号和无符号之间的混淆。
OID 别名类型除了输入和输出过程之外没有自己的操作。这些过程可以为系统对象接受和显示符号名,而不仅仅是类型 oid 将要使用的行数值。别名类型允许我们简化为对象查找 OID 值的过程。比如,检查和一个表 mytable 相关的 pg_attribute 行,我们可以这样写
SELECT * FROM pg_attribute WHERE attrelid = 'mytable'::regclass;
而不用
SELECT * FROM pg_attribute WHERE attrelid = (SELECT oid FROM pg_class WHERE relname = 'mytable');
虽然看上去不坏,但是这个例子还是简化了好多,如果在不同的模式里有好多叫 mytable 的表,那么我们需要写一个更复杂的子查询。regclass 的输入转换器处理根据模式路径设置的表检索工作,所以它自动干了"正确的事情"。类似的还有,把一个表的 OID 转换成 regclass 是查找一个 OID 对应的符号名称的最简单方法。
表. 对象标识符类型
名字 | 引用 | 描述 | 数值例子 |
---|---|---|---|
oid | 任意 | 数字化的对象标识符 | 564182 |
regproc | pg_proc | 函数名字 | sum |
regprocedure | pg_proc | 带参数类型的函数 | sum(int4) |
regoper | pg_operator | 操作符名 | + |
regoperator | pg_operator | 带参数类型的操作符 | *(integer,integer) 或 -(NONE,integer) |
regclass | pg_class | 关系名 | pg_type |
regtype | pg_type | 数据类型名 | integer |
所有 OID 别名类型都接受有模式修饰的名字,并且如果在当前搜索路径中不增加修饰无法找到该对象的话,那么在输出时将显示有模式修饰的名字。regproc 和 regoper 别名类型将只接受唯一的输入名字(不能重载),因此它们的用途有限。对于大多数应用,regprocedure 或 regoperator 更合适。对于 regoperator,单目操作符是通过在那些未用的操作数上写 NONE 来标识的。
OID 别名类型的一个额外的属性是如果这些类型之一的常量出现在一个存储的表达式里(比如字段缺省表达式或者视图),它在被引用的对象上创建一个依赖性。比如,如果一个字段有缺省的 nextval('my_seq'::regclass) 表达式,PostgreSQL 理解缺省表达式依赖于序列 my_seq ;系统将不允许在删除缺省的表达式之前删除该序列。
系统使用的另外一个标识符类型是事务(缩写 xact)标识符 xid 。它是系统字段 xmin 和 xmax 的数据类型。事务标识符是 32 位的量。
系统需要的第三种标识符类型是命令标识符 cid。它是系统字段 cmin 和 cmax 的数据类型。命令标识符也是 32 位的量。
系统使用的最后一个标识符类型是行标识符 tid 。它是系统表字段 ctid 的数据类型。行 ID 是一对数值(块号,块内的行索引),它标识该行在其所在表内的物理位置。
PostgreSQL类型系统包含一系列特殊用途的条目,它们按照类别来说叫做伪类型。伪类型不能作为字段的数据类型,但是它可以用于声明一个函数的参数或者结果类型。伪类型在一个函数不只是简单地接受并返回某种 SQL 数据类型的情况下很有用。表8-20列出了所有的伪类型。
表. 伪类型
名字 | 描述 |
---|---|
any | 表示一个函数接受任何输入数据类型 |
anyarray | 表示一个函数接受任意数组数据类型(参阅节33.2.5) |
anyelement | 表示一个函数接受任何数据类型(参阅节33.2.5) |
cstring | 表示一个函数接受或者返回一个空结尾的 C 字符串 |
internal | 表示一个函数接受或者返回一种服务器内部的数据类型 |
language_handler | 一个过程语言调用处理器声明为返回 language_handler |
record | 标识一个函数返回一个未声明的行类型 |
trigger | 一个触发器函数声明为返回 trigger |
void | 表示一个函数不返回数值 |
opaque | 一个已经过时的类型,以前用于所有上面这些用途 |
用 C 编写的函数(不管是内置的还是动态装载的)都可以声明为接受或者返回这样的伪数据类型。在把伪类型用做函数参数类型的时候,保证函数行为正常就是函数作者的任务了。
用过程语言编写的函数只能根据它们的实现语言是否可以使用伪类型而使用它。目前,过程语言都不允许使用伪类型作为参数类型,并且只允许使用 void 和 record 作为结果类型(如果函数用做触发器,那么加上 trigger)。一些多态的函数还支持使用 anyarray 和 anyelement 类型。
伪类型 internal 用于声明那种只能在数据库系统内部调用的函数,它们不能直接在 SQL 查询里调用。如果函数至少有一个 internal 类型的参数,那么我们就不能从 SQL 里调用它。为了保留这个限制的类型安全,我们一定要遵循这样的编码规则:不要创建任何声明为返回 internal 的函数,除非它至少有一个 internal 参数。
XML(可扩展标记语言)支持不是一种单一的能力,而是需要数据库系统支持一系列的特性,包括存储、导入/导出、验证、索引、修改、搜索、转换、XML 到 SQL 映射,PostgreSQL 只支持其中的一部分,未来的版本将提供更多的 XML 支持。关于在数据库中使用 XML 的概览,请查看 http://www.rpbourret.com/xml/XMLAndDatabases.htm 。
存储
PostgreSQL 没有专门的 XML 数据类型。用户应当将 XML 文档存储在普通的 TEXT 字段中。如果需要将文档分割成各个组成部分以便于将每个元素分别存储,必须使用中间层解决方案。但是一旦完成,数据将变成关系并且必须这样处理。
导入/导出
必须使用外部工具才能将 XML 映射为关系表。一个导出 XML 的简单途径是以 HTML 模式使用 psql(\pset format html),然后使用外部工具将 XHTML 输出转换为 XML 。
验证
/contrib/xml2 中有一个 xml_is_well_formed()
函数可以在 CHECK 约束中使用以确保字段中保存的内容是格式良好的 XML ,但是它不支持根据特定的 XML 模式进行校验。可以使用具有 XML 功能的服务端语言根据特定的 XML 模式进行校验。
索引
/contrib/xml2 中的函数可以用于表示特定 XML 字段的索引。要对 XML 文档全文进行索引,可以使用全文索引工具 /contrib/tsearch2 。由于 Tsearch2 索引对 XML 一无所知,所以必须在查询中添加额外的 /contrib/xml2 检查。
修改
如果 UPDATE 没有修改 XML 字段,那么 XML 数据在新旧行之间是共享的。但是,如果 UPDATE 修改了 XML 字段,那么必须在内部创建一个完整的已修改的该 XML 字段副本。
搜索
可以使用 /contrib/xml2 实现 XPath 搜索。它用于处理 XML 文本文档并根据被请求的查询返回结果。
转换
/contrib/xml2 支持 XSLT (Extensible Stylesheet Language Transformation)。
XML 到 SQL 映射
在 XML 数据和关系结构之间相互转换。PostgreSQL 没有这种映射功能的内部支持,必须依靠外部工具。
不支持的特性
不支持的特性包括:XQuery 、SQL/XML 语法(ISO/IEC 9075-14)、专用于 XML 存储的 XML 数据类型。