PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)|学习笔记

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: 快速学习3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)

开发者学堂课程【PostgreSQL快速入门:3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)】学习笔记与课程紧密联系,让用户快速学习知识

课程地址https://developer.aliyun.com/learning/course/16/detail/89


3 PostgreSQL psql的使用,SQL语法,数据类型,递归SQL用法(四)

 

内容介绍

DML

、Query

练习

八、DML

INSERT

UPDATE

DELETE

个事务最大2^32条SQL(因为cmin,cmax的长度是4Bytes)一个事务中可以执行40条sql语句,cmin,cmax相当于事务中某一条sql语句的编号四个字节存储

PostgreSQL个事务中可以包含DML, DDL, DCL。

Begin

Drop table t

Rollback

t表还在可以放在事务里面

除了以下

create tablespace

create database

使用concurrently并行创建索引

其他未尽情况略

(Oracle执行DDL前自动将前面的未提交的事务提交,所以Oracle不支持在事务中执行DDL语句)

 

九、Query

1、JOIN

T1 CROSS JOIN T2 (T1 INNER JOIN T2 ON TRUE )

T1 [INNER]  LEFT RIGHT FULL ; [OUTER] , JOIN T2 ON boolean_ expression支持内部join还支持外部join

T1 [INNER] LEFT RIGHT FULL ; [OUTER] , JOIN T2 USING ( join column list )

T1 NATURAL  [INNER]  LEFT  RIGHT  FULL , [OUTER]  JOIN T2

The words INNER and OUTER are optional in all forms. INNER is the default; LEFT, RIGHT,and FULL imply an outer join.

减少sql的写法比如两个表的字段名相同类型相同的情况下可以用NATURAL join,NATURAL列刚好相同的字段时可以用NATURAL join

In the join只有两个表完全匹配时join才有输出left join左边的表跟右边的表没有任何匹配的情况下记录也可以输出

CROSS JOIN产生笛卡尔积不带任何条件两个表之间每一条都会有相互关系

当关联的两个表有两列同名同类型同长度,以下三种写法等同。

Natural inner join t1表和t2表有一个字段是相同的并且同名同类型就可以省掉 join的字段num跟Natural join一样

左或右连接,不满足条件的右或左表的值。

Left join t2 on t1,t1表的id等于2跟右边的number等于2没有匹配因为右边的表只有13,左边的表有123,关联时左边的表也会输出Right join也是一样t2表所有的字段跟t1表没有匹配时也有输出

全关联,不满足条件的值都置空。匹配的可以输出不匹配也能输出

2、ALIAS

table alias: 表的别名

FROM table_reference AS alias

FROM table_reference alias

colum alias:列的别名

SELECT expression [[ AS ] output_ name ]

subquery alias:只查询的别名

FROM (SELECT * FROM table1) AS alias_ name

3、Table as Function's Return data type作为表的返回类型

表或者函数返回的数据类型把一个表名作为函数的返回类型

return table's row type

create table t1 (id int,name text,crt_ time timestamp(o));创建表是t1

create or replace function f_ t1 (i_ id int) returns setof t1 as $$返回类型也可以指定t1,在创建一个表时默认创建跟表结构一样的复合数据类型

declare

begin

return query select * from t1 where id=i_id;直接把表返回也可以

return;

end;

$$ language plpgsql;

insert into t1 values(1,'digoal',now0);

insert into t1 values(1,'DIGOAL',now0);

select * fromf _t1(1);

id] nameI crt_ time

1 digoal I 2012-04-26 08:15:09

1 DIGOALI 2012-04-26 08:15:15

return composite type返回复合类型

create type type1 as (id int,name text,crt_time timestamp(o));创建复合类型跟t1表一样

create or replace function f_ type1 (i_id int) returns setof type1 as $$

declare

begin

return query select * from t1 where id=i_id;

return;

end;

$$ language plpgsql;

select * from f_ type1(1);

id I name I crt_ time

1 I digoal I 2012-04-26 08:15:09

1 I DIGOAL I 2012-04-26 08:15:15

return record 弹性数据类型可能有一个字段也可能有两个字段在弹性类型里面返回的时候输出的是什么结构

create or replace function f_ record1 (i_ id int) returns setof record as $$

declare

begin

return query select * from t1 where id=i _id;

return;

end;

$$ language plpgsql;

输出的是t1的数据类型写上对应t的框架

select * from f_ record1(1) as (id int,name text,crt_time timestamp(o));

id I Name I crt_ time

1 I digoal  I2012-04-26 08:15:09

1 I DIGOAL I 2012-04-26 08:15:15

4、GROUP BY [ HAVING ]

5、DISTINCT

SELECT DISTINCT setect_ list ... (NULL/EDISTINCT [ON]中视为相等)

postgres= # select * from t1 ;

id  name crt_ time

1 I digoal I 2012-04-26 08:15:09

1 I DIGOAL I 2012-04-26 08:15:15

postgres= # select distinct id from t1;

id

1

SELECT DISTINCT ON (expression [, expression .. ])select_ list ... -- ON()里面必须出现在order by中作为前导列,输出跟前导列相关的DISTINCT ON,同时输出其它的查询列可以简化使用窗口函数

Here expression is an arbitrary value expression that is evaluated for all rows. A set of rows for which all the expressions are equal are considered duplicates, and only the first row of the set is kept in the output. Note that the "first row" of a set is unpredictable unless the query is sorted on enough columns to guarantee a unique ordering of the rows arriving at the DISTINCT filter. (DISTINCT ON processing occurs after ORDER BY sorting.)

postgres= =# select distinct on (id)-id,name,crt_ time from t1 order by id,crt_ time;输出id是唯一的id只输出一个按照顺序进行排序只要拿到一条就返回,digoal查处消息排在第一位当id=1取到两条只会要第一条

Id  name crt_ time

1 digoal 2012-04-26 08:15:09

postgres= =# select distinct on (id) id,name,crt_ time from tl order by id,crt_ time desc;必须在前导列里

Id name crt_ time

1 DIGOAL  2012-04-2608:15:156、COMBINING QUERY

Id name也是一样要放在前面

使用DISTINCT ON实现用窗口函数实现的取第一名的功能

postgres=# CREATE TABLE window_ test(id int, name text, subject text, score numeric);创建表记录学生每一科的考试成绩插入15条记录每个学科取一个取出id取出name分数科目

postgres=# INSERT INTO window_ test VALUES (,digoal,'数学',99.5), (2,digoal,;语文',89.5),

(3,'digoal',英语',79.5), (4,digoal",;物理' ,9.5), (5,digoal;'化学',98.5),

(6,刘德华';数学,89.5), (7,刘德华',语文.,99.5), (8,刘德华,英语',79.5),

(9,刘德华,物理,89.5), (10,刘德华;化学',69.5),

(11,张学友,数学",89.5), (12,张学友,语文",91.5), (13,张学友,英语',92.5),

(14,张学友,物理",93.5), (15,;张学友,化学,94.5);

取出每门课程的第一名

postgres=# select distinct on (subject) jid,name,subject,score from window_test order by subject,score desc;排序按照倒叙

5 Digoal 化学98.5

1 Digoal 数学99.5

4 Digoal 物理99.5

13 张学友 英语92.5

7 刘德华 语文99.5

与使用窗口函数得到的结果一致, 并且写法更简洁

select = from <select row_number() over < partition by subject order by score desc> as rn, * from window test> as t where rn

=1;

5 Digoal 化学98.5

1 Digoal 数学99.5

4 Digoal 物理99.5

13 张学友 英语92.5

7 刘德华 语文99.5

合并查询两个查询的结果集合起来两个查询的结果集算出相交的部分第一个查询结果集排除掉第二个查询的结果集的一些结果

queryl UNION [ALL] query2 两个查询结果集合并并且去重

queryl INTERSECT [ALL] query2 两个结果集合并不用去重

queryl EXCEPT [ALL] query2

5"

UNION effectively appends the result of query2 to the result of 'queryl (although there is no guarantee that this is the order in which the rows are actually returned)

INTERSECT returns all rows that are both in the result of queryl and in the result of query2.两个查询共有的部分相交的部分输出并且去重all就不去重

EXCEPT returns all rows that are in the result of queryl but not in the result of 'query2.

Combining Query eliminates duplicate rows from its result, in the same way as DISTINCT,

I unless ALL is used

queryl and query2 must return the same number of columns and the corresponding columns

have compatible data types.

8、LIMIT [ OFFSET ]

SELECT select_ list

FROM table_ expression

ORDER BY sort_ expressionl [ASCIDESC] [NULLS { FIRST[LAST} ,]空放在最前面还是最后面

[, sort_ expression2 [ASC I DESC] [NULLS i FIRST LAST ]...]

限制多少行比如表只输出一行就只有一个offset10扫描到第十行之后只输出一行

9、函数三态简介

非常重要的函数三态

在prostgre里面函数有三个状态第一个跟函数本身有关第二个函数的三个状态对prostgre执行计划的优化器会拿来用优化器如果看到函数是超级稳定的状态比如immutable,在执行时对于同样的参数传入在sql语句里面即使扫描了多行也只会处理一遍,immutable类似于常量只会同样的参数传入只会传入一遍返回结果sql语句在执行时会大大减少运算量

immutable最稳定的状态

Stable第二稳定

Volatile最不稳定即使传入的参数是一样的输出的结果多次调用输出的结果可能不一样比如select-now函数多次调用输出的结果不一样对于常量的函数如果是StableVolatile在一次事务中多次执行的结果可能是一样的

创建一个序列seq

digoal=# create sequence seq;

CREATE SEQUENCE

取序列的下一个值最不稳定

digoal=# select provolatile from pg_ proc where proname = 'nextval';

在一次事务中不停的调用它得到的值是不一样的即使是在sql语句中调用它也是不一样的比如调用15条记录得到的值都是不一样的

provolatile

V

(1 row)

digoal=# select nextval('seq'::regclass) from generate_ _series(1,3);

Nextval

1

2

3

(3 rows)

改成immutable后只会执行一次优化器在解析sql时只会执行一次不管有多少条记录30条记录也只会执行一次

digoal=# alter function nextval(regclass) immutable;

ALTER FUNCTION

digoal=# select nextval('seq'::regclass) from generate_ series(1,3);

nextval

4

4

4

(3 rows)

digoal=#alter function nextval(regclass) stable;执行一次只不过放在查询里面和放在后面不一样

ALTER FUNCTION

digoal=# select nextval'seq':regclass) from generate_ series(1,3);

nextval

5

6

7

(3 rows)

在select子句和在where子句中有差别。

例如某个表有5条记录:

digoal=# select *from t;

Id

3

3

3

4

5

(5 rows)

对于stable态,它在select中,每条tuple都要运算到这个函数,但是在where子句中,只运算次。

select nextval<' seq' >from t;

select from t where nextval<' seq' >=138;只执行一遍三条记录都返回执行多遍只返回一条记录

select nextval<’seq'>;

nextval

142

where子句在selec字句中,stable有一定的区别跟解析有关系

stableimmutable的不同之处,immutable两遍都是一样的where字句中和查询字句中结果一样

stable函数在execute时执行

immutable函数在plan时执行

所以stableimmutable仅仅当使用planed query时才有区别

如果使用执行器绑定变量的查询Planed query属于一次plan多次execute,immutable在Planed query中也只会执行一次就是常量stable函数是在execute执行的在这种场景中是会执行多次的

immutable稳定性>stable>volatile

另一些需要注意的:

函数内有多条SQL时,例如

digoal=# Create or replace function f10 returns setof int as$$

declare

v_ id int;

begin

Forv_ id in select id from t loop

return next V_ id;

End loop;

在这之间如果t发生了变更并提交了,volatile函数能看到这个变更。

perform pg_sleep(10);在执行过程中每一个sql语句看到的不是一个

For v_ id in select id from t loop

return next v_id;

End loop;

Return;

End;

$$ language plpgsql strict volatile;

volatile处理每个SQL都是一个新的snapshot. Stable和immutable将调用函数时作为这个snapshot。

创建函数中间休息十秒钟返回int类型来自于t表休息十秒钟把t表id全部储存如果期间有过变更当前123,三条记录第一次取出的是前面几条第二次取出后面几条如果是在执行过程中delete就只会返回三条记录后面的变更也会看到t表已经没了如果把函数改成stable,再进行查询比如插入几条数据都看不到,0 rows使用volatile函数在整个里面不是镜像而是每一条sql语句都会产生pg_sleep隔离级别的类似的查询对于Stable和immutable函数停了10秒钟再查询看到的是调用函数的

Select=from f1() ;

多次调用和单词调用有区别当前状态改成immutable。

function f1<>immutable;

immutable函数只调用一次只会select10十秒之后就可以看到结果如果改成volatile,每一条就都会执行执行十次一百秒之后才会看到结果pg_sleep默认volatile执行三条记录会执行3如果改成immutable就只会执行一遍只需要一秒出结果。对于大运算量的函数,且一条sQL如果返回多值,并且传入的参数致的情况下. 调用次显然比调用多次耗费小。

实际使用过程中选择好三态是非常重要的。

10、WITH

as把查询作为一个表对两个表进行关联查询

WITH regional_ sales AS (

SELECT region, SUM(amount) AS total_ sales

FROM orders

GROUP BY region

),top_ regions AS ( 也可以是update查询数据后面再进行查询也可以

SELECT region

FROM regional sales

WHERE total_ sales > (SELECT SUM(total_ sales)/10 FROM regional_sales)

SELECT region,

product,

SUM(quantity) AS product_ units,

SUM(amount) AS product_ sales

FROM orders

WHERE region IN (SELECT region FROM(top_ regions)

GROUP BY region, product;

如果是递归查询使用valus

Valus(1)非递归字句

Union all 不去重Union去重

SELECT n+1 FROMt WHERE n< 100 递归字句

SELECT sum(n) FROM t;输出的结果做查询中间会产生临时存放的对象

UNION ALL去重复(去重复时NULL视为等同)

图中所有输出都涉及UNION[ALL]的操作,包含以往返回的记录和当前返回的记录

图表示如何做循环的从非递归子句开始输出放在output里面递归字句读取output同时输出到TEMP Working TABLe临时工作表里面会有判断如果临时工作表里面有数据它会进入递归再次调用递归子句递归子句会先把临时工作表的数据读出来进行关联递归子句里面有关联t是工作表where条件再次查询输出到TEMP immutable table替换掉twt的内容后清空自己递归子句执行后TEMP Working TABLe已经刷新了已经从TEMP immutable table刷到TEMP Working TABLe中表已经清掉如果TEMP Working TABLe中有数据又会进入循环如果没有数据就直接输出了在循环里面有第六步还做了输出递归子句从TEMP Working TABLe读数据同时会产生输出输出到output有合并的动作如果是UNION就会去重如果是UNION all就直接扔到output中如果在TEMP Working TABLe中一直有数据就会进入无限的循环

image.png

TEMP Working Table 没有ctid, cmin, cmax, xmin, xmax, tableoid虚拟字段如果有会报错

postgres=# create table test (id int,name text);

postgres=# insert into test values (1,'digoall),(2,'digoal2');

postgres=# begin;

postgres=# with t1 as (update test set name= ='DIGOAL2' where id=2 returning *)

select ctid from t1;

ERROR: column "ctid" does not exist

LINE 2: select ctid from t1;

postgres =# rollback;

其它字段(cmin,cmax,xmin,xmax,tableoid)同样错误

递归查询输出产品部以及该部门的所有子部门信息

然后输出各个子部门以及各个子部门的人数

第一次取出的表隐式使用中间表

WITH RECURSIVE included_ parts(sub_ part, part, quantity) AS (开头非递归子句

初始输出

SELECT sub_part, part, quantity FROM parts WHERE part=产品部’。子部门部门id人数

UNION ALL不去重

把第一个sql取到的值作关联

SELECT p.sub_ part, B:part, p.quantity循环sql

第一步时读取的是初始输出,后面都是TEMP Working TABLE

FROM included parts pr, parts p进行关联

WHERE p.part = pr.sub_ part部门等于子部门输出只要产品部输出之后就能关联到子部门sub就是r表父表父表的子部门等于p表的部门id输出第一个子部门输出完之后会放到临时工作表相当于产品部第一个子部门第一步循环到达再进行循环一直到两个字段不匹配,把所有的子部门都输出来在递归循环后面再做操作

)

SELECT sub_ part, SUM(quantity) as total quantity输出每一子部门的部门id和总人数

FROM included_ parts

GROUP BY sub_ part

死循环

image.png

每次递归输出的记录与以往的记录都不一样,TEMP Working Table永远都有记录,因此无限循环

规避上一个死循环的方法

让递归SQL有机会没记录输出

WITH RECURSIVE search_ graph(id, link, data, depth, path, cycle) AS (

SELECT g.id, g.link, g.data, 1,

ARRAY[g.id],

False 变成ture

FROM graph g

UNION ALL

SELECT g.id, g.link, g.data, sg.depth+ 1,

Path||g.id,

g.id = ANY(path)

FROM graph g, search_ graph sg

WHERE g.id = sg.link AND NOT cycle判断变成flase

SELECT * FROM search_ graph;

多值比较需使用ROW类型的ARRAY

WITH RECURSIVE search_ graph(id, link, data, depth, path, cycle) AS (

SELECT g.id, g.link, g.data, 1,

ARRAY[ROW(g.f1, g.f2)],

false

FROM graph g

UNION ALL

SELECT g.id, g.link, g.data, sg.depth+1,

path||ROW(g.f1, g.f2),

ROW(g.f1, g.f2)= ANY(path)相当于数组

FROM graph g, search_graph sg

WHERE g.id = sg.link AND NOT cycle

SELECT * FROM search_ graph;

还有什么情况可以跳出循环

WITH RECURSIVE t(n) AS(

SELECT 1

UNION ALL

SELECT n+1FROM t

SELECT n FROM t LIMIT 100;

注意如果t表在递归子句的外围被join了然后再limit的还死循环

使用递归查询注意防止死循环

把属于产品部以及它的子部门的记录删除跟第一个例子差不多

WITH RECURSIVE included_ parts(sub_ part, part) AS (

SELECT sub part, part FROM parts WHERE part = '产品部'

UNION ALL

SELECT p.sub_ part, p.part

FROM included_ parts pr, parts p

WHERE p.part = pr.sub_ part

)

DELETE FROM parts

WHERE part IN (SELECT part FROM included_ parts);

WITH的所有子句包括MAIN子句查看到的是一个SNAPSHOT拆成很多个sql语句看到的是同一个SNAPSHOT

各个子句对记录的变更相互看不到,如果要看到变更的数据需使用RETURNING子句比如update一些记录对于查询语句它看到的是update之前的记录并不是update之后的只有RETURNING *才会查产品表查t表看到更新后的记录否则看到的就是更新前的记录

WITH t AS(

UPDATE products SET price = price * 1.05 WHERE id= 10

RETURNING *

SELECT * FROM products WHERE id= 10;

WITH t AS (

UPDATE products SET price= price * 1.05 WHERE id= 10

RETURNING *

SELECT* FROM t;

测试表

postgres =# create table test (id int,name text);

CREATE TABLES

postgres= =# insert into test values(1,'digoall),(2,'digoal2');

这样会看到老数据

postgres= =# with t1 as (update test set name ='NEW' where id=2 returning *)

postgres-# select * from test where name ='NEW";

id name

(0 rows)

postgres= =# with t1 as (update test set name= ='NEWNEW" where id= 2 returning *)

postgres-# select * from t1 where name ='NEWNEW";

id name

2 I NEWNEW

(1 row)

 

十、练习

1、psqI的使用熟悉特别是psql的帮助对于使用会带来很大的便利性查看表结构查看表大小都可以查询比如创建extensions外部表

2、数据库基本操作练习,例如建表,表的操作,建立测试模型,使用pgbench测试

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
1月前
|
SQL 关系型数据库 MySQL
TiDB支持的SQL语法概述
【2月更文挑战第28天】本章将对TiDB所支持的SQL语法进行概述,涵盖其主要的语法特性和功能。我们将从基本的SQL语句到更复杂的查询和操作,逐步介绍TiDB的SQL语法,帮助读者更好地理解和使用TiDB进行数据库操作。
|
2月前
|
SQL 存储 数据管理
阿里云视觉智能开放平台的逻辑数仓基于统一的SQL语法
【2月更文挑战第9天】阿里云视觉智能开放平台的逻辑数仓基于统一的SQL语法
52 2
|
1月前
|
SQL 数据库
sql server中创建数据库和表的语法
sql server中创建数据库和表的语法
18 1
|
1月前
|
SQL 存储 关系型数据库
SQL的基本语法以及SQL语句的关键字的使用,SELECT、INSERT、UPDATE、DELETE、CREATE、ALTER、DROP等。
SQL的基本语法以及SQL语句的关键字的使用,SELECT、INSERT、UPDATE、DELETE、CREATE、ALTER、DROP等。
|
1月前
|
SQL 监控 测试技术
SQL语法优化与最佳实践
【2月更文挑战第28天】本章将深入探讨SQL语法优化的重要性以及具体的优化策略和最佳实践。通过掌握和理解这些优化技巧,读者将能够编写出更高效、更稳定的SQL查询,提升数据库性能,降低系统资源消耗。
|
1月前
|
SQL 关系型数据库 MySQL
TiDB特有的SQL语法和特性
【2月更文挑战第28天】本章将深入探讨TiDB特有的SQL语法和特性,这些功能和优化是TiDB相较于传统关系型数据库所独有的。通过了解这些特性,读者将能更充分地利用TiDB的优势,优化数据库性能,提升业务处理效率。
|
1月前
|
SQL Java 关系型数据库
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
MyBatis的动态SQL之OGNL(Object-Graph Navigation Language)表达式以及各种标签的用法
18 0
|
1月前
|
SQL Java 关系型数据库
MyBatis中的9种常用动态sql标签精妙用法
MyBatis中的9种常用动态sql标签精妙用法
67 0
|
2月前
|
SQL 关系型数据库 MySQL
Mysql SQL的一些特殊用法记录
1、查询group by having 中having不起作用,及解决
15 0
|
2月前
|
SQL
SQL语句case when的用法
SQL语句case when的用法