PostgreSQL学习笔记(已完结)

POSTGRESQL

1.安装

下载地址:https://www.enterprisedb.com/downloads/postgres-postgresql-downloads

本环境安装目录为:D:\Program Files (x86)\PostgreSQL

#启动psql
d:
cd /Program Files (x86)/PostgreSQL/13/
./bin/pg_ctl -D ./data start
./bin/psql postgres

2.PLSQL常用操作命令

命令含义
\l查看已经存在的数据库
\c <数据库名>连接指定数据库
\d查看已经存在的表
\q退出pssql

3.创建数据库

CREATE DATABASE shop;	--创建数据库shop

4.创建表

CREATE TABLE Product(
product_id	CHAR(4)	NOT NULL,
product_name	VARCHAR(100)	NOT NULL,
product_type	VARCHAR(32)	NOT NULL,
sale_price	INTEGER,
purchase_price	INTEGER,
regist_date	DATE,
PRIMARY KEY (product_id));

5.基本数据类型

数据类型含义
INTEGER数字型,不能存储小数
CHAR定长字符型,CHAR(10)表示最多输入10长度的字符,不足10长度,以空格补齐
VARCHAR可变长字符型
DATE日期型

6.删除表

DROP TABLE Product;

7.表中列的操作

ALTER TABLE Product ADD COLUMN product_name_pinyin VARCHAR(100);	--表中增加列
ALTER TABLE Product	DROP COLUMN product_name_pinyin;	--表中删除列
BEGIN TRANSACTION;										--表中插入数据
INSERT INTO Product VALUES('0001','T恤衫','衣服',1000,500,'2009-09-20');
COMMIT;

练习题

练习一:

编写一条CREATE TABLE语句,用来创建一个包含下表中所列各项的表Addressbook(地址簿),并为regist_no(注册编号)列设置主键约束。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nviUCB74-1610353893932)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201214182721962.png)]

CREATE TABLE Addressbook(
regist_no INTEGER NOT NULL,
name VARCHAR(128) NOT NULL,
address VARCHAR(256) NOT NULL,
tel_no CHAR(10),
mail_address CHAR(20),
PRIMARY KEY (regist_no));

练习二:

假设在创建练习1.1中的Addressbook表时忘记添加如下一列postal_code(邮政编码)了,请把此列添加到Addressbook表中。列名:postal_code数据类型:定长字符串类型(长度为8)约束:不能为NULL。

ALTER TABLE Addressbook ADD COLUMN postal_code CHAR(8) NOT NULL;

练习三:

编写SQL语句来删除Addressbook表。

DROP TABLE Addressbook;

8.SELECT语句

语法:SELECT <列名> FROM <表名>;

子句:DISTINCT:从查询结果中删除重复行

​ WEHRE:指定行所对应的条件

​ AS:为列设置别名

SELECT * FROM Product;
product_id | product_name | product_type | sale_price | purchase_price | regist_date
-----------+--------------+--------------+------------+----------------+-------------
 0001      | T恤衫        | 衣服         |       1000 |            500 | 2009-09-20
 0002      | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11
 0003      | 运动T恤      | 衣服         |       4000 |           2800 |
 0004      | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20
 0005      | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-09-20
 0006      | 叉子         | 厨房用具     |        500 |                | 2009-09-20
 0007      | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28
 0008      | 圆珠笔       | 办公用品     |        100 |                | 2009-11-11
(8 行记录)

SELECT DISTINCT product_type FROM Prodcut;
 product_type
--------------
 衣服
 办公用品
 厨房用具
(3 行记录)

SELECT product_id AS "商品编号",		--设置别名,如果别名是中文,必须要双引号引起来
product_name AS "商品名称",
purchase_price AS "进货单价"
FROM Product;
 商品编号 | 商品名称 | 进货单价
----------+----------+----------
 0001     | T恤衫    |      500
 0002     | 打孔器   |      320
 0003     | 运动T恤  |     2800
 0004     | 菜刀     |     2800
 0005     | 高压锅   |     5000
 0006     | 叉子     |
 0007     | 擦菜板   |      790
 0008     | 圆珠笔   |
(8 行记录)

SELECT product_name, product_type WHERE product_type='衣服';
 product_name | product_type
--------------+--------------
 T恤衫        | 衣服
 运动T恤      | 衣服
(2 行记录)

9.注释

注释符含义
–<注释内容>单行注释
/*<注释内容>*/多行注释

10.运算符

四则运算

运算符含义
+
-
*
/

示例

SELECT (100+200)*3 AS colculation;
 calculation
-------------
         900
(1 行记录)

比较运算

运算符含义
=等于
<>不等于
>=大于等于
>大于
<=小于等于
<小于
IS NULL
IS NOT NULL非空

示例

SELECT product_id, product_name, purchase_price FROM product WHERE purchase_price=500;
 product_id | product_name | purchase_price
------------+--------------+----------------
 0001       | T恤衫        |            500
(1 行记录)
SELECT product_id, product_name, purchase_price FROM product WHERE purchase_price<>500; 
 product_id | product_name | purchase_price
------------+--------------+----------------
 0002       | 打孔器       |            320
 0003       | 运动T恤      |           2800
 0004       | 菜刀         |           2800
 0005       | 高压锅       |           5000
 0007       | 擦菜板       |            790
(5 行记录)
字符串比较运算

‘1’<‘10’<‘2’<‘222’<‘3’

示例

CREATE TABLE Chars(
chr CHAR(3) NOT NULL,
PRIMARY KEY (chr));						--创建表Chars
BEGIN TRANSACTION;
INSERT INTO Chars VALUES ('1');
INSERT INTO Chars VALUES ('2');
INSERT INTO Chars VALUES ('3');
INSERT INTO Chars VALUES ('10');
INSERT INTO Chars VALUES ('11');
INSERT INTO Chars VALUES ('222');
COMMIT;									--插入6行数据
SELECT * FROM Chars;					--查询Chars表中数据
 chr
-----
 1
 2
 3
 10
 11
 222
(6 行记录)
SELECT chr FROM Chars WHERE chr>'2';	--从Chars表中查询大于'2'的字符
 chr
-----
 3
 222
(2 行记录)
注意

不能对NULL进行比较运算,如果需要选取不是NULL的记录时,需要使用IS NOT NULL

示例

SELECT product_name, purchase_price FROM product WHERE purchase_price IS NOT NULL;
 product_name | purchase_price
--------------+----------------
 T恤衫        |            500
 打孔器       |            320
 运动T恤      |           2800
 菜刀         |           2800
 高压锅       |           5000
 擦菜板       |            790
(6 行记录)

逻辑运算

运算符含义
NOT
AND
OR

示例

SELECT product_name, purechase_price FROM product WHERE product_type='厨房用具' AND sale_price>=3000;
 product_name | purchase_price
--------------+----------------
 菜刀         |           2800
 高压锅       |           5000
(2 行记录)
SELECT product_name, purchase_price FROM product WHERE product_type='厨房用具' OR sale_price>=30000;
--------------+----------------
 菜刀         |           2800
 高压锅       |           5000
 叉子         |
 擦菜板       |            790
(4 行记录)

练习题

练习题一:

编写一条SQL语句,从Product(商品)表中选取出“登记日期(regist_date)在2009年4月28日之后”的商品。查询结果要包含product_name和regist_date两列。

SELECT product_name, regist_date FROM Product WHERE regist_date>'2009-04-28';
 product_name | regist_date
--------------+-------------
 T恤衫        | 2009-09-20
 打孔器       | 2009-09-11
 菜刀         | 2009-09-20
 高压锅       | 2009-09-20
 叉子         | 2009-09-20
 圆珠笔       | 2009-11-11
(6 行记录)

练习题二:

请说出对Product表执行如下3条SELECT语句时的返回结果。

SELECT * FROM product WHERE purchase_price=NULL;
--无结果
SELECT * FROM product WHERE purchase_price<>NULL;
--无结果
SELECT * FROM product WHERE purchase_price>NULL;
--无结果
--原因为不能与NULL做比较运算,如果需要的话,可以使用IS NOT NULL或IS NULL

练习题三:

从Product表中取出“销售单价(sale_price)比进货单价(purchase_price)高出500日元及以上”的商品。请写出两条可以得到相同结果的SELECT语句。执行结果如下所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vyDvJ0Pg-1610353893935)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201214184915126.png)]

SELECT product_name, sale_price, purchase_price FROM Product WHERE (sale_price-purchase)>=500;

练习题四:

请写出一条SELECT语句,从Product表中选取出满足“销售单价打九折之后利润高于100日元的办公用品和厨房用具”条件的记录。查询结果要包括product_name列、product_type列以及销售单价打九折之后的利润(别名设定为profit)。

SELECT product_name, product_type, (sale_price*0.9) AS profit 
FROM Product 
WHERE (sale_price*0.9-purchase_price)>100 
AND (product_type='办公用品' OR product_type='厨房用具');
 product_name | product_type | profit
--------------+--------------+--------
 打孔器       | 办公用品     |  450.0
 高压锅       | 厨房用具     | 6120.0
(2 行记录)

11.聚合函数

将多行汇总为一行的函数,即输入多行、输出一行

注意,聚合行数会将NULL排除在,COUNT(*)例外。

常用函数含义备注
COUNT计算表中的记录数(行数)COUNT(*)会得到所有列(即一张表)的行数,COUNT(<列名>)会得到NULL之外的数据行数
SUM计算表中数值列中数据的合计值NULL为0,只适用于数值类型
AVG计算表中数值列中数据的平均值NULL所在的行不算在分母中,只适用于数值类型
MAX计算表中任意列数据的最大值
MIN计算表中任意列数据的最小值

示例

SELECT COUNT(*) FROM product;
 count
-------
     8
(1 行记录)
SELECT COUNT(purchase_price) FROM product;		//purchase_price列中有两行值为NULL
 count
-------
     6
(1 行记录)
SELECT SUM(sale_price) FROM product;
  sum
-------
 16780
(1 行记录)
SELECT SUM(sale_price), SUM(purchase_price) FROM product;
  sum  |  sum
-------+-------
 16780 | 12210
(1 行记录)
SELECT AVG(sale_price), AVG(purchase_price) FROM product;
          avg          |          avg
-----------------------+-----------------------
 2097.5000000000000000 | 2035.0000000000000000
(1 行记录)
SELECT MAX(sale_price), min(purchase_price) FROM product;
 max  | min
------+-----
 6800 | 320
(1 行记录)

12.GROUP BY子句

对指定列的数据进行分组。

注意:

  1. GROUP BY子句中指定的列名被称之为聚合键。当使用GROUP BY子句时,SELECT子句中不能出现聚合键之外的列名。
  2. GROUP BY子句结果的显示是无序的。
  3. 只有SELECT子句和HAVING子句(以及ORDER BY子句)中能够使用聚合函数。

示例:

SELECT product_type, COUNT(*) FROM product GROUP BY product_type;
 product_type | count
--------------+-------
 衣服         |     2
 办公用品     |     2
 厨房用具     |     4
(3 行记录)

13.HAVING子句

指定组所对应的条件,一般用于对分组后的数据进行过滤。

示例:

SELECT product_type, COUNT(*) FROM product GROUP BY product_type HAVING COUNT(*)=2;
--按照product_type分组之后,筛选出行数为2的组
 product_type | count
--------------+-------
 衣服         |     2
 办公用品     |     2
(2 行记录)
SELECT product_type, AVG(sale_price) FROM product GROUP BY product_type HAVING AVG(sale_price)>=2500;	--按照product_type分组之后,筛选出平均价格大于等于2500的组
 product_type |          avg
--------------+-----------------------
 衣服         | 2500.0000000000000000
 厨房用具     | 2795.0000000000000000
(2 行记录)

14.ORDER BY子句

对查询结果进行排序。默认升序(关键字ASC)。

ORDER BY子句被称为排序键。

ORDER BY可以指定多个排序键,排序规则为优先使用左侧的键。

排序键中包含NULL时,会在开头或末尾进行汇总。

排序键可以使用别名。

SELECT product_id, product_name, sale_price, purchase_price FROM product ORDER BY sale_price;
 product_name | product_type | sale_price | purchase_price
--------------+--------------+------------+----------------
 圆珠笔       | 办公用品     |        100 |
 叉子         | 厨房用具     |        500 |
 打孔器       | 办公用品     |        500 |            320
 擦菜板       | 厨房用具     |        880 |            790
 T恤衫        | 衣服         |       1000 |            500
 菜刀         | 厨房用具     |       3000 |           2800
 运动T恤      | 衣服         |       4000 |           2800
 高压锅       | 厨房用具     |       6800 |           5000
(8 行记录)
SELECT product_id, product_name, sale_price, purchase_price FROM product ORDER BY sale_price DESC;	--对查询结果进行降序
 product_name | product_type | sale_price | purchase_price
--------------+--------------+------------+----------------
 高压锅       | 厨房用具     |       6800 |           5000
 运动T恤      | 衣服         |       4000 |           2800
 菜刀         | 厨房用具     |       3000 |           2800
 T恤衫        | 衣服         |       1000 |            500
 擦菜板       | 厨房用具     |        880 |            790
 打孔器       | 办公用品     |        500 |            320
 叉子         | 厨房用具     |        500 |
 圆珠笔       | 办公用品     |        100 |
(8 行记录)
SELECT product_id, product_name, sale_price, purchase_price FROM product ORDER BY sale_price, product_id;	--当sale_price相同时,按照product_id排序
 product_name | product_type | sale_price | purchase_price
--------------+--------------+------------+----------------
 圆珠笔       | 办公用品     |        100 |
 打孔器       | 办公用品     |        500 |            320
 叉子         | 厨房用具     |        500 |
 擦菜板       | 厨房用具     |        880 |            790
 T恤衫        | 衣服         |       1000 |            500
 菜刀         | 厨房用具     |       3000 |           2800
 运动T恤      | 衣服         |       4000 |           2800
 高压锅       | 厨房用具     |       6800 |           5000
(8 行记录)
SELECT product_id AS id, product_name, sale_price AS sp, purchase_price FROM product ORDER BY sp, id;	--排序键可以使用sale_price的别名sp和product_id的别名id
  id  | product_name |  sp  | purchase_price 
------+--------------+------+----------------
 0008 | 圆珠笔       |  100 |
 0002 | 打孔器       |  500 |            320
 0006 | 叉子         |  500 |
 0007 | 擦菜板       |  880 |            790
 0001 | T恤衫        | 1000 |            500
 0004 | 菜刀         | 3000 |           2800
 0003 | 运动T恤      | 4000 |           2800
 0005 | 高压锅       | 6800 |           5000
(8 行记录)

练习题

练习题一:

请指出下述SELECT语句中所有的语法错误。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XZQXSuTE-1610353893937)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201215145414493.png)]

WHERE子句是对行进行过滤。如果要对分组后的结果进行过滤,需要使用HAVING子句。

练习题二:

请编写一条SELECT语句,求出销售单价(sale_price列)合计值是进货单价(purchase_price列)合计值1.5倍的商品种类。执行结果如下所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iu9scPuS-1610353893939)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201215145606607.png)]

SELECT prodcut_type, SUM(sale_price), SUM(purchase_price) FROM product GROUP BY product_type HAVING SUM(sale_price)>SUM(purchase_price)*1.5;
 product_type | sum  | sum
--------------+------+------
 衣服         | 5000 | 3300
 办公用品     |  600 |  320
(2 行记录)

练习三:

此前我们曾经使用SELECT语句选取出了Product(商品)表中的全部记录。当时我们使用了ORDER BY子句来指定排列顺序,但现在已经无法记起当时如何指定的了。请根据下列执行结果,思考ORDER BY子句的内容。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5kzbXrUE-1610353893941)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201215161510676.png)]

SELECT * FROM product ORDER BY regist_date DESC;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0003       | 运动T恤      | 衣服         |       4000 |           2800 | 
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-11-11
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-09-20
 0006       | 叉子         | 厨房用具     |        500 |                | 2009-09-20
 0005       | 高压锅       | 厨房用具     |       6800 |           5000 | 2009-09-20
 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11
 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28
(8 行记录)

15.INSERT语句

向表中插入数据(行)。可以插入多行,原则上每次执行插入一行。

语法:INSERT INTO <表明> (列1, 列2, 列3…) VALUES (值1, 值2, 值3…)

DEFAULT关键字:创建表的时候,可以设置DEFAULT约束来设定初始值,在后续插入值的时候,可以不指定该值。

示例:

CREATE TABLE ProductIns(
product_id	CHAR(4)	NOT NULL,
product_name	VARCHAR(100)	NOT NULL,
prodcut_type	VARCHAR(32)	NOT NULL,
sale_price	INTEGER	DEFAULT 0,		--设置该值默认为0
purchase_price	INTEGER,
regist_date	DATE,
PRIMARY KEY (product_id));		--创建表ProductIns
INSERT INTO ProductIns (product_id, product_name, product_type, sale_price, purchase_price, regist_date) VALUES ('0001', 'T恤衫', '衣服', 1000, 500, '2009-09-20'); --插入一行数据(值清单)
INSERT INTO ProductIns VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11'),
('0003', '运动T恤', '衣服', 4000, 2800, NULL),
('0004', '菜刀', '厨房用品', 3000, 2800, '2009-09-20');	--插入多行数据,每个值清单用,隔开
INSERT INTO ProductIns VALUES('0007', '擦菜板', '厨房用具', DEFAULT, 790, '2009-04-28');	--通过DEFAULT关键字插入默认值
SELECT product_id, product_name, sale_price FROM ProductIns WHERE product_id='0007';
 product_id | product_name | sale_price
------------+--------------+------------
 0007       | 擦菜板       |          0
(1 行记录)
CREATE TABLE ProductCopy(
product_id       CHAR(4) NOT NULL,
product_name     VARCHAR(100)    NOT NULL,
product_type     VARCHAR(32)     NOT NULL,
sale_price       INTEGER,
purchase_price   INTEGER,
regist_date      DATE,
PRIMARY  KEY (product_id));		--创建表ProductCopy
INSERT INTO ProductCopy(product_id, product_name, product_type, sale_price, purchase_price, regist_date) SELECT product_id, product_name, product_type, sale_price, purchase_price, regist_date FROM product;
--将product表中的数据复制黏贴到ProductCopy表中
SELECT * FROM ProductCopy;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |                | 2009-09-20
 0002       | 打孔器       | 办公用品     |        500 |                | 2009-09-11
 0003       | 运动T恤      | 衣服         |       4000 |                | 
 0004       | 菜刀         | 厨房用具     |       3000 |                | 2009-09-20
 0005       | 高压锅       | 厨房用具     |       6800 |                | 2009-09-20
 0006       | 叉子         | 厨房用具     |        500 |                | 2009-09-20
 0007       | 擦菜板       | 厨房用具     |        880 |                | 2008-04-28
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-11-11
(8 行记录)

16.DELETE语句

删除数据(行)。DELETE

会删除所有行,留下表头,而DROP
会删除整张表。

语法:DELETE FROM <表名> WHERE <条件>

示例:

DELETE FROM product WHERE sale_price>=4000;	--删除sale_price大于等于4000的行
SELECT * FROM product;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-09-20
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11
 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20
 0006       | 叉子         | 厨房用具     |        500 |                | 2009-09-20
 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-11-11
(6 行记录)

17.UPDATE语句

更改(更新)表中的数据。可以使用WHERE子句和SET子句,SET子句可用于更新多列数据。

语法:UPDATE <表名> SET <列名>=<表达式> WEHERE <条件>;

使用UPDATE语句可以将值清空为NULL(但只限于未设置NOT NULL约束的列)。

示例:

UPDATE product SET regist_date='2009-10-10';	--修改regist_date列的数据为“2009-10-10”
SELECT * FROM product ORDER BY poroduct_id;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-10-10
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-10-10
 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-10-10
 0006       | 叉子         | 厨房用具     |        500 |                | 2009-10-10
 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2009-10-10
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-10-10
(6 行记录)
UPDATE product SET sale_price=sale_price*10 WHERE product_type='厨房用具';
SELECT * FROM product ORDER BY product_id;
shop=# SELECT * FROM product ORDER BY product_id;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-10-10
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-10-10
 0004       | 菜刀         | 厨房用具     |      30000 |           2800 | 2009-10-10
 0006       | 叉子         | 厨房用具     |       5000 |                | 2009-10-10
 0007       | 擦菜板       | 厨房用具     |       8800 |            790 | 2009-10-10
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-10-10
(6 行记录)
UPDATE product SET sale_price=sale_price*10, purchase_price=purchase_price/2 WHERE product_type='厨房用具';		--更新多列数据
SELECT * FROM product ORDER BY product_id;
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-10-10
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-10-10
 0004       | 菜刀         | 厨房用具     |     300000 |           1400 | 2009-10-10
 0006       | 叉子         | 厨房用具     |      50000 |                | 2009-10-10
 0007       | 擦菜板       | 厨房用具     |      88000 |            395 | 2009-10-10
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-10-10
(6 行记录)

18.事务

语法:

​ 事务开始语句;

​ DML语句①;

​ DML语句②;

​ DML语句③;

​ …

​ 事务结束语句;

数据库事务开始语句
MSSQL、PLSQLBEGIN TRANSACTION;
MYSQLSTART TRANSACTION;
ORACLE、DB2
事务结束语句含义
COMMIT提交事务
ROLLBACK回滚

DBMS的ACID特性

特性含义
Atomicity(原子性)事务结束时,其中所包含的更新处理要么全部执行,要么完全不执行,也就是要么占有一切要么一无所有。
Consisty(一致性)事务中包含的处理要满足数据库提前设置的约束,如主键约束或者NOT NULL约束等。
Isolation(隔离性)保证不同事务之间互不干扰的特性。该特性保证了事务之间不会互相嵌套。此外,在某个事务中进行的更改,在该事务结束之前,对其他事务而言是不可见的。因此,即使某个事务向表中添加了记录,在没有提交之前,其他事务也是看不到新添加的记录的。
Durability(持久性)在事务(不论是提交还是回滚)结束后,DBMS能够保证该时间点的数据状态会被保存的特性。即使由于系统故障导致数据丢失,数据库也一定能通过某种手段进行恢复。练习题

练习题

练习题一:

A先生在自己的计算机(电脑)上,使用CREATE TABLE语句创建出了一张空的Product(商品)表,并执行了如下的SQL语句向其中插入数据。

GEBIN TRANACTION:
	INSERT INTO Product VALUES(
	'0001', 'T恤衫', '衣服', 1000, 500, '2008-09-20');
	INSERT INTO Product VALUES(
	'0002', '打孔器', '办公用品', 500, 320, '2008-09-11');
	INSERT INTO Product VALUES(
	'0003', '运动T恤', '衣服', 4000, 2800, NULL);

紧接着,B先生使用其他的计算机连接上该数据库,执行了如下SELECT语句。这时B先生能得到怎样的查询结果呢?

SELECT * FROM Product;

无结果。由于DBMS的隔离性,当前的事务没有结束之前,对其他事务是不可见的。

练习题二:

如下所示,有一张包含3条记录的Product表

商品编号商品名称商品种类销售单价进货单价登记日期
0001T恤衫衣服10005002009-09-20
0002打孔器办公用品5003202009-09-11
0003运动T恤衣服400002800

使用如下的INSERT语句复制这3行数据,应该能将表中的数据增加为6行,请说出该语句的执行结果。

INSERT INTO Product SELECT * FROM Product;

结果只包含3条记录,因为主键重复。该操作违反了DBMS的一致性,事务的改变需要遵循原表的约束。

练习题三:

以练习二的Product表为基础,再创建另外一张包含利润列的新表ProductMargin(商品利润)。

--商品利润表
CREATE TABLE ProductMargin(
product_id CHAR(4) NOT NULL,
product_name VARCHAR(100) NOT NULL,
sale_price INTEGER,
pruchase_price INTEGER,
margin INTEGER,
PRIMARY KEY(product_id));

请写出上述表中插入如下数据的SQL语句,其中的利润可以简单的通过Product表中的数据进行计算(销售单价-进货单价)得出。

product_idproduct_namesale_pricepurchase_pricemargin
0001T恤衫1000500500
0002打孔器500320180
0003运动T恤400028001200
INSERT INTO ProductMargin(
product_id, product_name, sale_price, purchase_price, margin)
SELECT product_id, product_name, sale_price, purchase_price, sale_price-purchase_price FROM Product;

练习题四:

对练习三中的ProductMargin表的数据进行如下更改。

  • 将运动T恤的销售单价从4000日元下调至3000日元。
  • 根据上述结果再次计算运动T恤的利润。

更改后的ProductMargin表如下所示。请写出能够实现该变更的SQL语句。

product_idproduct_namesale_pricepurchase_pricemargin
0001T恤衫1000500500
0002打孔器500320180
0003运动T恤30002800200
--下调销售单价
UPDATE ProductMargin SET sale_price=3000 WHERE product_name='运动T恤';
--重新计算利润
UPDATE ProductMargin SET margin=saleprice-purchase_price;

19.视图

  • 视图中保存的是SELECT语句,表中保存的是实际的数据。
  • 使用视图可以完成跨表查询数据等操作。
  • 创建试图需要使用CREATE VIEW语句,删除需要使用DROP VIEW语句。
  • 视图中不能使用ORDER BY语句。
  • 表中数据更新之后,视图也会自动更新。
  • 如果要更新视图,需要对
--创建productsum(商品类型总计)视图
CREATE VIEW productsum(product_type, cnt_product) AS
SELECT product_type, count(*) FROM product GROUP BY product_type;
--查询视图
SELECT product_type, cnt_product FROM productsum;
 product_type | cnt_product
--------------+-------------
 衣服         |           2
 办公用品     |           2
 厨房用具     |           4
(3 行记录)

20.子查询

  • 子查询是一次性视图(SELECT语句),与视图不同,子查询在查询结束后就会消失。
  • 子查询需要命名。
  • 标量子查询是返回单一值的子查询。
  • 关联子查询会在细分的组内进行比较时使用。结合条件如果未出现在子查询之中就会发生错误。
--子查询
SELECT product_type, cnt_product FROM (SELECT product_type, COUNT(*) AS cnt_product FROM product GROUP BY product_type) AS productsum;
 product_type | cnt_product
--------------+-------------
 衣服         |           2
 办公用品     |           2
 厨房用具     |           4
(3 行记录)
--WHERE子句中不能使用聚合函数
SELECT product_id, product_name, sale_price FROM product WHERE sale_price>AVG(sale_price);
2020-12-20 14:37:00.600 HKT [3536] 错误:  聚合函数不允许出现在WHERE中 第 75 个字符处
2020-12-20 14:37:00.600 HKT [3536] 语句:  SELECT product_id, product_name, sale_price FROM product WHERE sale_price>AVG(sale_price);
错误:  聚合函数不允许出现在WHERE中
--可以使用标量子查询代替聚合函数
SELECT product_id, product_name, sale_price FROM product WHERE sale_price>(SELECT AVG(sale_price) FROM product); 
 product_id | product_name | sale_price
------------+--------------+------------
 0003       | 运动T恤      |       4000
 0005       | 叉子         |       6800
 0004       | 菜刀         |       3000
(3 行记录)
--HAVING子句中也可以使用标量子查询
SELECT product_type, AVG(sale_price) FROM product GROUP BY product_type HAVING AVG(sale_price)>(SELECT AVG(sale_price) FROM product);
 product_type |          avg
--------------+-----------------------
 衣服         | 2500.0000000000000000
 厨房用具     | 2795.0000000000000000
(2 行记录)
--当子查询返回多条(多行多列)数据时,会报错
SELECT product_id, product_name, sale_price FROM product WHERE sale_price>(SELECT AVG(sale_price) FROM product GROUP BY product_type);
2020-12-20 15:01:31.205 HKT [3536] 错误:  作为一个表达式使用的子查询返回了多列
2020-12-20 15:01:31.205 HKT [3536] 语句:  SELECT product_id, product_name, sale_price FROM product WHERE sale_price>(SELECT AVG(sale_price) FROM product GROUP BY product_type);
错误:  作为一个表达式使用的子查询返回了多列
--以上情况可以使用关联子查询
SELECT product_type, product_name, sale_price FROM product AS p1 WHERE sale_price>(SELECT AVG(sale_price) FROM product AS p2 WHERE p1.product_type=p2.product_type GROUP BY product_type);
 product_type | product_name | sale_price
--------------+--------------+------------
 衣服         | 运动T恤      |       4000
 办公用品     | 打孔器       |        500
 厨房用具     | 叉子         |       6800
 厨房用具     | 菜刀         |       3000
(4 行记录)

练习题

练习题一:

创建出满足下述三个条件的视图(视图名称为ViewPractice5_1)。使用Product(商品)表作为参照表,假设表中包含初始状态的8行数据。

  • 条件1:销售单价大于等于1000日元。

  • 条件2:登记日期是2009年9月20日。

  • 条件3:包含商品名称、销售单价和登记日期三列。

CREATE VIEW ViewPractice5_1 AS SELECT product_name, sale_price, regist_date FROM product WHERE sale_price>=1000 AND regist_date='2009-09-20';
SELECT * FROM ViewPractice5_1;
 product_name | sale_price | regist_date
--------------+------------+-------------
 T恤衫        |       1000 | 2009-09-20
 菜刀         |       3000 | 2009-09-20
(2 行记录)

练习题二:

向练习题一中创建的视图ViewPractice5_1中插入如下数据,会得到什么样的结果呢?

INSERT INTO ViewPractice5_1 VALUES('刀子', 300, '2009-11-02');

对视图的更新归根结底是对视图所对应的表进行更新。因此,该INSERT语句实质上和下面的INSERT语句相同。

INSERT INTO product VALUES(NULL, '刀子', NULL, 300, NULL, '2009-11-02');

而product_id,product_type都设置了NOT NULL的约束,因此,插入以上数据会发生错误。

练习题三:

编写SELECT语句,其中sale_price_all列为全部商品的平均销售单价。

SLECT product_id, product_name, product_type, sale_price, (SELECT AVG(sale_price) FROM product) AS sale_price_all FROM product ORDER BY product_id;
 product_id | product_name | product_type | sale_price |    sale_price_all
------------+--------------+--------------+------------+-----------------------
 0001       | T恤衫        | 衣服         |       1000 | 2097.5000000000000000
 0002       | 打孔器       | 办公用品     |        500 | 2097.5000000000000000
 0003       | 运动T恤      | 衣服         |       4000 | 2097.5000000000000000
 0004       | 菜刀         | 厨房用具     |       3000 | 2097.5000000000000000
 0005       | 叉子         | 厨房用具     |       6800 | 2097.5000000000000000
 0006       | 叉子         | 厨房用具     |        500 | 2097.5000000000000000
 0007       | 擦菜板       | 厨房用具     |        880 | 2097.5000000000000000
 0008       | 圆珠笔       | 办公用品     |        100 | 2097.5000000000000000
(8 行记录)

练习题四:

根据练习题一的条件编写一条SQL语句,创建一幅包含如下数据的视图(名称为AvgPriceByType)。

CREATE VIEW AvgPriceType AS SELECT product_id, product_name, product_type, sale_price, (SELECT AVG(sale_price) FROM product AS p2 WHERE p1.product_type=p2.product_type GROUP BY product_type) AS avg_sale_price FROM product AS p1 ORDER BY product_id;
SELECT * FROM AvgPriceType;
 product_id | product_name | product_type | sale_price |    avg_sale_price
------------+--------------+--------------+------------+-----------------------
 0001       | T恤衫        | 衣服         |       1000 | 2500.0000000000000000
 0002       | 打孔器       | 办公用品     |        500 |  300.0000000000000000
 0003       | 运动T恤      | 衣服         |       4000 | 2500.0000000000000000
 0004       | 菜刀         | 厨房用具     |       3000 | 2795.0000000000000000
 0005       | 叉子         | 厨房用具     |       6800 | 2795.0000000000000000
 0006       | 叉子         | 厨房用具     |        500 | 2795.0000000000000000
 0007       | 擦菜板       | 厨房用具     |        880 | 2795.0000000000000000
 0008       | 圆珠笔       | 办公用品     |        100 |  300.0000000000000000
(8 行记录)

21.函数

数学函数

函数含义
ABS(数值)数值的绝对值
MOD(被除数, 除数)计算余数(SQL SERVER不支持该函数)
SQL SERVER使用%取余数
ROUND(对象数值, 保留小数的位数)四舍五入操作
--创建SampleMath表,表中内容如下:
SELECT * FROM SampleMath;
    m     | n | p
----------+---+---
  500.000 | 0 |
 -180.000 | 0 |  
          |   |
          | 7 | 3
          | 5 | 2
          | 4 |
    8.000 |   | 3
    2.270 | 1 |
    5.555 | 2 |
          | 1 |
    8.760 |   |
(11 行记录)
--ABS示例
SELECT m, ABS(m) AS abs_col FROM SampleMath;
    m     | abs_col
----------+---------
  500.000 | 500.000
 -180.000 | 180.000
          |
          |
          |
          |
    8.000 |   8.000
    2.270 |   2.270
    5.555 |   5.555
          |
    8.760 |   8.760
(11 行记录)
--MOD示例
SELECT n, p, MOD(n, p) AS mod_col FROM SampleMath;
 n | p | mod_col
---+---+---------
 0 |   |
 0 |   |        
   |   |
 7 | 3 |       1	--MOD(7, 3)=1
 5 | 2 |       1	--MOD(5, 2)=1
 4 |   |
   | 3 |
 1 |   |
 2 |   |
 1 |   |
   |   |
(11 行记录)
--ROUND示例
SELECT m, n, ROUND(m, n) AS round_col FROM SampleMath;
    m     | n | round_col
----------+---+-----------
  500.000 | 0 |       500		--500.000四舍五入,取小数点后0位
 -180.000 | 0 |      -180		
          |   |
          | 7 |
          | 5 |
          | 4 |
    8.000 |   |
    2.270 | 1 |       2.3		--2.270四舍五入,取小数点后1位
    5.555 | 2 |      5.56		--5.555四舍五入,取小数点后两位
          | 1 |
    8.760 |   |
(11 行记录)

字符串函数

函数含义
str1||str2str1和str2拼接
SQL SERVER使用“+”拼接
MYSQL及SQL SERVER 2012之后的版本使用CONCAT函数拼接
CONCAT(str1, str2, str3)
LENGTH(str)显示srt的长度
SQL SERVER使用LEN函数
LOWER(str)将str中的字母全部转换位小写
UPPER(str)将str中的字母全部转换位大写
REPLACE(对象字符串, 替换前的字符串, 替换后的字符串)将对象字符串中包含替换前的字符串替换为替换后的字符串
SUBSTRING(对象字符串 FROM 截取的起始位置 FOR截取的字符数)在字符串的指定位置,截取指定长度的字符串
--创建SampleStr表,表中内容如下:
SELECT * FROM SampleStr;
   str1    | str2 | str3
-----------+------+------
 opx       | rt   |
 abc       | def  |
 山田      | 太郎 | 是我
 aaa       |      |
           | xyz  |
 @!#$%     |      |
 ABC       |      |
 aBC       |      |
 abc太郎   | abc  | ABC
 abcdefabc | abc  | ABC
 micmic    | i    | I
(11 行记录)
--拼接函数示例
SELECT str1, str2, str1||str2 AS str_concat FROM SampleStr;
   str1    | str2 |  str_concat
-----------+------+--------------
 opx       | rt   | opxrt
 abc       | def  | abcdef
 山田      | 太郎 | 山田太郎
 aaa       |      |
           | xyz  |
 @!#$%     |      |
 ABC       |      |
 aBC       |      |
 abc太郎   | abc  | abc太郎abc
 abcdefabc | abc  | abcdefabcabc
 micmic    | i    | micmici
(11 行记录)
--LENGTH函数示例
SELECT str1, LENGTH(str1) AS len_str FROM SampleStr;
   str1    | len_str
-----------+---------
 opx       |       3
 abc       |       3
 山田      |       2
 aaa       |       3
           |
 @!#$%     |       5
 ABC       |       3
 aBC       |       3
 abc太郎   |       5
 abcdefabc |       9
 micmic    |       6
(11 行记录)
--LOWER函数示例
SELECT str1, LOWER(str1) AS low_str FROM SampleStr WHERE str1 IN ('ABC', 'aBC', 'abc', '山田');
 str1 | low_str
------+---------
 abc  | abc
 山田 | 山田
 ABC  | abc
 aBC  | abc
(4 行记录)
--REPLACE函数示例
SELECT str1, str2, str3, REPLACE(str1, str2, str3) AS rep_str FROM SampleStr; 
   str1    | str2 | str3 |  rep_str
-----------+------+------+-----------
 opx       | rt   |      |
 abc       | def  |      |
 山田      | 太郎 | 是我 | 山田
 aaa       |      |      |
           | xyz  |      |
 @!#$%     |      |      |
 ABC       |      |      |
 aBC       |      |      |
 abc太郎   | abc  | ABC  | ABC太郎			--abc太郎中的abc替换为ABC
 abcdefabc | abc  | ABC  | ABCdefABC		--abcdefabc中的abc替换为ABC
 micmic    | i    | I    | mIcmIc			--micmicz中的i替换为I
(11 行记录)
--SUBSTRING函数示例
SELECT str1, SUBSTRING(str1 FROM 3 FOR 2) AS sub_str FROM SampleStr;
   str1    | sub_str
-----------+---------
 opx       | x
 abc       | c
 山田      |
 aaa       | a
           |
 @!#$%     | #$
 ABC       | C
 aBC       | C
 abc太郎   | c太
 abcdefabc | cd
 micmic    | cm
(11 行记录)

日期函数

函数含义
CURRENT_DATE当前日期
CURRENT_TIME当前时间
CURRENT_TIMESTAMP当前日期和时间
EXTRACT(日期元素 FROM 日期)从日期中取出指定的日期元素
--示例
SELECT CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP; 
 current_date |    current_time    |       current_timestamp
--------------+--------------------+-------------------------------
 2020-12-20   | 23:30:22.804072+08 | 2020-12-20 23:30:22.804072+08
(1 行记录)
SELECT CURRENT_TIMESTAMP AS now_time,
EXTRACT(YEAR FROM CURRENT_TIMESTAMP) AS year,     
EXTRACT(MONTH FROM CURRENT_TIMESTAMP) AS month,
EXTRACT(DAY FROM CURRENT_TIMESTAMP) AS day,
EXTRACT(HOUR FROM CURRENT_TIMESTAMP) AS hour,
EXTRACT(MINUTE FROM CURRENT_TIMESTAMP) AS minute,
EXTRACT(SECOND FROM CURRENT_TIMESTAMP) AS second;
           now_time            | year | month | day | hour | minute |  second
-------------------------------+------+-------+-----+------+--------+-----------
 2020-12-20 23:44:16.770124+08 | 2020 |    12 |  20 |   23 |     44 | 16.770124
(1 行记录)

转换函数

函数含义
CAST(转换前的值 AS 想要转换的数据类型)类型转换,将指定的值转换为别的数据类型
COALESCE(数据1, 数据2, 数据3…)返回参数中左侧开始第一个不是NULL的值,可以用来将NULL转换为指定的字符串
--示例
ELECT COALESCE(NULL, 1) AS col_1,            
COALESCE(NULL, 'test', NULL) AS col_2,        
COALESCE(NULL, NULL, '2009-11-01') AS col_3;  
 col_1 | col_2 |   col_3
-------+-------+------------
     1 | test  | 2009-11-01
(1 行记录)
SELECT COALESCE(str2, 'not_null') FROM SampleStr; 
 coalesce
----------
 rt
 def
 太郎
 not_null			--NULL被转换为'not_null'
 xyz
 not_null			--NULL被转换为'not_null'
 not_null			--NULL被转换为'not_null'
 not_null			--NULL被转换为'not_null'
 abc
 abc
 i
(11 行记录)

22.谓词

谓词的作用就是“判断是否存在满足某种条件的记录”。如果存在这样的记录就返回指定值,如果不存在就返回假(FALSE)。

谓词含义
LIKE(字符串的部分一致查询)查询包含某些字符的数据。常与通配符搭配使用
百分号%:匹配一个或多个字符
下划线_:匹配一个字符
BETWEEN(范围查询)选取介于两个值之间的数据范围内的值,值可以是数值、文本、日期
IS NULL、IS NOT NULL判断是否为NULL
IN查询范围内的多个值,一般用于WHERE子句中,相当与OR。可以用于子查询
EXISTS、NOT EXISTS与IN含义一样,可以用于子查询

示例:

--创建SampleLike表
CREATE TABLE SampleLike(
strcol NOT NULL,
PRIMARY KEY(strcol));
--插入数据
BEGIN TRANSACTION;
INSERT INTO SampleLike(strcol) VALUES('abcddd');
INSERT INTO SampleLike(strcol) VALUES('dddabc');
INSERT INTO SampleLike(strcol) VALUES('abdddc');
INSERT INTO SampleLike(strcol) VALUES('abcdd');
INSERT INTO SampleLike(strcol) VALUES('ddabc');
INSERT INTO SampleLike(strcol) VALUES('abddc');
COMMIT;
--LIKE示例
SELECT * FROM SampleLike LIKE 'ddd%';
 strcol
--------
 dddabc
(1 行记录)
SELECT * FROM SampleLike LIKE '%ddd%';
 strcol
--------
 abcddd
 dddabc
 abdddc
 SELECT * FROM SampleLike LIKE '%ddd';
 strcol
--------
 abcddd
(1 行记录)
SELECT * FROM SampleLike LIKE 'abc__';
 strcol
--------
 abcdd
(1 行记录)
SELECT * FROM SampleLike WHERE strcol LIKE 'abc___'; 
 strcol
--------
 abcddd
(1 行记录)
--BETWEEN示例
SELECT product_name, sale_price FROM product WHERE sale_price BETWEEN 100 AND 1000; 
 product_name | sale_price
--------------+------------
 T恤衫        |       1000
 打孔器       |        500
 圆珠笔       |        100
 擦菜板       |        880
 叉子         |        500
(5 行记录)
--IS NULL和IS NOT NULL示例
SELECT product_name, purchase_price FROM product WHERE purchase_price IS NULL;
 product_name | purchase_price
--------------+----------------
 圆珠笔       |
 叉子         |
(2 行记录)
SELECT product_name, purchase_price FROM product WHERE purchase_price IS NOT NULL; 
 product_name | purchase_price
--------------+----------------
 运动T恤      |           2800
 T恤衫        |            500
 打孔器       |            320
 叉子         |           5000
 菜刀         |           2800
 擦菜板       |            790
(6 行记录)
--IN示例
SELECT product_name, purchase_price FROM product WHERE purchase_price IN (320, 500, 5000);                             
 product_name | purchase_price
--------------+----------------
 T恤衫        |            500
 打孔器       |            320
 叉子         |           5000
(3 行记录)
--IN子查询示例
--创建表
CREATE TABLE ShopProduct(         
    shop_id CHAR(4) NOT NULL,
    shop_name VARCHAR(200) NOT NULL,
    product_id CHAR(4) NOT NULL,
    quantity INTEGER NOT NULL,
    PRIMARY KEY (shop_id, product_id));
BEGIN TRANSACTION;
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000A', '东京', '0001', 30);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000A', '东京', '0002', 50);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000A', '东京', '0003', 15);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000B', '名古屋', '0002', 20);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000B', '名古屋', '0003', 120);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000B', '名古屋', '0004', 20);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000B', '名古屋', '0006', 10);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000B', '名古屋', '0007', 40);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000C', '大阪', '0003', 20);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000C', '大阪', '0004', 50);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000C', '大阪', '0006', 90);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000C', '大阪', '0007', 70);
INSERT INTO ShopProduct(shop_id, shop_name, product_id, quantity) VALUES('000D', '福冈', '0001', 100);
COMMIT;
SELECT product_name, sale_price FROM product WHERE product_id IN (SELECT product_id FROM ShopProduct WHERE shop_id='000A');
--EXIST示例
SELECT product_name, sale_price FROM product AS p WHERE EXISTS (SELECT product_id FROM ShopProduct AS sp WHERE sp.shop_id='000C' AND sp.product_id=p.product_id);

23.CASE

语法:

​ CASE WHEN <求值表达式> THEN <表达式>

​ WHEN <求值表达式> THEN <表达式>

​ …

​ ELSE <表达式>

​ END

示例:

--CASE示例
SELECT product_type,
	CASE WHEN product_type='衣服' THEN ’A:‘||product_type
		 WHEN product_type='办公用品' THEN 'B:'||product_type
		 WHEN PRODUCT_TYPE='厨房用具' THEN 'C:'||product_type
		ELSE NULL
	END AS abc_product_type
FROM product;
 product_name | abc_product_type
--------------+------------------
 运动T恤      | A:衣服
 T恤衫        | A:衣服
 打孔器       | B:办公用品
 叉子         | C:厨房用具
 圆珠笔       | B:办公用品
 菜刀         | C:厨房用具
 擦菜板       | C:厨房用具
 叉子         | C:厨房用具
(8 行记录)
--使用CASE进行行列转换
SELECT product_type, SUM(sale_price) AS sum_sale_price FROM product GROUP BY product_type;
 product_type | sum_sale_price
--------------+----------------
 衣服         |           5000
 办公用品     |            600
 厨房用具     |          11180
(3 行记录)
SELECT SUM(CASE WHEN product_type='衣服' THEN sale_price ELSE 0 END) AS sum_clothes,
SUM(CASE WHEN product_type='办公用品' THEN sale_price ELSE 0 END) AS sum_offce,
SUM(CASE WHEN product_type='厨房用具' THEN sale_price ELSE 0 END) AS sum_kitchen FROM product;
 sum_clothes | sum_offce | sum_kitchen
-------------+-----------+-------------
        5000 |       600 |       11180
(1 行记录)
--简单CASE表达式
SELECT product_type, CASE product_type
	WHEN '衣服' THEN ’A:‘||product_type
	WHEN '办公用品' THEN 'B:'||product_type
	WHEN '厨房用具' THEN 'C:'||product_type
	ELSE NULL
	END AS abc_product_type
FROM product;
 product_type | abc_product_type
--------------+------------------
 衣服         | A:衣服
 衣服         | A:衣服
 办公用品     | B:办公用品
 厨房用具     | C:厨房用具
 办公用品     | B:办公用品
 厨房用具     | C:厨房用具
 厨房用具     | C:厨房用具
 厨房用具     | C:厨房用具
(8 行记录)

练习题

练习题一:

对本章中使用的Product(商品)表执行如下2条SELECT语句,能够得到什么样的结果呢?

SELECT product_name, purchase_price FROM product WHERE purchase_price NOT IN (500, 2800, 5000);
SELECT product_name, purchase_price FROM product WHERE purchase_price NOT IN (500, 2800, 5000, NULL);

第一条SELECT语句可以得出正常结果,第二条为空,应为对于NULL的判断应该使用IS NOT NULL。

练习题二:

按照销售单价(sale_price)对练习题一中的Product(商品)表中的商品进行如下分类。

​ ● 低档商品:销售单价在1000日元以下(T恤衫、办公用品、叉子、擦菜板、圆珠笔)

​ ● 中档商品:销售单价在1001日元以上3000日元以下(菜刀)

​ ● 高档商品:销售单价在3001日元以上(运动T恤、高压锅)

请编写出统计上述商品种类中所包含的商品数量的SELECT语句,结果如下所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s6EiJt6z-1610353893942)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201225003117185.png)]

SELECT SUM(CASE WHEN sale_price<=1000 THEN 1 ELSE 0 END) AS low_price,
SUM(CASE WHEN sale_price BETWEEN 1001 AND 3000 THEN 1 ELSE 0 END) AS mid_price,
SUM(CASE WHEN sale_price>=3000 THEN 1ELSE 0 END) as high_price FROM product;

24.集合运算

分类运算符含义
以行为单位的表运算UNION表的并集
默认会去重,UNION ALL可以保留重复项
以行为单位的表运算INTERSET表的交集
以行为单位的表运算EXCEPT表的差集
MYSQL不支持,Oracle使用MINUS
以列为单位的表运算INNER JOIN根据连接谓词结合两个表(table1 和 table2)的列值来创建一个新的结果表。查询会把 table1 中的每一行与 table2 中的每一行进行比较,找到所有满足连接谓词的行的匹配对。
内联结只能取出同时存在于两张表中的数据
在FROM子句中使用多张表
ON为联结键,指定多张表连接所使用的列,必须要书写在FROM和WHERE之间,不可省略。
使用联结时,需要按照“<表的别名>.<列名>”的格式
以列为单位的表运算OUTER JOIN首先,执行内部连接。然后,对于表 T1 中不满足表 T2 中任何行连接条件的每一行,如果 T2 的列中有 null 值也会添加一个到结果中。此外,对于 T2 中不满足与 T1 中的任何行连接条件的每一行,将会添加 T1 列中包含 null 值的到结果中。
由LEFT和RIGHT指定主表,使用LEFT时,FROM子句中写在左侧的表为主表,使用RIGHT时,右侧的表为主表。
以列为单位的表运算CROSS JOIN又被称为外联结,一般不用于实际业务中。
第一个表的每一行与第二个表的每一行进行匹配。如果两个输入表分别有 x 和 y 行,则结果表有 x*y 行。

示例:

--创建表product2
CREATE TABLE product2(
product_id CHAR(4) NOT NULL,
product_name VARCHAR(100) NOT NULL,
product_type VARCHAR(32) NOT NULL,
sale_price INTEGER,
purchase_price INTEGER,
regist_date DATE,
RIMARY KEY (product_id));
--表中插入数据
BEGIN TRANSACTION;
INSERT INTO product2 VALUES('0001', 'T恤衫', '衣服', 1000, 500, '2008-09-20');
INSERT INTO product2 VALUES('0002', '打孔器', '办公用品', 500, 320, '2009-09-11');
INSERT INTO product2 VALUES('0003', '运动T恤', '衣服', 4000, 2800, NULL); 
INSERT INTO product2 VALUES('0009', '手套', '衣服', 800, 500, NULL);
INSERT INTO product2 VALUES('0010', '水浒', '厨房用具', 2000, 1700, '2009-09-20');
COMMIT;
--UNION用法示例
SELECT product_id, product_name FROM product UNION SELECT product_id, product_name FROM product2 ORDER BY product_id;
 product_id | product_name			--查询结果可以看出,UNION默认会去重
------------+--------------
 0001       | T恤衫
 0002       | 打孔器
 0003       | 运动T恤
 0004       | 菜刀
 0005       | 叉子
 0006       | 叉子
 0007       | 擦菜板
 0008       | 圆珠笔
 0009       | 手套
 0010       | 水浒
(10 行记录)
SELECT product_id, product_name FROM product UNION ALL SELECT product_id, product_name FROM product2 ORDER BY product_id; 	--ALL选项不会去重
 product_id | product_name
------------+--------------
 0001       | T恤衫
 0001       | T恤衫
 0002       | 打孔器
 0002       | 打孔器
 0003       | 运动T恤
 0003       | 运动T恤
 0004       | 菜刀
 0005       | 叉子
 0006       | 叉子
 0007       | 擦菜板
 0008       | 圆珠笔
 0009       | 手套
 0010       | 水浒
(13 行记录)
--INTERSECT用法示例
SELECT product_id, product_name FROM product INTERSECT SELECT product_id, product_name FROM product2 ORDER BY product_id;
 product_id | product_name
------------+--------------
 0001       | T恤衫
 0002       | 打孔器
 0003       | 运动T恤
(3 行记录)
--EXCEP用法示例
SELECT product_id, product_name FROM product EXCEPT SELECT product_id, product_name FROM product2 ORDER BY product_id;
 product_id | product_name
------------+--------------
 0004       | 菜刀
 0005       | 叉子
 0006       | 叉子
 0007       | 擦菜板
 0008       | 圆珠笔
(5 行记录)
/*INNER JION用法示例
两张表内联结(shopproduct和product)*/
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.prodcut_name, P.sale_price FROM shopproduct AS SP INNER JOIN product AS P ON SP.product_id=P.product_id;
 shop_id | shop_name | product_id | product_name | sale_price
---------+-----------+------------+--------------+------------
 000A    | 东京      | 0001       | T恤衫        |       1000
 000A    | 东京      | 0002       | 打孔器       |        500
 000A    | 东京      | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0002       | 打孔器       |        500
 000B    | 名古屋    | 0006       | 叉子         |        500
 000B    | 名古屋    | 0007       | 擦菜板       |        880
 000C    | 大阪      | 0003       | 运动T恤      |       4000
 000C    | 大阪      | 0004       | 菜刀         |       3000
 000C    | 大阪      | 0006       | 叉子         |        500
 000C    | 大阪      | 0007       | 擦菜板       |        880
 000D    | 福冈      | 0001       | T恤衫        |       1000
 000B    | 名古屋    | 0004       | 菜刀         |       3000
(13 行记录)
--三张表内联结(shopproduct、product、inventoryproduct)
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price, IP.inventory_quantity FROM shopproduct AS SP INNER JOIN product AS P ON SP.product_id=P.product_id INNER JOIN inventoryproduct AS IP ON SP.product_id=IP.product_id WHERE IP.inventory_id='P001';
 shop_id | shop_name | product_id | product_name | sale_price | inventory_quantity
---------+-----------+------------+--------------+------------+--------------------
 000A    | 东京      | 0001       | T恤衫        |       1000 |                  0
 000A    | 东京      | 0002       | 打孔器       |        500 |                120
 000A    | 东京      | 0003       | 运动T恤      |       4000 |                200
 000B    | 名古屋    | 0003       | 运动T恤      |       4000 |                200
 000B    | 名古屋    | 0002       | 打孔器       |        500 |                120
 000B    | 名古屋    | 0006       | 叉子         |        500 |                 99
 000B    | 名古屋    | 0007       | 擦菜板       |        880 |                999
 000C    | 大阪      | 0003       | 运动T恤      |       4000 |                200
 000C    | 大阪      | 0004       | 菜刀         |       3000 |                  3
 000C    | 大阪      | 0006       | 叉子         |        500 |                 99
 000C    | 大阪      | 0007       | 擦菜板       |        880 |                999
 000D    | 福冈      | 0001       | T恤衫        |       1000 |                  0
 000B    | 名古屋    | 0004       | 菜刀         |       3000 |                  3
(13 行记录)
--OUTER JOIN用法示例
--RIGHT OUTER JOIN
SELECT SP.shop_id, SP.shop_name, P.product_id, P.product_name, P.sale_price FROM shopproduct AS SP RIGHT OUTER JOIN product AS P ON SP.product_id=P.product_id;
 shop_id | shop_name | product_id | product_name | sale_price
---------+-----------+------------+--------------+------------
 000A    | 东京      | 0001       | T恤衫        |       1000
 000A    | 东京      | 0002       | 打孔器       |        500
 000A    | 东京      | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0002       | 打孔器       |        500
 000B    | 名古屋    | 0006       | 叉子         |        500
 000B    | 名古屋    | 0007       | 擦菜板       |        880
 000C    | 大阪      | 0003       | 运动T恤      |       4000
 000C    | 大阪      | 0004       | 菜刀         |       3000
 000C    | 大阪      | 0006       | 叉子         |        500
 000C    | 大阪      | 0007       | 擦菜板       |        880
 000D    | 福冈      | 0001       | T恤衫        |       1000
 000B    | 名古屋    | 0004       | 菜刀         |       3000
         |           | 0008       | 圆珠笔       |        100
         |           | 0005       | 叉子         |       6800
(15 行记录)
--LEFT OUTER JOIN
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price FROM shopproduct AS SP LEFT OUTER JOIN product AS P ON SP.product_id=P.product_id;
 shop_id | shop_name | product_id | product_name | sale_price
---------+-----------+------------+--------------+------------
 000A    | 东京      | 0001       | T恤衫        |       1000
 000A    | 东京      | 0002       | 打孔器       |        500
 000A    | 东京      | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0002       | 打孔器       |        500
 000B    | 名古屋    | 0006       | 叉子         |        500
 000B    | 名古屋    | 0007       | 擦菜板       |        880
 000C    | 大阪      | 0003       | 运动T恤      |       4000
 000C    | 大阪      | 0004       | 菜刀         |       3000
 000C    | 大阪      | 0006       | 叉子         |        500
 000C    | 大阪      | 0007       | 擦菜板       |        880
 000D    | 福冈      | 0001       | T恤衫        |       1000
 000B    | 名古屋    | 0004       | 菜刀         |       3000
(13 行记录)
--CROSS JOIN用法示例
SELECT SP.shop_id, SP.shop_name, SP.product_id, P.product_name FROM shopproduct AS SP CROSS JOIN product AS P;
 shop_id | shop_name | product_id | product_name
---------+-----------+------------+--------------
 000A    | 东京      | 0001       | 运动T恤
 000A    | 东京      | 0002       | 运动T恤
 000A    | 东京      | 0003       | 运动T恤
 000B    | 名古屋    | 0003       | 运动T恤
 000B    | 名古屋    | 0002       | 运动T恤
 000B    | 名古屋    | 0006       | 运动T恤
 000B    | 名古屋    | 0007       | 运动T恤
 000C    | 大阪      | 0003       | 运动T恤
 000C    | 大阪      | 0004       | 运动T恤
 ......
  000C    | 大阪      | 0006       | 叉子
 000C    | 大阪      | 0007       | 叉子
 000D    | 福冈      | 0001       | 叉子
 000B    | 名古屋    | 0004       | 叉子
(104 行记录)									--13行*8行,总共有104行记录

练习题

练习题一:

请说出下述SELECT语句的结果。

SELECT * FROM product UNION SELECT * FROM product INTERSECT SELECT * FROM product ORDER BY product_id;
/*先取并集,结果为product表中所有数据,再与product表取交集,结果为product表,即
SELECT * FROM product;*/
 product_id | product_name | product_type | sale_price | purchase_price | regist_date
------------+--------------+--------------+------------+----------------+-------------
 0001       | T恤衫        | 衣服         |       1000 |            500 | 2009-09-20
 0002       | 打孔器       | 办公用品     |        500 |            320 | 2009-09-11
 0003       | 运动T恤      | 衣服         |       4000 |           2800 | 
 0004       | 菜刀         | 厨房用具     |       3000 |           2800 | 2009-09-20
 0005       | 叉子         | 厨房用具     |       6800 |           5000 | 2009-01-15
 0006       | 叉子         | 厨房用具     |        500 |                | 2009-09-20
 0007       | 擦菜板       | 厨房用具     |        880 |            790 | 2008-04-28
 0008       | 圆珠笔       | 办公用品     |        100 |                | 2009-11-11
(8 行记录)

练习题二:

上述RIGHT OUTER JOIN外联结的结果中,高压锅和圆珠笔2条记录的商店编号(shop_id)和商店名称(shop_name)都是NULL。请使用字符串“不确定”替换其中的NULL。期望结果如下所示。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1neF3Id-1610353893943)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20201226174104574.png)]

SELECT COALESCE(SP.shop_id, '不确定') AS shop_id, COALESCE(SP.shop_name, '不确定') AS shop_name, P.product_id, P.product_name, P.sale_price FROM shopproduct AS SP RIGHT OUTER JOIN product AS P ON SP.product_id=P.product_id;
 shop_id | shop_name | product_id | product_name | sale_price
---------+-----------+------------+--------------+------------
 000A    | 东京      | 0001       | T恤衫        |       1000
 000A    | 东京      | 0002       | 打孔器       |        500
 000A    | 东京      | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0003       | 运动T恤      |       4000
 000B    | 名古屋    | 0002       | 打孔器       |        500
 000B    | 名古屋    | 0006       | 叉子         |        500
 000B    | 名古屋    | 0007       | 擦菜板       |        880
 000C    | 大阪      | 0003       | 运动T恤      |       4000
 000C    | 大阪      | 0004       | 菜刀         |       3000
 000C    | 大阪      | 0006       | 叉子         |        500
 000C    | 大阪      | 0007       | 擦菜板       |        880
 000D    | 福冈      | 0001       | T恤衫        |       1000
 000B    | 名古屋    | 0004       | 菜刀         |       3000
 不确定  | 不确定    | 0008       | 圆珠笔       |        100
 不确定  | 不确定    | 0005       | 叉子         |       6800
(15 行记录)

25.OLAP函数

  • 含义:

    OLAP(OnLine Analytial Processing),对数据库数据进行实时分析处理。

    窗口函数是为了实现OLAP而添加的标准SQL功能。

  • 语法:

    <窗口函数> OVER ([PARTITION BY <列清单>] ORDER BY <排序用列清单>)

    ​ 窗口函数兼具分组和排序两种功能;

    ​ 通过PARTITION BY分组后的记录集合称为窗口;

    ​ 窗口函数只能在SELECT子句中使用,不能在WEHRE和GROUP BY子句中;

    窗口函数类型窗口函数
    聚合函数SUM、AVG、COUNT、MAX、MIN等
    专用窗口函数RANK:计算排序时,如果存在相同位次的记录,则会跳过之后的位次。例如,有3条记录排在第1位时:1位、1位、1位、4位…
    DENE_RANK:计算排序,即使存在相同位次的记录,也不会跳过之后的位次。例如:有3条记录排在第1位时:1位、1位、1位、2位……
    ROW_NUMBER:赋予唯一的连续位次。例如,有3条记录排在第1位时:1位、2位、3位、4位……
  • 示例

    --根据不同的商品种类,按照销售单价从低到高的顺序创建排序表
    SELECT product_name, product_type, sale_price, RANK() OVER (PARTITION BY product_type ORDER BY sale_price) AS ranking FROM product;
     product_name | product_type | sale_price | ranking
    --------------+--------------+------------+---------
     圆珠笔       | 办公用品     |        100 |       1
     打孔器       | 办公用品     |        500 |       2
     叉子         | 厨房用具     |        500 |       1
     擦菜板       | 厨房用具     |        880 |       2
     菜刀         | 厨房用具     |       3000 |       3
     叉子         | 厨房用具     |       6800 |       4
     T恤衫        | 衣服         |       1000 |       1
     运动T恤      | 衣服         |       4000 |       2
    (8 行记录)
    --PARTITION BY可以省略
    SELECT product_name, product_type, sale_price, RANK() OVER (ORDER BY sale_price) AS ranking FROM product;                           
     product_name | product_type | sale_price | ranking
    --------------+--------------+------------+---------
     圆珠笔       | 办公用品     |        100 |       1
     打孔器       | 办公用品     |        500 |       2
     叉子         | 厨房用具     |        500 |       2
     擦菜板       | 厨房用具     |        880 |       4
     T恤衫        | 衣服         |       1000 |       5
     菜刀         | 厨房用具     |       3000 |       6
     运动T恤      | 衣服         |       4000 |       7
     叉子         | 厨房用具     |       6800 |       8
    (8 行记录)
    --比较RANK、DENSE_RANK、ROW_NUMBER的区别
    SELECT product_name, product_type, sale_price, RANK() OVER (ORDER BY sale_price) AS ranking, DENSE_RANK() OVER (ORDER BY sale_price), ROW_NUMBER() OVER (ORDER BY sale_price) FROM product;
     product_name | product_type | sale_price | ranking | dense_rank | row_number
    --------------+--------------+------------+---------+------------+------------
     圆珠笔       | 办公用品     |        100 |       1 |          1 |          1
     打孔器       | 办公用品     |        500 |       2 |          2 |          2
     叉子         | 厨房用具     |        500 |       2 |          2 |          3
     擦菜板       | 厨房用具     |        880 |       4 |          3 |          4
     T恤衫        | 衣服         |       1000 |       5 |          4 |          5
     菜刀         | 厨房用具     |       3000 |       6 |          5 |          6
     运动T恤      | 衣服         |       4000 |       7 |          6 |          7
     叉子         | 厨房用具     |       6800 |       8 |          7 |          8
    (8 行记录)
    --聚合函数示例
    --使用SUM函数作为窗口函数
    SELECT product_id, product_name, sale_price, SUM(sale_price) OVER (ORDER BY product_id) AS current_sum FROM product; 
     product_id | product_name | sale_price | current_sum
    ------------+--------------+------------+-------------
     0001       | T恤衫        |       1000 |        1000
     0002       | 打孔器       |        500 |        1500
     0003       | 运动T恤      |       4000 |        5500
     0004       | 菜刀         |       3000 |        8500
     0005       | 叉子         |       6800 |       15300
     0006       | 叉子         |        500 |       15800
     0007       | 擦菜板       |        880 |       16680
     0008       | 圆珠笔       |        100 |       16780
    (8 行记录)
    --使用AVG函数作为窗口函数
    SELECT product_id, product_name, sale_price, AVG(sale_price) OVER (ORDER BY product_id) AS current_avg FROM product; 
     product_id | product_name | sale_price |      current_avg
    ------------+--------------+------------+-----------------------
     0001       | T恤衫        |       1000 | 1000.0000000000000000
     0002       | 打孔器       |        500 |  750.0000000000000000
     0003       | 运动T恤      |       4000 | 1833.3333333333333333
     0004       | 菜刀         |       3000 | 2125.0000000000000000
     0005       | 叉子         |       6800 | 3060.0000000000000000
     0006       | 叉子         |        500 | 2633.3333333333333333
     0007       | 擦菜板       |        880 | 2382.8571428571428571
     0008       | 圆珠笔       |        100 | 2097.5000000000000000
    (8 行记录)
    --指定框架(汇总对象)
    SELECT product_id, product_name, sale_price, AVG(sale_price) OVER (ORDER BY product_id ROWS 1 PRECEDING) AS moving_avg FROM product; 
    --ROWS 1 PRECEDING表示以当前行及之前的1行作为框架
     product_id | product_name | sale_price |      moving_avg
    ------------+--------------+------------+-----------------------
     0001       | T恤衫        |       1000 | 1000.0000000000000000	--1000/1
     0002       | 打孔器       |        500 |  750.0000000000000000	--(500+1000)/2
     0003       | 运动T恤      |       4000 | 2250.0000000000000000	--(4000+500)/2
     0004       | 菜刀         |       3000 | 3500.0000000000000000	--(3000+4000)/2
     0005       | 叉子         |       6800 | 4900.0000000000000000
     0006       | 叉子         |        500 | 3650.0000000000000000
     0007       | 擦菜板       |        880 |  690.0000000000000000
     0008       | 圆珠笔       |        100 |  490.0000000000000000
    (8 行记录)
    --PRECEING换为FOLLOWING表示当前与之后行
    --ROWS 1 PRECEDING AND 1 FOLLOWING表示当前行与前后1行
    SELECT product_id, product_name, sale_price, AVG(sale_price) OVER (ORDER BY product_id ROWS BETWENN 1 PRECEDING AND 1 FOLLOWING) AS moving_avg FROM product;
     product_id | product_name | sale_price |      moving_avg
    ------------+--------------+------------+-----------------------
     0001       | T恤衫        |       1000 |  750.0000000000000000	--(1000+500)/2
     0002       | 打孔器       |        500 | 1833.3333333333333333
     --(500+1000+4000)/3
     0003       | 运动T恤      |       4000 | 2500.0000000000000000
     --(4000+500+3000)/3
     0004       | 菜刀         |       3000 | 4600.0000000000000000
     0005       | 叉子         |       6800 | 3433.3333333333333333
     0006       | 叉子         |        500 | 2726.6666666666666667
     0007       | 擦菜板       |        880 |  493.3333333333333333
     0008       | 圆珠笔       |        100 |  490.0000000000000000
    (8 行记录)
    

26.GROUPING运算符

对聚合键组合的结果进行运算

运算符含义
ROLLUP一次计算出不同聚合键组合的结果。可以同时得出合计和小计。
CUBE将GROUP BY子句中聚合键的“所有可能的组合”的汇总结果集中到一个结果中。
GROUPING SETS用于从ROLLUP或者CUBE的结果中取出部分记录。
  • ROLLUP

    一次计算出不同聚合键组合的结果。可以同时得出合计和小计。

    SELECT product_type, SUM(sale_price) AS sum_price FROM product GROUP BY ROLLUP(product_type); 
    /*执行流程
    先计算GROUP BY(),再计算GROUP BY(product_type)
    */
    /*MYSQL的写法:
    SELECT product_type, SUM(sale_price) AS sum_price FROM product GROUP BY product_type WIHT ROLLUP;
    */
     product_type | sum_price
    --------------+-----------
                  |     16780
     衣服         |      5000
     办公用品     |       600
     厨房用具     |     11180
    (4 行记录)
    

    GROUP()表示没有聚合键,相当于没有GROUP BY子句(即全部数据行的记录),因此被称之为超级分组记录。其实,聚合键默认为NULL。

    SELECT product_type, regist_date, SUM(sale_price) AS sum_price FROM product GROUP BY ROLLUP(product_type, regist_date);
    /*执行流程:
    先计算GROUP BY(),再计算GROUP BY(product_type),再计算GROUP BY(product_type, regist_date)
    */
     product_type | regist_date | sum_price
    --------------+-------------+-----------
                  |             |     16780
     衣服         |             |      4000
     厨房用具     | 2008-04-28  |       880
     衣服         | 2009-09-20  |      1000
     办公用品     | 2009-11-11  |       100
     厨房用具     | 2009-01-15  |      6800
     办公用品     | 2009-09-11  |       500
     厨房用具     | 2009-09-20  |      3500
     衣服         |             |      5000
     办公用品     |             |       600
     厨房用具     |             |     11180
    (11 行记录)
    
  • GROUPING

    GROUPING函数在其参数列的值为超级分组记录所产生的NULL时返回1,其他情况返回0。因此使用GROUPING函数能够便捷地分辨出原始数据中的NULL和超级分组记录中的NULL。

    SELECT GROUPING(product_type) AS product_type, GROUPING(regist_date) AS regist_type, SUM(sale_price) AS sum_price FROM product GROUP BY ROLLUP(product_type, regist_date);
     product_type | regist_type | sum_price
    --------------+-------------+-----------
                1 |           1 |     16780
                0 |           0 |      4000
                0 |           0 |       880
                0 |           0 |      1000
                0 |           0 |       100
                0 |           0 |      6800
                0 |           0 |       500
                0 |           0 |      3500
                0 |           1 |      5000
                0 |           1 |       600
                0 |           1 |     11180
    (11 行记录)
    --在超级分组记录的键值中插入恰当的字符串
    SELECT CASE WHEN GROUPING(product_type)=1 THEN '商品种类 合计' ELSE product_type END AS product_type, CASE WHEN GROUPING(regist_date)=1 THEN '登记日期 合计' ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date, SUM(sale_price) AS sum_price FROM product GROUP BY ROLLUP(product_type, regist_date);
     product_type  |  regist_date  | sum_price
    ---------------+---------------+-----------
     商品种类 合计 | 登记日期 合计 |     16780
     衣服          |               |      4000
     厨房用具      | 2008-04-28    |       880
     衣服          | 2009-09-20    |      1000
     办公用品      | 2009-11-11    |       100
     厨房用具      | 2009-01-15    |      6800
     办公用品      | 2009-09-11    |       500
     厨房用具      | 2009-09-20    |      3500
     衣服          | 登记日期 合计 |      5000
     办公用品      | 登记日期 合计 |       600
     厨房用具      | 登记日期 合计 |     11180
    (11 行记录)
    
  • CUBE

    将GROUP BY子句中聚合键的“所有可能的组合”的汇总结果集中到一个结果中。因此,组合的个数就是2n(n是聚合键的个数)。

    SELECT product_type, regist_date, SUM(sale_price) AS sum_price FROM product GROUP BY CUBE(product_type, regist_date);   
    /*执行流程:
    先计算GROUP BY(),再计算GROUP BY(product_type),再计算GROUP BY(regist_date),再计算GROUP BY(product_type, regist_date)
    */
     product_type | regist_date | sum_price
    --------------+-------------+-----------
                  |             |     16780
     衣服         |             |      4000
     厨房用具     | 2008-04-28  |       880
     衣服         | 2009-09-20  |      1000
     办公用品     | 2009-11-11  |       100
     厨房用具     | 2009-01-15  |      6800
     办公用品     | 2009-09-11  |       500
     厨房用具     | 2009-09-20  |      3500
     衣服         |             |      5000
     办公用品     |             |       600
     厨房用具     |             |     11180
                  |             |      4000
                  | 2009-11-11  |       100
                  | 2009-09-20  |      4500
                  | 2009-09-11  |       500
                  | 2009-01-15  |      6800
                  | 2008-04-28  |       880
    (17 行记录)
    
  • GROUPING SETS

    用于从ROLLUP或者CUBE的结果中取出部分记录。

    SELECT CASE WHEN GROUPING(product_type)=1 THEN '商品种类 合计' ELSE product_type END AS product_type, CASE WHEN GROUPING(regist_date)=1 THEN '登记日期 合计' ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date, SUM(sale_price) AS sum_price FROM product GROUP BY GROUPING SETS(product_type, regist_date);
     product_type  |  regist_date  | sum_price
    ---------------+---------------+-----------
     衣服          | 登记日期 合计 |      5000
     办公用品      | 登记日期 合计 |       600
     厨房用具      | 登记日期 合计 |     11180
     商品种类 合计 |               |      4000
     商品种类 合计 | 2009-11-11    |       100
     商品种类 合计 | 2009-09-20    |      4500
     商品种类 合计 | 2009-09-11    |       500
     商品种类 合计 | 2009-01-15    |      6800
     商品种类 合计 | 2008-04-28    |       880
    (9 行记录)
    

练习题

练习题一:

请说出针对product(商品)表执行如下SELECT语句所能得到的结果。

SELECT product_id, product_name, sale_price, MAX(sale_price) OVER (ORDER BY product_id) AS current_price FROM product;
 product_id | product_name | sale_price | current_price
------------+--------------+------------+---------------
 0001       | T恤衫        |       1000 |          1000
 0002       | 打孔器       |        500 |          1000
 0003       | 运动T恤      |       4000 |          4000
 0004       | 菜刀         |       3000 |          4000
 0005       | 叉子         |       6800 |          6800
 0006       | 叉子         |        500 |          6800
 0007       | 擦菜板       |        880 |          6800
 0008       | 圆珠笔       |        100 |          6800
(8 行记录)

练习题二:

继续使用product表,计算出按照登记日期(regist_date)升序进行排列的各日期的销售单价(sale_price)的总额。排序是需要将登记日期为NULL的“运动T恤”记录排在第1位(也就是将其看作比其他日期都早)。

SELECT product_name, sale_price, regist_date, SUM(sale_price) OVER (ORDER BY regist_date NULLS FIRST) AS current_sum_price FROM product;                          
 product_name | sale_price | regist_date | current_sum_price
--------------+------------+-------------+-------------------
 运动T恤      |       4000 |             |              4000
 擦菜板       |        880 | 2008-04-28  |              4880
 叉子         |       6800 | 2009-01-15  |             11680
 打孔器       |        500 | 2009-09-11  |             12180
 叉子         |        500 | 2009-09-20  |             16680
 T恤衫        |       1000 | 2009-09-20  |             16680
 菜刀         |       3000 | 2009-09-20  |             16680
 圆珠笔       |        100 | 2009-11-11  |             16780
(8 行记录)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值