|
|
|
|
|
## 一、常用命令及SQL
|
|
|
|
|
|
+ 系统性能指标
|
|
|
```sql
|
|
|
1.当前连接数
|
|
|
select count(*) from pg_stat_activity where query <>'COMMIT';
|
|
|
2.查看所有表
|
|
|
select * from pg_tables where schemaname='public';
|
|
|
3.查看表字段
|
|
|
select * from information_schema.columns where table_schema='public' and table_name='device';
|
|
|
4.查看表大小(包括索引)
|
|
|
select pg_size_pretty(pg_total_relation_size('device'))
|
|
|
5.使用表扫描最多的表
|
|
|
select * from pg_stat_user_tables where n_live_tup > 100000 and seq_scan > 0 order by seq_tup_read desc limit 10;
|
|
|
6.查看所有表大小
|
|
|
select relname, pg_size_pretty(pg_relation_size(relid)) as size from pg_stat_user_tables;
|
|
|
7.查看死锁
|
|
|
select * from pg_locks where granted='f'
|
|
|
8.结束死锁进程
|
|
|
select pg_terminate_backend(pid) from (select pid from pg_locks where granted='f')
|
|
|
9.查看表结构
|
|
|
SELECT
|
|
|
a.attname as column_name,
|
|
|
format_type(a.atttypid,a.atttypmod) as data_type,
|
|
|
col_description(a.attrelid,a.attnum) as comment
|
|
|
FROM pg_class as c,pg_attribute as a where a.attrelid = c.oid and a.attnum>0 and c.relname = 'Edge_0101';
|
|
|
|
|
|
-- 或者
|
|
|
SELECT a.attnum, a.attname AS field, t.typname AS type, a.attlen AS length, a.atttypmod AS lengthvar
|
|
|
, a.attnotnull AS notnull, b.description AS comment
|
|
|
FROM pg_class c, pg_attribute a
|
|
|
LEFT JOIN pg_description b
|
|
|
ON a.attrelid = b.objoid
|
|
|
AND a.attnum = b.objsubid, pg_type t
|
|
|
WHERE c.relname = 'pg_replication_slots'
|
|
|
AND a.attnum > 0
|
|
|
AND a.attrelid = c.oid
|
|
|
AND a.atttypid = t.oid
|
|
|
ORDER BY a.attnum;
|
|
|
|
|
|
10.查看所有进程
|
|
|
SELECT * FROM pg_stat_activity WHERE datname='数据库名'
|
|
|
11.根据pid结束进程
|
|
|
select pg_cancel_backend(pid)
|
|
|
```
|
|
|
## 二、函数
|
|
|
|
|
|
### 1.字符串
|
|
|
+ 字符串拼接
|
|
|
```sql
|
|
|
'Post' || 'greSQL' = PostgreSQL
|
|
|
SELECT CONCAT ('CONCAT',' ', 'function')
|
|
|
```
|
|
|
+ 转大、小写
|
|
|
```sql
|
|
|
select upper('tom')
|
|
|
select lower('ABC')
|
|
|
```
|
|
|
+ 子串在字符串中的位置,可以替代like
|
|
|
```sql
|
|
|
select position('om' in 'Thomas')
|
|
|
```
|
|
|
+ 字符串截取
|
|
|
```sql
|
|
|
# 从下标0开始截取4个字符
|
|
|
select substr('month',0,4)
|
|
|
```
|
|
|
+ 去空格(正则)
|
|
|
```sql
|
|
|
select regexp_replace('杂 项 制 品', E'\\s+', '', 'g')
|
|
|
```
|
|
|
### 2.日期时间
|
|
|
+ 获取当前时间绝对秒
|
|
|
```sql
|
|
|
select floor(extract(epoch from now()))
|
|
|
```
|
|
|
+ 日期转字符串
|
|
|
```sql
|
|
|
select to_char(now(),'yyyy-MM-dd HH24:mi:ss')
|
|
|
```
|
|
|
+ 字符串转日期
|
|
|
```sql
|
|
|
select to_date('1992-04-12','yyyy-MM-dd')
|
|
|
```
|
|
|
+ 当前时间、时间戳
|
|
|
```sql
|
|
|
select current_timestamp,now();
|
|
|
```
|
|
|
+ 字符串转时间戳
|
|
|
```sql
|
|
|
select to_timestamp('2024-03-21 14:03:14','yyyy-MM-dd HH24:mi:ss')
|
|
|
```
|
|
|
+ 获取当前日期
|
|
|
```sql
|
|
|
select current_date;
|
|
|
```
|
|
|
+ 获取6个月之前时间
|
|
|
```sql
|
|
|
select to_char((select now() - interval '6 month'),'yyyy-MM-dd hh24:mi:ss')
|
|
|
```
|
|
|
+ 计算时间差
|
|
|
```sql
|
|
|
1.天数(day可以换成month,year)
|
|
|
select date_part('day','2019-01-01'::date-'2011-10-02'::date)
|
|
|
|
|
|
2.秒
|
|
|
(extract('epoch' from a.pass_time::timestamp) - extract('epoch' from b.snap_time::timestamp))
|
|
|
```
|
|
|
|
|
|
### 3.窗口函数
|
|
|
|
|
|
窗口函数不是将一组数据汇总为单个结果;而是针对每一行数据,基于和它相关的一组数据计算出一个结果
|
|
|
![[Pasted image 20240501095744.png]]
|
|
|
|
|
|
窗口函数的定义如下:
|
|
|
```text
|
|
|
window_function ( expression, ... ) OVER (
|
|
|
PARTITION BY ...
|
|
|
ORDER BY ...
|
|
|
frame_clause
|
|
|
)
|
|
|
```
|
|
|
|
|
|
| 函数类型 | 名称 | 用途 |
|
|
|
| ---- | ------------ | ---------------------------------------------------- |
|
|
|
| 聚合函数 | AVG | 平均值 |
|
|
|
| 聚合函数 | COUNT | 行数 |
|
|
|
| 聚合函数 | MAX | 最大值 |
|
|
|
| 聚合函数 | MIN | 最小值 |
|
|
|
| 聚合函数 | SUM | 总数 |
|
|
|
| 排名函数 | ROW_NUMBER | 为分区中的每行数据分配一个序列号,序列号从 1 开始分配 |
|
|
|
| 排名函数 | RANK | 计算每行数据在其分区中的名次;如果存在名次相同的数据,后续的排名将会产生跳跃 |
|
|
|
| 排名函数 | DENSE_RANK | 计算每行数据在其分区中的名次;即使存在名次相同的数据,后续的排名也是连续的值 |
|
|
|
| 排名函数 | PERCENT_RANK | 以百分比的形式显示每行数据在其分区中的名次;如果存在名次相同的数据,后续的排名将会产生跳跃 |
|
|
|
| 排名函数 | CUME_DIST | 计算每行数据在其分区内的累积分布,也就是该行数据及其之前的数据的比率;取值范围大于 0 并且小于等于 1 |
|
|
|
| 排名函数 | NTILE | 将分区内的数据分为 N 等份,为每行数据计算其所在的位置 |
|
|
|
| 取值函数 | FIRST_VALUE | 返回窗口内第一行的数据 |
|
|
|
| 取值函数 | LAST_VALUE | 返回窗口内最后一行的数据 |
|
|
|
| 取值函数 | NTH_VALUE | 返回窗口内第 N 行的数据 |
|
|
|
| 取值函数 | LAG | 返回分区中当前行之前的第 N 行的数据 |
|
|
|
| 取值函数 | LEAD | 返回分区中当前行之后第 N 行的数据 |
|
|
|
|
|
|
#### 3.1 排名窗口函数
|
|
|
以下示例按照部门为单位,计算员工的月薪排名:
|
|
|
|
|
|
```sql
|
|
|
SELECT d.department_name "部门名称", concat(e.first_name, ',' , e.last_name) "姓名", e.salary "月薪",
|
|
|
ROW_NUMBER() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS "row_number",
|
|
|
RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS "rank",
|
|
|
DENSE_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS "dense_rank",
|
|
|
PERCENT_RANK() OVER (PARTITION BY e.department_id ORDER BY e.salary DESC) AS "percent_rank"
|
|
|
FROM employees e
|
|
|
JOIN departments d ON (e.department_id = d.department_id)
|
|
|
WHERE d.department_name in ('IT', 'Purchasing')
|
|
|
ORDER BY 1, 4;
|
|
|
```
|
|
|
|
|
|
![[Pasted image 20240501102554.png]]
|
|
|
ROW_NUMBER 函数为每个员工分配了一个连续的数字编号,可以看作是一种排名。IT 部门的“Valli,Pataballa”和“David,Austin”的月薪相同,但是编号不同;
|
|
|
|
|
|
RANK 函数为每个员工指定了一个名次,IT 部门的“Valli,Pataballa”和“David,Austin”的名次都是 3;而且在他们之后的“Diana,Lorentz”的名次为 5,产生了跳跃;
|
|
|
|
|
|
DENSE_RANK 函数为每个员工指定了一个名次,IT 部门的“Valli,Pataballa”和“David,Austin”的名次都是 3;在他们之后的“Diana,Lorentz”的名次为 4,名次是连续值;
|
|
|
|
|
|
PERCENT_RANK 函数按照百分比指定名次,取值位于 0 到 1 之间。其中“Diana,Lorentz”的百分比排名为 1,也产生了跳跃。
|
|
|
|
|
|
以上SQL的简化写法:
|
|
|
```sql
|
|
|
SELECT d.department_name "部门名称", concat(e.first_name, ',' , e.last_name) "姓名", e.salary "月薪",
|
|
|
ROW_NUMBER() OVER w AS "row_number",
|
|
|
RANK() OVER w AS "rank",
|
|
|
DENSE_RANK() w AS "dense_rank",
|
|
|
PERCENT_RANK() w AS "percent_rank"
|
|
|
FROM employees e
|
|
|
JOIN departments d ON (e.department_id = d.department_id)
|
|
|
WHERE d.department_name in ('IT', 'Purchasing')
|
|
|
WINDOW w AS (PARTITION BY e.department_id ORDER BY e.salary DESC)
|
|
|
ORDER BY 1, 4;
|
|
|
```
|
|
|
其中,`WINDOW`定义了一个窗口变量 w,然后在窗口函数的 OVER 子句中使用了该变量;这样可以简化函数的输入。
|
|
|
|
|
|
以下语句演示了 CUME_DIST 和 NTILE 函数的作用:
|
|
|
```sql
|
|
|
SELECT concat(first_name, ',' , last_name) "姓名", hire_date AS "入职日期",
|
|
|
CUME_DIST() OVER (ORDER BY hire_date) AS "累积占比",
|
|
|
NTILE(100) OVER (ORDER BY hire_date) AS "相对位置"
|
|
|
FROM employees;
|
|
|
```
|
|
|
![[Pasted image 20240501102825.png]]
|
|
|
其中,CUME_DIST 函数显示 2001-01-13 以及之前入职的员工大概有 0.9%(1/107);NTILE(100) 函数表明前 1% 入职的员工有“Lex,De Haan”和“Hermann,Baer”,由于员工总数为 107,所以不是完全准确。
|
|
|
#### 3.2 取值窗口函数
|
|
|
|
|
|
以下语句使用 FIRST_VALUE、LAST_VALUE 以及 NTH 函数分别获取每个部门内部月薪最高、月薪最低以及月薪第三高的员工:
|
|
|
```sql
|
|
|
SELECT department_id, first_name, last_name, salary,
|
|
|
FIRST_VALUE(salary) OVER w,
|
|
|
LAST_VALUE(salary) OVER w,
|
|
|
NTH_VALUE(salary, 3) OVER w
|
|
|
FROM employees
|
|
|
WINDOW w AS (PARTITION BY department_id ORDER BY salary desc ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)
|
|
|
ORDER BY department_id, salary DESC;
|
|
|
```
|
|
|
![[Pasted image 20240501104752.png]]
|
|
|
LAG 和 LEAD 函数同样用于计算销量数据的环比/同比增长
|
|
|
```sql
|
|
|
WITH sales_monthly AS (
|
|
|
SELECT product, to_char(saledate,'YYYYMM') ym, sum(amount) sum_amount
|
|
|
FROM sales_data
|
|
|
GROUP BY product, to_char(saledate,'YYYYMM')
|
|
|
)
|
|
|
SELECT product AS "产品", ym "年月", sum_amount "销量",
|
|
|
(sum_amount - LAG(sum_amount, 1) OVER (PARTITION BY product ORDER BY ym))/
|
|
|
LAG(sum_amount, 1) OVER (PARTITION BY product ORDER BY ym) * 100 AS "环比增长率(%)"
|
|
|
FROM sales_monthly
|
|
|
ORDER BY product, ym;
|
|
|
```
|
|
|
![[Pasted image 20240501105012.png]]
|
|
|
首先,创建一个通用表表达式 sales_monthly,得到了不同产品每个月的销量汇总;LAG(sum_amount, 1) 表示获取上一期的销量;当前月份的销量减去上个月的销量,再除以上个月的销量,就是环比增长率。
|
|
|
|
|
|
### 4.正则表达式
|
|
|
|
|
|
```sql
|
|
|
-- 提取数字
|
|
|
NULLIF(regexp_replace(po_number, '\D','','g'), '')::numeric
|
|
|
```
|
|
|
|
|
|
## 三、索引
|
|
|
[PostgreSQL 14种索引的原理和应用场景 - 简书 (jianshu.com)](https://www.jianshu.com/p/6aedd1b79dba)
|
|
|
|
|
|
| 索引 | 原理 | 场景 | 适用的数据类型 | 版本限制 |
|
|
|
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ---- |
|
|
|
| btree | b-tree适合所有的数据类型,支持排序,支持大于、小于、等于、大于或等于、小于或等于的搜索 | | 所有 | 无 |
|
|
|
| hash | hash索引存储的是被索引字段VALUE的哈希值,只支持等值查询。 | | 所有 | 无 |
|
|
|
| gin | 当需要搜索多值类型内的VALUE时,适合多值类型,例如数组、全文检索、TOKEN。(根据不同的类型,支持相交、包含、大于、在左边、在右边等搜索) | | | |
|
|
|
| gist | GiST是一个通用的索引接口,可以使用GiST实现b-tree, r-tree等索引结构。不同的类型,支持的索引检索也各不一样。 | 例如: <br>1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。 <br>2、范围类型,支持位置搜索(包含、相交、在左右等)。 <br>3、IP类型,支持位置搜索(包含、相交、在左右等)。 <br>4、空间类型(PostGIS),支持位置搜索(包含、相交、在上下左右等),按距离排序。 <br>5、标量类型,支持按距离排序 | | |
|
|
|
| sp-gist | SP-GiST类似GiST,是一个通用的索引接口,但是SP-GIST使用了空间分区的方法,使得SP-GiST可以更好的支持非平衡数据结构,例如quad-trees, k-d tree, radis tree。 | 应用场景 :<br>1、几何类型,支持位置搜索(包含、相交、在上下左右等),按距离排序。 <br>2、范围类型,支持位置搜索(包含、相交、在左右等)。 <br>3、IP类型,支持位置搜索(包含、相交、在左右等)。 | | |
|
|
|
| brin | BRIN 索引是块级索引,有别于B-TREE等索引,BRIN记录并不是以行号为单位记录索引明细,而是记录每个数据块或者每段连续的数据块的统计信息。因此BRIN索引空间占用特别的小,对数据写入、更新、删除的影响也很小。 | 时序数据,在时间或序列字段创建BRIN索引,进行等值、范围查询时效果很棒。 | | |
|
|
|
| rum | rum 是一个索引插件,由Postgrespro开源,适合全文检索,属于GIN的增强版本。 <br>增强包括: <br>1、在RUM索引中,存储了lexem的位置信息,所以在计算ranking时,不需要回表查询(而GIN需要回表查询)。 <br>2、RUM支持phrase搜索,而GIN无法支持。 <br>3、在一个RUM索引中,允许用户在posting tree中存储除ctid(行号)以外的字段VALUE,例如时间戳。 | RUM不仅支持GIN支持的全文检索,还支持计算文本的相似度值,按相似度排序等。同时支持位置匹配,例如(速度与激情,可以采用"速度" <2> "激情" 进行匹配,而GIN索引则无法做到) | | |
|
|
|
| bloom | bloom索引接口是PostgreSQL基于bloom filter构造的一个索引接口,属于lossy索引,可以收敛结果集(排除绝对不满足条件的结果,剩余的结果里再挑选满足条件的结果),因此需要二次check,bloom支持任意列组合的等值查询。 <br>bloom存储的是签名,签名越大,耗费的空间越多,但是排除更加精准。有利有弊 | | | |
|
|
|
| zombodb | zombodb是PostgreSQL与ElasticSearch结合的一个索引接口,可以直接读写ES | 与ES结合,实现SQL接口的搜索引擎,实现数据的透明搜索 | | |
|
|
|
| bitmap | bitmap索引是Greenplum的索引接口,类似GIN倒排,只是bitmap的KEY是列的值,VALUE是BIT(每个BIT对应一行),而不是行号list或tree | 当某个字段的唯一值个数在100到10万之间(超出这个范围,不建议使用bitmap)时,如果表的记录数特别多,而且变更不频繁(或者是AO表),那么很适合BITMAP索引,bitmap索引可以实现快速的多个或单个VALUE的搜索。因为只需要对行号的BITMAP进行BIT与或运算,得到最终的BITMAP,从最终的BITMAP映射到行进行提取。 <br>bitmap与btree一样,都支持 等于,大于,小于,大于等于,小于等于的查询。 | | |
|
|
|
| 表达式索引 | 表达式索引也是PostgreSQL特有的特性,例如用户的数据需要转换后查询,例如某些设备上传的地理坐标的坐标系不符合国标,需要转换为国内的空间坐标来查询。 <br>那么可以针对这类字段,创建表达式索引,将转换过程放到表达式中,查询时也使用表达式进行查询。 | CREATE INDEX ods_ht_transport_realtime_pass_time_idx ON ods_ht_transport_realtime ((pass_time::DATE)); 函数索引 | | |
|
|
|
|
|
|
|
|
|
## 四、事务
|
|
|
|
|
|
|
|
|
|
|
|
## 五、常见问题
|
|
|
|
|
|
|
|
|
## 六、常用函数
|
|
|
|
|
|
+ 生成建表语句
|
|
|
```sql
|
|
|
CREATE OR REPLACE FUNCTION show_create_table(
|
|
|
in_schema_name varchar,
|
|
|
in_table_name varchar
|
|
|
)
|
|
|
RETURNS text
|
|
|
LANGUAGE plpgsql VOLATILE
|
|
|
AS
|
|
|
$$
|
|
|
DECLARE
|
|
|
-- the ddl we're building
|
|
|
v_table_ddl text;
|
|
|
|
|
|
-- data about the target table
|
|
|
v_table_oid int;
|
|
|
|
|
|
v_table_type char;
|
|
|
v_partition_key varchar;
|
|
|
v_table_comment varchar;
|
|
|
|
|
|
-- records for looping
|
|
|
v_column_record record;
|
|
|
v_constraint_record record;
|
|
|
v_index_record record;
|
|
|
v_column_comment_record record;
|
|
|
v_index_comment_record record;
|
|
|
v_constraint_comment_record record;
|
|
|
BEGIN
|
|
|
-- grab the oid of the table; https://www.postgresql.org/docs/8.3/catalog-pg-class.html
|
|
|
SELECT c.oid, c.relkind INTO v_table_oid, v_table_type
|
|
|
FROM pg_catalog.pg_class c
|
|
|
LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
|
|
|
WHERE c.relkind in ('r', 'p')
|
|
|
AND c.relname = in_table_name -- the table name
|
|
|
AND n.nspname = in_schema_name; -- the schema
|
|
|
|
|
|
-- throw an error if table was not found IF (v_table_oid IS NULL) THEN
|
|
|
RAISE EXCEPTION 'table does not exist';
|
|
|
END IF;
|
|
|
|
|
|
-- start the create definition
|
|
|
v_table_ddl := 'CREATE TABLE "' || in_table_name || '" (' || E'\n';
|
|
|
|
|
|
-- define all of the columns in the table; https://stackoverflow.com/a/8153081/3068233
|
|
|
FOR v_column_record IN
|
|
|
SELECT c.column_name,
|
|
|
c.data_type,
|
|
|
c.character_maximum_length,
|
|
|
c.is_nullable,
|
|
|
c.column_default
|
|
|
FROM information_schema.columns c
|
|
|
WHERE (table_schema, table_name) = (in_schema_name, in_table_name)
|
|
|
ORDER BY ordinal_position
|
|
|
LOOP
|
|
|
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|
|
|
|| '"' || v_column_record.column_name || '" '
|
|
|
|| v_column_record.data_type || CASE WHEN v_column_record.character_maximum_length IS NOT NULL THEN ('(' || v_column_record.character_maximum_length || ')') ELSE '' END || ' '
|
|
|
|| CASE WHEN v_column_record.is_nullable = 'NO' THEN 'NOT NULL' ELSE 'NULL' END
|
|
|
|| CASE WHEN v_column_record.column_default IS NOT null THEN (' DEFAULT ' || v_column_record.column_default) ELSE '' END
|
|
|
|| ',' || E'\n';
|
|
|
END LOOP;
|
|
|
|
|
|
-- define all the constraints in the; https://www.postgresql.org/docs/9.1/catalog-pg-constraint.html && https://dba.stackexchange.com/a/214877/75296
|
|
|
FOR v_constraint_record IN
|
|
|
SELECT con.conname as constraint_name,
|
|
|
con.contype as constraint_type,
|
|
|
CASE
|
|
|
WHEN con.contype = 'p' THEN 1 -- primary key constraint
|
|
|
WHEN con.contype = 'u' THEN 2 -- unique constraint
|
|
|
WHEN con.contype = 'f' THEN 3 -- foreign key constraint
|
|
|
WHEN con.contype = 'c' THEN 4
|
|
|
ELSE 5
|
|
|
END as type_rank,
|
|
|
pg_get_constraintdef(con.oid) as constraint_definition
|
|
|
FROM pg_catalog.pg_constraint con
|
|
|
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
|
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
|
|
|
WHERE nsp.nspname = in_schema_name
|
|
|
AND rel.relname = in_table_name
|
|
|
ORDER BY type_rank
|
|
|
LOOP
|
|
|
IF v_constraint_record.constraint_type = 'p' THEN
|
|
|
v_table_ddl := v_table_ddl || ' '
|
|
|
|| v_constraint_record.constraint_definition
|
|
|
|| ',' || E'\n';
|
|
|
ELSE
|
|
|
v_table_ddl := v_table_ddl || ' ' -- note: two char spacer to start, to indent the column
|
|
|
|| 'CONSTRAINT' || ' '
|
|
|
|| '"' || v_constraint_record.constraint_name || '" '
|
|
|
|| v_constraint_record.constraint_definition
|
|
|
|| ',' || E'\n';
|
|
|
END IF;
|
|
|
END LOOP;
|
|
|
|
|
|
-- drop the last comma before ending the create statement
|
|
|
v_table_ddl = substr(v_table_ddl, 0, length(v_table_ddl) - 1) || E'\n';
|
|
|
|
|
|
-- end the create definition
|
|
|
v_table_ddl := v_table_ddl || ')';
|
|
|
|
|
|
IF v_table_type = 'p' THEN
|
|
|
SELECT pg_get_partkeydef(v_table_oid) INTO v_partition_key;
|
|
|
IF v_partition_key IS NOT NULL THEN
|
|
|
v_table_ddl := v_table_ddl || ' PARTITION BY ' || v_partition_key;
|
|
|
END IF;
|
|
|
END IF;
|
|
|
|
|
|
v_table_ddl := v_table_ddl || ';' || E'\n';
|
|
|
|
|
|
-- suffix create statement with all of the indexes on the table
|
|
|
FOR v_index_record IN
|
|
|
SELECT regexp_replace(indexdef, ' "?' || schemaname || '"?\.', ' ') AS indexdef
|
|
|
FROM pg_catalog.pg_indexes
|
|
|
WHERE (schemaname, tablename) = (in_schema_name, in_table_name)
|
|
|
AND indexname NOT IN (
|
|
|
select con.conname
|
|
|
FROM pg_catalog.pg_constraint con
|
|
|
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
|
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
|
|
|
WHERE nsp.nspname = in_schema_name
|
|
|
AND rel.relname = in_table_name
|
|
|
)
|
|
|
LOOP
|
|
|
v_table_ddl := v_table_ddl
|
|
|
|| v_index_record.indexdef
|
|
|
|| ';' || E'\n';
|
|
|
END LOOP;
|
|
|
|
|
|
-- comment on table
|
|
|
SELECT description INTO v_table_comment
|
|
|
FROM pg_catalog.pg_description
|
|
|
WHERE objoid = v_table_oid AND objsubid = 0;
|
|
|
|
|
|
IF v_table_comment IS NOT NULL THEN
|
|
|
v_table_ddl := v_table_ddl || 'COMMENT ON TABLE "' || in_table_name || '" IS ''' || replace(v_table_comment, '''', '''''') || ''';' || E'\n';
|
|
|
END IF;
|
|
|
|
|
|
-- comment on column
|
|
|
FOR v_column_comment_record IN
|
|
|
SELECT col.column_name, d.description
|
|
|
FROM information_schema.columns col
|
|
|
JOIN pg_catalog.pg_class c ON c.relname = col.table_name
|
|
|
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = c.relnamespace AND col.table_schema = nsp.nspname
|
|
|
JOIN pg_catalog.pg_description d ON d.objoid = c.oid AND d.objsubid = col.ordinal_position
|
|
|
WHERE c.oid = v_table_oid
|
|
|
ORDER BY col.ordinal_position
|
|
|
LOOP
|
|
|
v_table_ddl := v_table_ddl || 'COMMENT ON COLUMN "' || in_table_name || '"."'
|
|
|
|| v_column_comment_record.column_name || '" IS '''
|
|
|
|| replace(v_column_comment_record.description, '''', '''''') || ''';' || E'\n';
|
|
|
END LOOP;
|
|
|
|
|
|
-- comment on index
|
|
|
FOR v_index_comment_record IN
|
|
|
SELECT c.relname, d.description
|
|
|
FROM pg_catalog.pg_index idx
|
|
|
JOIN pg_catalog.pg_class c ON idx.indexrelid = c.oid
|
|
|
JOIN pg_catalog.pg_description d ON idx.indexrelid = d.objoid
|
|
|
WHERE idx.indrelid = v_table_oid
|
|
|
LOOP
|
|
|
v_table_ddl := v_table_ddl || 'COMMENT ON INDEX "'
|
|
|
|| v_index_comment_record.relname || '" IS '''
|
|
|
|| replace(v_index_comment_record.description, '''', '''''') || ''';' || E'\n';
|
|
|
END LOOP;
|
|
|
|
|
|
-- comment on constraint
|
|
|
FOR v_constraint_comment_record IN
|
|
|
SELECT con.conname,
|
|
|
pg_description.description
|
|
|
FROM pg_catalog.pg_constraint con
|
|
|
JOIN pg_catalog.pg_class rel ON rel.oid = con.conrelid
|
|
|
JOIN pg_catalog.pg_namespace nsp ON nsp.oid = connamespace
|
|
|
JOIN pg_catalog.pg_description ON pg_description.objoid = con.oid
|
|
|
WHERE rel.oid = v_table_oid
|
|
|
LOOP
|
|
|
v_table_ddl := v_table_ddl || 'COMMENT ON CONSTRAINT "'
|
|
|
|| v_constraint_comment_record.conname || '" ON "' || in_table_name || '" IS '''
|
|
|
|| replace(v_constraint_comment_record.description, '''', '''''') || ''';' || E'\n';
|
|
|
END LOOP;
|
|
|
|
|
|
-- return the ddl
|
|
|
RETURN v_table_ddl;
|
|
|
END
|
|
|
$$;
|
|
|
```
|
|
|
```sql
|
|
|
-- 需要指定schema
|
|
|
select show_create_table('public', 'device')
|
|
|
```
|