PostgreSQL知识点总结

一、基本使用

1. 交互式终端psql

  • 连接至数据库: psql -h -p <端口号>
  • \d:查看所有表
  • \d + 表名:查看表结构
  • \timing:显示SQL语句执行时间

2. 表空间的使用

如果需要把不同的表放在不同的存储介质或不同的文件系统下,可以使用表空间

`CREATE TABLESPACE tablespace_name LOCATION 'directory'
[WITH (tablespace_option = value)]`

在创建数据库、表时可以指定表空间,以便将对象存储在特定的表空间上

表空间参数 tablespace_option :如果在比 I/O子系统更快或更慢的磁盘上存储表空间,则配置项会变得很有意义

  • seq_page_cost:在磁盘上顺序扫描获取一个页面的代价,默认1.0
  • random_page_cost:在磁盘上随机扫描获取一个页面的代价,默认4.0
  • effective_io_concurrency:可以执行并发异步磁盘 I/O 的数量

3. 创建数据库

CREATE DATABASE name

[ENCODING[=]encoding]
[TABLESPACE[=]tablespace_name]
[CONNECTION LIMIT[=]connlimit]

ENCODING:编码
TABLESPACE:表空间

4. 数据库表约束

创建数据库:

CREATE [[GLOBAL|LOCAL]{TEMPORARY|TEMP}|UNLOGGED] TABLE
[IF NOT EXIST] table_name([
{ column_name data_type [COLLATE collation][column_constraint
[...]]
|table_constraint
|LIKE source_table [like_option...]}
])
[INHERITS(parent_table[,...])]
[PARTITION BY{RANGE|LIST}({column_name|(expression)}
[COLLATE collation][opclass][,...])]
[WITH(storage_parameter[=value][,...])|WITH OIDS|WITHOUT OIDS]
[ON COMMIT{PERSERVE ROWS|DELETE ROWS|DROP}]
[TABLESPACE tablespace_name]

字段约束:

  • 非空约束: NOT NULL
  • 唯一约束: UNIQUE
  • 主键约束: PRIMARY KEY
  • 默认约束: DEFAULT
  • 外键约束deptid int not null REFERENCES department(deptid)
  • Check约束:createtime timestamp CHECK(createtime > ‘1970-01-01- 00:00:00’)

表约束:

  • 外键约束:FOREIGN KEY (b,c) REFERENCE tb_test01 (b1,c1)
  • Check约束:CHECK(createtime < modifytime)

删除数据库表:

删除表时如果该表被其他表引用(外键),直接删除会报错,需添加 CASCADE 连带删除子表的外键约束 : DROP TABLE employee CASCADE

二、数据类型和运算符

1. 数据类型

  • 整型:smallint、integer、bigint

  • 任意精度:numeric、decimal
    decimal(6,2):表示总共位数6位,小数部分位数2位。如果插入的数字小数位数超过2,则自动进行四舍五入

  • 浮点:real 6位精度、 double precision 15位精度、float(n) 指定精度。浮点类型会造成精度丢失,需要准确计算应该使用numeric类型。浮点类型特殊值(Infinity、-Infinity、NaN)

  • 序列:smallserial、serial、bigserial 可用作自增主键

  • 货币:money 可以接受 ‘¥10000.00’货币格式,货币符号由数据库 lc_monetary 参数决定

  • 字符串:char(n)、varchar(n)、text 。n表示最多存储n个字符,text无长度限制

  • 二进制:bytea

  • 日期时间

    • timestamp [with time zone]:8位日期时间
    • date:4位日期
    • time[with time zone]:时间
    • interval [fields]:时间间隔

    系统内部所有与时区相关的日期时间存的都是UTC时间,可以使用timezone参数指定本地时间

  • 布尔:true、false、unknown

  • 位串:bit(n) 、 bit varying(n)。n表示存储位的长度

  • 枚举:CREATE TYPE week AS ENUM (‘Mon’、‘Tue’、‘Wed’)

  • 几何类型:point、line、lseg、box、path、polygon、cricle

  • JSON类型

    • json:存储文本的精确复制。处理函数执行时需要解析
    • jsonb:存储为分解好的二进制格式,函数处理不需要解析效率高,且支持索引(建议使用)
      SELECT ‘{“bar”:bar, "balance:7.7"}’::jsonb;
  • 范围类型:int4range、int8range、numrange、tsrange、tstzrange、daterange
    INSERT INTO range1 VALUES('[2017-01-01 07:00:00,2017-01-01 08:00:00]')

  • 数组类型

    • 定义数组类型 pay_by_month float[] 或 pay_by_month float array[]
    • 插入数组 INSERT INTO payment VALUES ‘{10000.23, 12030.2, 10000}’
      INSERT INTO payment VALUES ARRAY[10000.23, 12030.2, 10000]
    • 查询数组时要注意PostgreSQL数组元素的下标从1开始
    • ANY、ALL运算:SELECT * FROM payment WHERE 10000 = ANY(pay_by_month);

2. 运算符

  • 字符串运算符
    • ||:字符串拼接 SELECT a||b FROM test_char;
    • LIKE:字符串匹配
    • SIMILAR TO:字符串匹配,使用正则表达式匹配
  • 时间运算符:+、-、*
    • date’2001-09-28’ + inteval ‘1 hour’ -> timestamp ‘2001-09-28 01:00:00’
    • date’2001-09-28’ + time ‘03:00’ -> timestamp ‘2001-09-28 03:00:00’
  • 布尔运算符: is [not] distinct from 、 is [not] null
  • 几何运算符:平移、旋转、缩放…

三、表查询

1. 普通查询

[WITH [RECURSIVE] with_query [,...]]         //大型查询中使用辅助语句,常用于复杂查询和递归查询
SELECT [ALL|DISTINCT[ON(expression [,...])]]
[*|expression[[AS] output_name][,...]]
[FROM from_item [,...]]
[WHERE condition]
[GROUP BY grouping_element[,...]]
[HAVING condition[,...]]
[WINDOW window_name AS(window_definition)[,...]]
[{UNION|INTERSECT|EXCEPT}[ALL|DISTINCT]select]
[ORDER BY expression [ASC|DESC][NULLS {FIRST|LAST}][,...]]
[LIMIT{count|ALL}]
[OFFSET start[ROW|ROWS]]
[FETCH{FIRST|NEXT}[COUNT]{ROW|ROWS} ONLY]
[FOR{UPDATE|NO KEY UPDATE|SHARE|KEY SHARE}
[OF table_name[,...]][NOWAIT|SKIP LOCKED][...]]
  • DISTINCT ON(expression[,…]):根据表达式中的字段进行分组,分组后仅返回每组的第一行(如果不指定ORDER BY,返回的第一条记录是不确定的)
// 根据 insuranceid和sex字段进行分组,返回每组sex最小的一行数据(包含 insuranceid, empid, insurant 字段)
SELECT DISTINCT ON (insuranceid, sex) insuranceid, empid, insurant FROM emp_order_insurance ORDER BY sex;
  • ORDER BY:根据字段排序
    • NULLS {FIRST|LAST}:NULL值排序后放前面还是后面
  • LIMIT:分页

翻页不能解决因为结果集过大导致的性能问题,因为翻页也是先排序再分页的被OFFSET子句忽略的行仍然需要在服务器内部计算

解决方案:

  1. 根据业务实际需求,看能否替换为下一页,上一页的功能,特别在ios, android端,完全的分页是不常见的。 这里可以把limit, offset,替换为>辅助索引(即搜索条件)id的方式。该id再调用时,需要返回给前端。
  2. 利用索引覆盖
    select column_name from table_name where id in (select id from table where second_index = xxx limit 10 offset 10000) 先在子查询中,查找数据对应的数据库唯一id值,因为主键在辅助索引上就有,所以不用回归到聚簇索引的磁盘去拉取。再通过这些已经被limit出来的10个主键id,去查询聚簇索引。这样只会十次随机io。 在业务确实需要用分页的情况下,使用该方案可以大幅度提高性能。通常能满足性能要求。
  • EXISTS/NOT EXISTS:如果子查询至少返回一行,则返回true(子查询只运行到能判断它是否可以返回至少一行为止,而不用等到全部结束。结果只取决于是否返回行,而不取决于这些行的内容,所以子查询的输出列表通常是无关紧要的)
// 购买了保险号为1001的所有员工信息
SELECT empid,empname
FROM employee a
WHERE EXISTS(SELECT 1 FROM emp_order_insurance b WHERE a.empid = b.empid and b.insuranceid = 1001);

EXISTS里表用于和外表连接的字段一定要建立索引。因为EXISTS子查询需要的工作内存较少,优化器匹配到1条索引后即刻返回,所以会选择使用索引,性能非常好!

IN 和 EXISTS的比较:
使用in ,sql语句是先执行子查询,也就是先查询b表,在查a表,
而使用exists是先查主表a ,再查字表b;
对于主表数据较多时,我们使用in速度比exist更快,反之,从表b较大时,使用exist速度更快

  • ANY/ALLWHERE empid > ANY(SELECT empid FROM emp_order_insurance);

2. 模糊匹配查询

  • LIKE/ILIKE:ILIKE会忽略大小写
  • SIMILAR TO:字符串匹配,使用正则表达式匹配

模糊查询性能提升:

  • 带前缀的模糊索引,如’南%',可以使用B-tree索引进行优化
  • 带后缀的模糊索引,如’%南’,可以使用reverse()反转函数建立函数索引优化
  • 不带前缀和后缀的模糊查询,可以使用 pg_trgm的 gin 索引进行优化

3. 查询运算

  • COUNT
    • COUNT(*):不忽略NULL值
    • COUNT(字段名):忽略NULL值
    • COUNT(DISTINCT column) :计算非重复结果的数目
  • 分组集
//分别按照deptid、sex和全部进行分组
SELECT deptid,sex,COUNT(*)
FROM employee
GROUP BY GROUPING SETS (deptid, sex, ());

ROLLUP 和 CUBE可以简化分组集的写法

  • 集合运算UNION [ALL]、 INTERSECT[ALL]、EXCEPT[ALL] ,加ALL则不会过滤重复的行

4. 窗口函数

窗口函数对表中一系列与当前行有某种关联的行执行跨行计算,这与聚集函数类似。但窗口函数不会使多行聚合成一个单独的输出行,这与聚集函数不同,在查询中输出的每一行仍然保持独立

function_name ([expression[,expression...]]) OVER (window_definition)

window_definition的格式:

PARTITION BY expression[,...]]
[ORDER BY expression[ASC|DESC]]

PARTITION BY子句将查询的行进行分组,窗口函数会对各个分组作独立的处理

// 查询各部门的员工在所属部门中的工资排名
SELECT empid,salary, deptid, row_number()over(partition by deptid order by salary desc)rnum
FROM employee
ORDEr BY deptid,rnum

empid|salary|deptid|rnum
10001|100000|100001|1
10002|80000 |100001|2
10003|60000 |100001|3
10004|100000|100002|1
10005|70000 |100002|2

常用的窗口函数:

  • row_number():行号
  • rank()、dense_rank():带间隙,不带间隙的当前行排名
  • first_value()、last_value():该分组的第一个值和最后一个值
  • nth_value(value, integer):该分组第几行的值

5. 高级操作

  • 归并数据:UPSERT操作,用于解决数据插入过程中发生的冲突
insert into department(deptid, deptname)
values(1001,'xx')
on conflict(deptid)
do update set createtime = now()
[do nothing]
  • 批量插入
    • INSERT INTO … SELECT …
    • BEGIN 多条insert END:一个事务里插入多条SQL语句
    • COPY table_name FROM ‘filename’:从外部文件导入数据,性能最好
  • 关联更新:通过与其他表关联实现批量更新多条记录
//把a的department更新为b的department
UPDATE department a SET deptname = b.deptnam FROM
upd_dept b WHERE a.deptid = b.deptid;
  • 关联删除
DELETE FROM department a USING test b WHERE  a.deptid = b.deptid;

你可能感兴趣的:(postgresql,oracle,数据库)