PostgreSQL.png 数据库连接工具 选择

工具1 datagrip :https://www.jetbrains.com/zh-cn/datagrip/
工具2 navicat

数据库配置文件

Linux 安装 Postgres

Linux 配置文件存放路径

/var/lib/pgsql/14/data
image.png

数据库安装完成 修改 pg_hba.conf

配置完成后 重启 服务生效,此配置设置 所有ip地址都可以访问数据库,不配置此选项,需要在此配置文件内设置固定的 访问IP地址

  1. host all all 0.0.0.0/0 md5

修改pg_config文件

将文件中的 对应的键替换成 如下的值,修改此参数 是为了 查询最耗时SQL用的

  1. shared_preload_libraries = 'pg_stat_statements'
  2. pg_stat_statements.max = 1000
  3. pg_stat_statements.track = all

如果出现 获取当前时间不正确 需要配置时区

配置 postgresql.conf 找到 timezone

  1. timezone = 'Asia/Shanghai'

安装常用扩展包

postgresql 有很多官方和第三方开源的扩展包,有很强大辅助功能,我们要解决复杂的处理,先看看网上有没有人已经提供了扩展包 无需我们自己实现

  1. --此扩展包 固定列的交叉表转换函数
  2. create extension tablefunc;
  3. --可查询 最好是视图的扩展包
  4. create extension pg_stat_statements;

开发工具 1,连接 数据库 获取时间不正确,设置 工具的 timezone ,Asia/Shanghai
image.png


系统数据查询

查询系统配置

  1. select * from pg_settings

查询系统配置文件路径

  1. select * from pg_file_settings where name ='TimeZone';

查询最耗时SQL表

  1. select * from pg_stat_statements;

查询锁表

  1. --bs_account 你要查看锁表的 表名
  2. --查询锁表语句
  3. select * from pg_stat_activity
  4. where pid in ( select pid from pg_locks l join pg_class t on l.relation = t.oid and t.relkind = 'r' where t.relname = 'bs_account');
  5. --解锁
  6. select pg_cancel_backend(pid) from pg_locks l
  7. join pg_class t on l.relation = t.oid and t.relkind = 'r'
  8. where t.relname = 'bs_account'

查询所有表及字段

  1. SELECT info.table_name AS "表名",
  2. CASE
  3. WHEN const.constraint_name IS NULL THEN ''::text
  4. ELSE 'p'::text
  5. END AS pk,
  6. info.column_name AS "列名",
  7. info.udt_name AS "数据类型",
  8. COALESCE(info.character_maximum_length::integer, 9999) AS "数据长度",
  9. CASE
  10. WHEN info.is_nullable::text = 'YES'::text THEN '是'::text
  11. ELSE '否'::text
  12. END AS "允许为空"
  13. FROM information_schema.columns info
  14. LEFT JOIN information_schema.tables tables ON tables.table_name::name = info.table_name::name
  15. LEFT JOIN information_schema.constraint_column_usage const
  16. ON const.table_name::name = tables.table_name::name AND const.column_name::name = info.column_name::name
  17. WHERE tables.table_type::text = 'BASE TABLE'::text
  18. ORDER BY info.table_name;

存储过程和函数

postgresql 在 13 以前没有存储过程,所有统一使用函数,13的以后的存储过程 和 函数基本一样
所以我们项目还是 统一将 存储过程也创建成函数,plv8 和 python 用存储过程

oracle 和 pg 语法区别

查询虚拟表

  1. --Oracle
  2. select 1 from dual ;
  3. -- postgresql
  4. select 1 ;

数据类型

oracle类型 pg数据类型 备注
varchar2,nvarchar2 varchar
clob text text 和 varchar没有存储限制上没有区别,建议使用 varchar 不给长度
char(1) boolean,bool
date date 或 timestamp date:值存储到日期
timestamp: 存储到时间
number numeric
integer integer

数据类型一致性

oracle中可能出现 同样是字符,出现字符集不相等的时候,通常我们会将2边的 字段都用 to_char 转换类型,
pgsql 就不要这么操作,如果在字符上使用to_char,pg将会出现错误。to_char 在pg中也是用来做日期格式化的

函数不同

oracle函数 postgresql函数 备注
instr position
substr substr pg的下标从1开始
nvl coalesce 特别注意oracle nvl 的返回值是任意类型,但是pg的返回值,必须第一个参数的字段类型一致,
我自己封装了一个 nvl 函数仅限于字符类型和数字类型使用,正常情况下 不需要用我写的
sysdate now()
listagg string_agg

代码块 begin end;

oracle 的代码块是 declare begin end;
postgresql 的代码块如下

  1. do
  2. $do$
  3. declare
  4. begin
  5. end;
  6. $do$;

字符串拼接

在 Oracle 拼接字符串,遇到字符串单带单引号的,拼接起来需要 将2两边的单引号 对上,异常麻烦 在 postgresql 中 提供了 符号,被 包裹的 单引号 只是作为字符使用

  1. --其他数据库拼接单引号。此SQL的单引号异常复杂
  2. select 'to_char(now(),''' || 'yyyy-dd-mm' || ''')' ;
  3. --postgresql 的拼接单引号,postgresql 也支持 上面的SQL语句
  4. select $$to_char(now(),'$$ ||$$yyyy-dd-mm$$|| $$')$$;

文本输出语句

  1. --% 固定写法 str 是要输出的变量
  2. raise notice '%',str;

常用函数

所有函数查看 官方文档 :

返回非空值 coalesce coalesce(A,B) 当参数A为Null返回参数B 当A
nullif nullif(A,B) 当A和B相等返回A
全球唯一码 gen_random_uuid 生成全球唯一码
替换指定位置字符 overlay select overlay(A placing B from 开始下标 for 结束下标); 将 A 字符串 开始位置 到 结束位置 替换成B字符串
替换指定字符 replace replace(‘abcdefabcdef’, ‘cd’, ‘XX’) 将字符串中的cd 替换成 xx
返回字符长度 length length(A)
分割字符串 split_part select split_part(A, B, 下标) ; 将A字符 按B字符进行分割,返回执行下标的字符串
正则表达分割字符串 regexp_split_to_table select regexp_split_to_table(A,B) 将A字符 按B字符进行分割,返回表 ,此函数 B 参数支持正则表达式
regexp_split_to_array 这个函数返回数组,
返回行号 row_number rownumber() over() 返回行号
提取日期 date_part date_part(A,B)
例子: date_part(‘hour’,now())
A 参数是要提取的 类型
有:year month,day hour 等
B 要提取的日期
截取日期 date_trunc date_trunc(A,B)
例子: date_part(‘hour’,now())
hour以后的日期,将被替换成默认值
提取interva日期 extract extract(type from interval) type 参数是要提取的类型
有:year month,day hour 等,
interval 要提取的 interval 类型的时间值

自定义函数模板

调用函数

  1. select return_basic() ; --返回字段
  2. select * from return_basic() ; --返回表

返回基本类型

  1. create function return_basic()
  2. returns integer
  3. as
  4. $function$
  5. declare
  6. out integer;
  7. begin
  8. out := 1;
  9. return out;
  10. end;
  11. $function$ language plpgsql;

返回自定义类型


  1. --创建自定义类型
  2. create type type_person as
  3. (
  4. iid varchar(100),
  5. cname varchar(200)
  6. );
  1. --模板1
  2. create function return_type() returns
  3. SETOF type_person
  4. language plpgsql
  5. as
  6. $function$
  7. declare
  8. row type_person;
  9. cur cursor for select iid,cname from as_person;
  10. begin
  11. for row in cur loop
  12. return next row;
  13. end loop;
  14. return ;
  15. end;
  16. $function$;
  17. --模板1-1
  18. create function return_type() returns
  19. SETOF type_person
  20. language plpgsql
  21. as
  22. $function$
  23. declare
  24. row type_person;
  25. cur cursor for select iid,cname from as_person;
  26. begin
  27. for row in cur loop
  28. if row.iid <> '1' then
  29. return query execute 'select '||$$''$$ || '::varchar(100) as iid,' ||$$'测试'::varchar(200) as cname $$ ;
  30. row.cname := 'xx';
  31. return next row;
  32. else
  33. return query execute 'select '||$$'1'$$ || '::varchar(100) as iid,' ||$$'测试'::varchar(200) as cname $$ ;
  34. end if;
  35. end loop;
  36. return ;
  37. end
  38. $function$;
  1. --模板2 动态SQL
  2. create function return_type()
  3. returns SETOF 你的自定义类型
  4. as
  5. $function$
  6. declare
  7. csql text;
  8. begin
  9. csql := '你的SQL';
  10. return query execute csql;
  11. return ;
  12. end;
  13. $function$ language plpgsql;

返回游标

  1. create function return_cursor(OUT curs refcursor )
  2. returns refcursor
  3. as
  4. $$
  5. declare
  6. begin
  7. open curs for execute '你的SQL';
  8. end
  9. $$ language plpgsql;

小技巧

日期加减

postgresql 有一个数据类型 interval 时间间隔,利用这个 类型可以轻松进行 日期的操作

  1. select now() - '1 year'::interval ;
  2. select now() - '1 day 23 hour'::interval;

类型转换

可以使用 :: 符号进行类型转换

  1. --函数显示转换
  2. select cast(now() as varchar(10));
  3. --::快捷转换 建议用这个
  4. select now()::varchar(10);

返回询前N行

row_number() 函数

返回行号

  1. select * from (
  2. select row_number() over () as row_num,cname from aa_data
  3. ) as a where row_num <= 5

limit 关键字

提取前N行数据

  1. select cname from aa_data limit 5;

分页

分页功能 用到 limit offset 2个关键字组合使用 limit 已做过讲解,offset 作用是从第几行开始返回数据

  1. do
  2. $$
  3. declare
  4. page_count int; --每页行数
  5. current_page int; --当前页
  6. page_star_row int; --当前页起始行
  7. begin
  8. page_star_row := page_count * current_page;
  9. select cname from aa_data limit page_count offset page_star_row;
  10. end;
  11. $$;

实现分组小计

通过 grouping sets 或者 rollup

grouping sets

  1. -- 特别注意空的()意思是,将所有行进行汇总,最后一行总合计
  2. --(科目)是一个小分组条件,(科目,人员)又是一个分组条件,以此类推
  3. with tem as (
  4. select '科目1' as "科目", '张三' as "人员", 10 as "分数"
  5. union all
  6. select '科目2' as "科目", '张三' as "人员", 40 as "分数"
  7. union all
  8. select '科目3' as "科目", '张三' as "人员", 20 as "分数"
  9. union all
  10. select '科目1' as "科目", '李四' as "人员", 20 as "分数"
  11. union all
  12. select '科目2' as "科目", '李四' as "人员", 30 as "分数"
  13. union all
  14. select '科目3' as "科目", '李四' as "人员", 50 as "分数"
  15. )
  16. select "科目", "人员", sum("分数") from tem
  17. group by grouping sets (("科目"),("科目","人员"),())
  18. order by 科目;

效果:
image.png
rollup 效果同上是一样的

  1. with tem as (
  2. select '科目1' as "科目", '张三' as "人员", 10 as "分数"
  3. union all
  4. select '科目2' as "科目", '张三' as "人员", 40 as "分数"
  5. union all
  6. select '科目3' as "科目", '张三' as "人员", 20 as "分数"
  7. union all
  8. select '科目1' as "科目", '李四' as "人员", 20 as "分数"
  9. union all
  10. select '科目2' as "科目", '李四' as "人员", 30 as "分数"
  11. union all
  12. select '科目3' as "科目", '李四' as "人员", 50 as "分数"
  13. )
  14. select "科目", "人员", sum("分数") from tem
  15. group by rollup (("科目"),("科目","人员"))
  16. order by 科目;

Filter 代替 case when then

filter 在聚合统计的时候 ,在数据量大的情况下,比case的效率高 filter 和 函数组合使用

  1. -- 等价下面语句,filter 效率会更快,语法也更好理解
  2. -- sum(case temp.class when '二班' then temp.score else null end ) as "二班分数",
  3. with temp as (
  4. select '一班' as class , 10 score
  5. union all
  6. select '一班' as class , 20 score
  7. union all
  8. select '二班' as class , 3 score
  9. union all
  10. select '二班' as class , 20 score
  11. ) select class,
  12. sum(score) filter ( where class = '二班') as "二班分数",
  13. sum(score) filter ( where class = '一班') as "一班分数" from temp
  14. group by class;

递归截取字符串

已逗号分隔 截取字符串,返回结果集

  1. WITH RECURSIVE t AS (
  2. SELECT 1 as count, substring('a,b,c' from '[^,]+') as ss FROM dual
  3. union all
  4. SELECT t.count + 1 as count, substring( substr('a,b,c',position(ss in 'a,b,c') + 1,length('a,b,c')) from '[^,]+') as ss FROM t
  5. where t.count < ( length('a,b,c') - length(replace('a,b,c',',',''))+1 )
  6. )
  7. SELECT ss FROM t;

获取动态SQL的列名

实现思路:将结果集转换成JSON,JSON的键值就是SQL中查询的列名

  1. do
  2. $$
  3. declare
  4. col_name varchar;
  5. user_sql varchar;
  6. exec_sql varchar;
  7. begin
  8. --用户的SQL
  9. user_sql := 'select 1 as id,2 as name';
  10. --获取JSON的键值
  11. exec_sql := 'select json_object_keys(a.js) from (
  12. select row_to_json(t.*) as js from ('||user_sql||' ) as t limit 1
  13. ) as a';
  14. --循环查询列表名
  15. for col_name in execute exec_sql loop
  16. raise NOTICE '%',col_name;
  17. end loop;
  18. end;
  19. $$;

数据库备份及还原

全库备份

cmd 进入到postgresql安装目录 bin文件夹 执行 如下语句 pg_dump —hellp 可以查看更多备份 参数 -v 这个参数是,在控制台总输出 备份的明细

  1. 7全库备份 不建议使用,这将备份 系统库 和 系统用户等信息
  1. 1、--全库备份
  2. pg_dumpall --host=127.0.0.1 --username=cqpt --port=5432 -v --file=D:\PostgreSQL\backup.sql

备份指定库

  1. 建议使用 指定备份数据库,注意 2 备份出来的 没有用户和对应的数据库,还原时需要手动创建好用户和,同名的空数据库,在进行还原
  1. 2、--备份指定 数据库
  2. pg_dump --host 127.0.0.1 --port 5432 --username cqpt --file D:\Temp\backup --format p -v cqpt_database

还原数据库

还原数据库 cqpt_database 最后一个参数 还原到 cqpt_database这个数据库

  1. --还原数据 到指定 用户 指定 数据库
  2. psql --username cqpt --set ON_ERROR_STOP=on --file D:\PostgreSQL\backup cqpt_database