学习方式

学习资料:dw
练习:牛客网
输出:知乎专栏笔记

1.环境搭建

在终端登陆(不设置环境变量):
/usr/local/mysql/bin/mysql -u root -p
password:17351735

在终端输入mysql,提示找不到命令,设置环境变量。
PATH=”$PATH”:/usr/local/mysql/bin
export PATH=$PATH:/usr/local/mysql/bin
source ~/.bash_profile
mysql -u root -p
之后输入密码:17351735,以后就可以通过 mysql -u root -p在终端登陆。

客户端登陆:
datatrip(付费)
dbeaver(免费)

2.认识数据库

数据库管理系统:Database Management System,DBMS

2.1DBMS类型

有本叫做《七周七数据库》的书,每周介绍1种数据库类型,总共介绍了5种类型,7个数据库。

2.1.1层次数据库(Hierarchical Database,HDB)

2.1.2关系数据库(Relational Database,RDB)

  • Oracle Database:甲骨文公司的RDBMS
  • SQL Server:微软公司的RDBMS
  • DB2:IBM公司的RDBMS
  • PostgreSQL:开源的RDBMS
  • MySQL:开源的RDBMS

如上是5种具有代表性的RDBMS,其特点是由行和列组成的二维表来管理数据,这种类型的 DBMS 称为关系数据库管理系统(Relational Database Management System,RDBMS)。

2.1.3面向对象数据库(Object Oriented Database,OODB)

2.1.4XML数据库(XML Database,XMLDB)

2.1.5键值存储系统(Key-Value Store,KVS)

举例:MongoDB

这个笔记仅讲述关系数据库,即RDBMS,重点讲mysql。
RDBMS常见系统结构为c/s类型。
image.png
数据库按照记录的形式存储数据。
用excel做类比:
一条记录相当于excel里的一行。
一个列相当于一个字段。
行和列交汇的地方,称之为单元格。
表头,也就是列名。

2.2SQL语句的3大类

使用SQL语句来操作数据库。
sql语句可分为3类。

2.2.1DDL:数据定义语言

DDL(Data Definition Language,数据定义语言) 用来创建或者删除存储数据用的数据库以及数据库中的表等对象。DDL 包含以下几种指令。

  • CREATE : 创建数据库和表等对象
  • DROP : 删除数据库和表等对象
  • ALTER : 修改数据库和表等对象的结构

    2.2.2DML:数据操纵语言

    DML(Data Manipulation Language,数据操纵语言) 用来查询或者变更表中的记录。DML 包含以下几种指令。

  • SELECT :查询表中的数据

  • INSERT :向表中插入新数据
  • UPDATE :更新表中的数据
  • DELETE :删除表中的数据

实际使用的 SQL 语句当中有 90% 属于 DML

2.2.3DCL:数据控制语言

DCL(Data Control Language,数据控制语言) 用来确认或者取消对数据库中的数据进行的变更。除此之外,还可以对 RDBMS 的用户是否有权限操作数据库中的对象(数据库表等)进行设定。DCL 包含以下几种指令。

  • COMMIT : 确认对数据库中的数据进行的变更
  • ROLLBACK : 取消对数据库中的数据进行的变更
  • GRANT : 赋予用户操作权限
  • REVOKE : 取消用户的操作权限

2.3SQL书写规范

  • SQL语句要以分号( ; )结尾
  • SQL 不区分关键字的大小写,但是插入到表中的数据是区分大小写的
  • win 系统默认不区分表名及字段名的大小写
  • linux / mac 默认严格区分表名及字段名的大小写 * 本教程已统一调整表名及字段名的为小写,以方便初学者学习使用。
  • 常数的书写方式是固定的

‘abc’, 1234, ‘26 Jan 2010’, ‘10/01/26’, ‘2010-01-26’……

  • 单词需要用半角空格或者换行来分隔
  • 只能使用半角英文字母、数字、下划线(_)作为数据库、表和列的名称
  • 名称必须以半角英文字母开头
  • 注释:—

SQL 语句的单词之间需使用半角空格或换行符来进行分隔,且不能使用全角空格作为单词的分隔符,否则会发生错误,出现无法预期的结果。
请大家认真查阅《附录1 - SQL 语法规范》,养成规范的书写习惯。

2.4操作与语句

创建数据库

  1. CREATE DATABASE < 数据库名称 > ;
  2. CREATE DATABASE shop;

创建数据表

  1. CREATE TABLE < 表名 >
  2. ( < 列名 1> < 数据类型 > < 该列所需约束 > ,
  3. < 列名 2> < 数据类型 > < 该列所需约束 > ,
  4. < 列名 3> < 数据类型 > < 该列所需约束 > ,
  5. < 列名 4> < 数据类型 > < 该列所需约束 > ,
  6. .
  7. .
  8. .
  9. < 该表的约束 1> , < 该表的约束 2> ,……);
  10. ----------------------------------------
  11. CREATE TABLE product
  12. (product_id CHAR(4) NOT NULL, -- 商品编号
  13. product_name VARCHAR(100) NOT NULL, -- 商品名称
  14. product_type VARCHAR(32) NOT NULL, -- 商品种类
  15. sale_price INTEGER , -- 销售单价
  16. purchase_price INTEGER , -- 进货单价
  17. regist_date DATE , -- 登记日期
  18. PRIMARY KEY (product_id));

四种最基本数据类型

  • INTEGER 型

用来指定存储整数的列的数据类型(数字型),不能存储小数。

  • CHAR 型

用来存储定长字符串,当列中存储的字符串长度达不到最大长度的时候,使用半角空格进行补足,由于会浪费存储空间,所以一般不使用。

  • VARCHAR 型

用来存储可变长度字符串,定长字符串在字符数未达到最大长度时会用半角空格补足,但可变长字符串不同,即使字符数未达到最大长度,也不会用半角空格补足。

  • DATE 型

用来指定存储日期(年月日)的列的数据类型(日期型)。

约束

约束是除了数据类型之外,对列中存储的数据进行限制或者追加条件的功能。
NOT NULL是非空约束,即该列必须输入数据。
PRIMARY KEY是主键约束,代表该列是唯一值,可以通过该列取出特定的行的数据。
DEFAULT DEFAULT 0 默认值

删除表

  1. DROP TABLE < 表名 > ;
  2. DROP TABLE product;

更新表(alter:添加与删除列)

执行后无法恢复

  1. -- 添加列
  2. ALTER TABLE < 表名 > ADD COLUMN < 列的定义 >;
  3. -- 添加一列可以存储100位的可变长字符串的 product_name_pinyin
  4. ALTER TABLE product ADD COLUMN product_name_pinyin VARCHAR(100);
  5. -- 删除列
  6. ALTER TABLE < 表名 > DROP COLUMN < 列名 >;
  7. ALTER TABLE product DROP COLUMN product_name_pinyin;
  8. -- 清空表内容
  9. TRUNCATE TABLE TABLE_NAME;

删除列的3种方法比较:
相比drop / deletetruncate用来清除数据时,速度最快。

更新数据(update)

  1. UPDATE <表名>
  2. SET <列名> = <表达式> [, <列名2>=<表达式2>...];
  3. WHERE <条件>; -- 可选,非常重要。
  4. ORDER BY 子句; --可选
  5. LIMIT 子句; --可选
  6. --------------------------------------------
  7. -- 修改所有的注册时间
  8. UPDATE product
  9. SET regist_date = '2009-10-10';
  10. -- 仅修改部分商品的单价
  11. UPDATE product
  12. SET sale_price = sale_price * 10
  13. WHERE product_type = '厨房用具';
  14. --------------------------------------------
  15. -- 将商品编号为0008的数据(圆珠笔)的登记日期更新为NULL
  16. UPDATE product
  17. SET regist_date = NULL
  18. WHERE product_id = '0008';
  19. --------------------------------------------
  20. --UPDATE 语句的 SET 子句支持同时将多个列作为更新对象。
  21. -- 基础写法,一条UPDATE语句只更新一列
  22. UPDATE product
  23. SET sale_price = sale_price * 10
  24. WHERE product_type = '厨房用具';
  25. UPDATE product
  26. SET purchase_price = purchase_price / 2
  27. WHERE product_type = '厨房用具';
  28. -- 合并后的写法
  29. UPDATE product
  30. SET sale_price = sale_price * 10,
  31. purchase_price = purchase_price / 2
  32. WHERE product_type = '厨房用具';

null值:可用于数据清空。但是,只有未设置 NOT NULL 约束和主键约束的列才可以清空为NULL

插入数据(insert)

  1. INSERT INTO <表名> (列1, 2, 3, ……) VALUES (值1, 2, 3, ……);
  2. --------------------------------------------
  3. CREATE TABLE productins
  4. (product_id CHAR(4) NOT NULL,
  5. product_name VARCHAR(100) NOT NULL,
  6. product_type VARCHAR(32) NOT NULL,
  7. sale_price INTEGER DEFAULT 0,
  8. purchase_price INTEGER ,
  9. regist_date DATE ,
  10. PRIMARY KEY (product_id));
  11. -- 包含列清单
  12. INSERT INTO productins (product_id, product_name, product_type, sale_price, purchase_price, regist_date) VALUES ('0005', '高压锅', '厨房用具', 6800, 5000, '2009-01-15');
  13. -- 省略列清单
  14. INSERT INTO productins VALUES ('0005', '高压锅', '厨房用具', 6800, 5000, '2009-01-15');
  15. -- 通常的INSERT
  16. INSERT INTO productins VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11');
  17. INSERT INTO productins VALUES ('0003', '运动T恤', '衣服', 4000, 2800, NULL);
  18. INSERT INTO productins VALUES ('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20');
  19. -- 多行INSERT DB2SQLSQL Server PostgreSQL MySQL多行插入)
  20. INSERT INTO productins VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11'),
  21. ('0003', '运动T恤', '衣服', 4000, 2800, NULL),
  22. ('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20');
  23. -- Oracle中的多行INSERT
  24. INSERT ALL INTO productins VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11')
  25. INTO productins VALUES ('0003', '运动T恤', '衣服', 4000, 2800, NULL)
  26. INTO productins VALUES ('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20')
  27. SELECT * FROM DUAL;
  28. -- DUALOracle特有(安装时的必选项)的一种临时表A。因此“SELECT *FROM DUAL 部分也只是临时性的,并没有实际意义。
  29. - DML :插入数据
  30. STARTTRANSACTION;
  31. INSERT INTO product VALUES('0001', 'T恤衫', '衣服', 1000, 500, '2009-09-20');
  32. INSERT INTO product VALUES('0002', '打孔器', '办公用品', 500, 320, '2009-09-11');
  33. INSERT INTO product VALUES('0003', '运动T恤', '衣服', 4000, 2800, NULL);
  34. INSERT INTO product VALUES('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20');
  35. INSERT INTO product VALUES('0005', '高压锅', '厨房用具', 6800, 5000, '2009-01-15');
  36. INSERT INTO product VALUES('0006', '叉子', '厨房用具', 500, NULL, '2009-09-20');
  37. INSERT INTO product VALUES('0007', '擦菜板', '厨房用具', 880, 790, '2008-04-28');
  38. INSERT INTO product VALUES('0008', '圆珠笔', '办公用品', 100, NULL, '2009-11-11');
  39. COMMIT;

2.5练习

image.png

2.5.1

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

  1. create table Addressbook(
  2. regist_no integer not null,
  3. name varchar(128) not null,
  4. address varchar(256) not null,
  5. tel_no char(10),
  6. mail_address char(20),
  7. primary key (regist_no)
  8. );

2.5.2

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

  1. alter table Addressbook add column postal_code char(8) not null;

2.5.3

编写 SQL 语句来删除 Addressbook 表。

  1. drop table Addressbook;

2.5.4

编写 SQL 语句来恢复删除掉的 Addressbook 表。

  1. create table Addressbook(
  2. regist_no integer not null,
  3. name varchar(128) not null,
  4. address varchar(256) not null,
  5. tel_no char(10),
  6. mail_address char(20),
  7. postal_code char(8) not null,
  8. primary key (regist_no)
  9. );

3.基础查询与排序

3.1限定查询

  • 星号(*)代表全部列的意思。
  • SQL中可以随意使用换行符,不影响语句执行(但不可插入空行)。
  • 设定汉语别名时需要使用双引号(”)括起来。
  • 在SELECT语句中使用DISTINCT可以删除重复行。
  • 注释是SQL语句中用来标识说明或者注意事项的部分。分为1行注释”— “和多行注释两种”/ /“。
  • SELECT子句中可以使用常数或者表达式。
  • 使用比较运算符时一定要注意不等号和等号的位置。
  • 字符串类型的数据原则上按照字典顺序进行排序,不能与数字的大小顺序混淆。
  • 希望选取NULL记录时,需要在条件表达式中使用IS NULL运算符。希望选取不是NULL的记录时,需要在条件表达式中使用IS NOT NULL运算符。 ```sql SELECT <列名>, FROM <表名>;

    SELECT <列名>, …… FROM <表名> WHERE <条件表达式>;

    — 用来选取product type列为衣服’的记录的SELECT语句 SELECT product_name, product_type FROM product WHERE product_type = ‘衣服’; — 也可以选取出不是查询条件的列(条件列与输出列不同) SELECT product_name FROM product WHERE product_type = ‘衣服’;

— 想要查询出全部列时,可以使用代表所有列的星号()。 SELECT FROM <表名>; — SQL语句可以使用AS关键字为列设定别名(用中文时需要双引号(“”))。 SELECT product_id As id, product_name As name, purchase_price AS “进货单价” FROM product; — 使用DISTINCT删除product_type列中重复的数据 SELECT DISTINCT product_type FROM product;

— SQL语句中也可以使用运算表达式 SELECT product_name, sale_price, sale_price 2 AS “sale_price x2” FROM product; — WHERE子句的条件表达式中也可以使用计算表达式 SELECT product_name, sale_price, purchase_price FROM product WHERE sale_price-purchase_price >= 500; / 对字符串使用不等号 首先创建chars并插入数据 选取出大于‘2’的SELECT语句*/ — DDL:创建表 CREATE TABLE chars (chr CHAR(3)NOT NULL, PRIMARY KEY(chr)); — 选取出大于’2’的数据的SELECT语句(‘2’为字符串) SELECT chr FROM chars WHERE chr > ‘2’; — 选取NULL的记录 SELECT product_name, purchase_price FROM product WHERE purchase_price IS NULL; — 选取不为NULL的记录 SELECT product_name, purchase_price FROM product WHERE purchase_price IS NOT NULL;

— 选取出销售单价大于等于1000日元的记录 SELECT product_name, product_type, sale_price FROM product WHERE sale_price >= 1000; — 向代码清单2-30的查询条件中添加NOT运算符 SELECT product_name, product_type, sale_price FROM product WHERE NOT sale_price >= 1000;

  1. <a name="d0uK8"></a>
  2. ## 3.2运算符
  3. - 算术运算符:+,-,*,/
  4. - 比较运算符:=,<>,>=, >, <, <=
  5. - 逻辑运算符:
  6. - not,表示否定
  7. - and,取交集
  8. - or,取并集
  9. and优先级优于or,可以利用括号提升优先级<br />真值表和普通认为的一样。<br />sql语句除了真和假,还有一个不确定(unknown)值。属于三值逻辑。<br />真值表:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/453178/1624902519183-eab86605-6bf0-4899-acc8-1c6a1869950d.png#height=361&id=UPR7M&margin=%5Bobject%20Object%5D&name=image.png&originHeight=722&originWidth=1164&originalType=binary&ratio=1&size=133741&status=done&style=none&width=582)
  10. <a name="fmMqP"></a>
  11. ## 3.3练习题
  12. <a name="ZsPQL"></a>
  13. ### 3.3.1
  14. 编写一条SQL语句,从product(商品)表中选取出“登记日期(regist在2009年4月28日之后”的商品,查询结果要包含product name和regist_date两列。
  15. ```sql
  16. select product_name, regist_date from product where regist_date > '2009-04-28';

image.png

3.3.2

请说出对product 表执行如下3条SELECT语句时的返回结果。
null要用is /is not,不如什么都查不到。

  1. --1
  2. SELECT *
  3. FROM product
  4. WHERE purchase_price = NULL;

image.png

  1. --2
  2. SELECT *
  3. FROM product
  4. WHERE purchase_price <> NULL;

image.png

  1. --3
  2. SELECT *
  3. FROM product
  4. WHERE product_name > NULL;

image.png

3.3.3

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

  1. product_name | sale_price | purchase_price
  2. -------------+------------+------------
  3. T恤衫 |   1000 | 500
  4. 运动T | 4000 | 2800
  5. 高压锅 | 6800 | 5000
  1. select product_name, sale_price, purchase_price from product where sale_price-purchase_price >= 500;
  2. select product_name, sale_price, purchase_price from product where sale_price>=purchase_price + 500;

3.3.4

请写出一条SELECT语句,从product表中选取出满足“销售单价打九折之后利润高于100日元的办公用品和厨房用具”条件的记录。查询结果要包括product_name列、product_type列以及销售单价打九折之后的利润(别名设定为profit)。
提示:销售单价打九折,可以通过saleprice列的值乘以0.9获得,利润可以通过该值减去purchase_price列的值获得。

  1. select product_name, product_type, purchase_price-sale_price*0.9 as profit from product where purchase_price-sale_price * 0.9 >= 100 and (product_type = '办公用品' or product_type ='厨房用具');

image.png

查看全部的:

  1. elect product_name, product_type,purchase_price, sale_price, sale_price*0.9 as sale,purchase_price-sale_price*0.9 as profit from product;

image.png

3.4聚合查询

3.4.1使用聚合函数

聚合函数

  • COUNT:计算表中的记录数(行数)
  • SUM:计算表中数值列中数据的合计值
  • AVG:计算表中数值列中数据的平均值
  • MAX:求出表中任意列中数据的最大值
  • MIN:求出表中任意列中数据的最小值

注意事项

  • COUNT函数的结果根据参数的不同而不同。COUNT(*)会得到包含NULL的数据行数,而COUNT(<列名>)会得到NULL之外的数据行数。
  • 聚合函数会将NULL排除在外。但COUNT(*)例外,并不会排除NULL。
  • MAX/MIN函数几乎适用于所有数据类型的列。SUM/AVG函数只适用于数值类型的列。
  • 想要计算值的种类时,可以在COUNT函数的参数中使用DISTINCT。
  • 在聚合函数的参数中使用DISTINCT,可以删除重复数据。 ```sql — 计算全部数据的行数(包含NULL) SELECT COUNT(*) FROM product; — 计算NULL以外数据的行数 SELECT COUNT(purchase_price) FROM product; — 计算销售单价和进货单价的合计值 SELECT SUM(sale_price), SUM(purchase_price) FROM product; — 计算销售单价和进货单价的平均值 SELECT AVG(sale_price), AVG(purchase_price) FROM product; — MAX和MIN也可用于非数值型数据 SELECT MAX(regist_date), MIN(regist_date) FROM product;

— 计算去除重复数据后的数据行数 SELECT COUNT(DISTINCT product_type) FROM product; — 是否使用DISTINCT时的动作差异(SUM函数) SELECT SUM(sale_price), SUM(DISTINCT sale_price) FROM product;

  1. <a name="XZhjY"></a>
  2. ## 3.5表分组
  3. - 在 GROUP BY 子句中指定的列称为**聚合键**或者**分组列**。
  4. - 聚合键包含null,会将null当作一组特殊数据处理。
  5. - GROUP BY的子句书写顺序有严格要求,不按要求会导致SQL无法正常执行,目前出现过的子句顺序为:
  6. 1 SELECT → 2. FROM → 3. WHERE → 4. GROUP BY<br />其中前三项用于筛选数据,GROUP BY对筛选出的数据进行处理
  7. <a name="7BV84"></a>
  8. ### 常见错误
  9. 1. 在聚合函数的SELECT子句中写了聚合健以外的列
  10. 使用COUNT等聚合函数时,**SELECT子句中如果出现列名,只能是GROUP BY子句中指定的列名**(也就是聚合键)。
  11. 2. 在GROUP BY子句中使用列的别名
  12. SELECT子句中可以通过AS来指定别名,但**在GROUP BY中不能使用别名**。因为在DBMS中 ,SELECT子句在GROUP BY子句后执行。
  13. 4. 在WHERE中使用聚合函数
  14. 原因是**聚合函数的使用前提是结果集已经确定,而WHERE还处于确定结果集的过程中,所以相互矛盾会引发错误**。 如果想指定条件,可以在SELECT,HAVING(下面马上会讲)以及ORDER BY子句中使用聚合函数。
  15. ```sql
  16. SELECT <列名1>,<列名2>, <列名3>, ……
  17. FROM <表名>
  18. GROUP BY <列名1>, <列名2>, <列名3>, ……;
  19. -- 按照商品种类统计数据行数
  20. SELECT product_type, COUNT(*)
  21. FROM product
  22. GROUP BY product_type;
  23. -- 不含GROUP BY
  24. SELECT product_type, COUNT(*)
  25. FROM product
  26. SELECT purchase_price, COUNT(*)
  27. FROM product
  28. GROUP BY purchase_price;
  29. SELECT purchase_price, COUNT(*)
  30. FROM product
  31. WHERE product_type = '衣服'
  32. GROUP BY purchase_price;

用having给分组指定条件

这里WHERE不可行,因为,WHERE子句只能指定记录(行)的条件,而不能用来指定组的条件(例如,“数据行数为 2 行”或者“平均值为 500”等)。
可以在GROUP BY后使用HAVING子句。
HAVING的用法类似WHERE
HAVING子句用于对分组进行过滤,可以使用数字、聚合函数和GROUP BY中指定的列名(聚合键)。

  1. -- 数字
  2. SELECT product_type, COUNT(*)
  3. FROM product
  4. GROUP BY product_type
  5. HAVING COUNT(*) = 2;
  6. -- 错误形式(因为product_name不包含在GROUP BY聚合键中)
  7. SELECT product_type, COUNT(*)
  8. FROM product
  9. GROUP BY product_type
  10. HAVING product_name = '圆珠笔';

3.6对查询结果进行排序

SQL中的执行结果是随机排列的,当需要按照特定顺序排序时,可已使用ORDER BY子句。
默认为升序排列,降序排列为DESC
在 ORDER BY 子句中却可以使用别名

为什么在GROUP BY中不可以而在ORDER BY中可以呢? 这是因为SQL在使用 HAVING 子句时 SELECT 语句的顺序为: FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY。 其中SELECT的执行顺序在 GROUP BY 子句之后,ORDER BY 子句之前。也就是说,当在ORDER BY中使用别名时,已经知道了SELECT设置的别名存在,但是在GROUP BY中使用别名时还不知道别名的存在,所以不能在ORDER BY中可以使用别名,但是在GROUP BY中不能使用别名

  1. SELECT <列名1>, <列名2>, <列名3>, ……
  2. FROM <表名>
  3. ORDER BY <排序基准列1>, <排序基准列2>, ……
  4. -- 降序排列
  5. SELECT product_id, product_name, sale_price, purchase_price
  6. FROM product
  7. ORDER BY sale_price DESC;
  8. -- 多个排序键
  9. SELECT product_id, product_name, sale_price, purchase_price
  10. FROM product
  11. ORDER BY sale_price, product_id;
  12. -- 当用于排序的列名中含有NULL时,NULL会在开头或末尾进行汇总。
  13. SELECT product_id, product_name, sale_price, purchase_price
  14. FROM product
  15. ORDER BY purchase_price;

3.7练习题

3.7.1

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

  1. SELECT product_id, SUMproduct_name
  2. --本SELECT语句中存在错误。
  3. FROM product
  4. GROUP BY product_type
  5. WHERE regist_date > '2009-09-01';

错误1:where要放在group by前
错误2:select里只能使用product_type或对其使用聚合函数。
错误3: sum函数使用了全角的括号。应为半角。
错误4: 注释的—后应有个空格。
改完后:

  1. SELECT product_type,sum(product_type)
  2. -- SELECT语句中存在错误。
  3. FROM product
  4. WHERE regist_date > '2009-09-01'
  5. GROUP BY product_type ;

3.7.2

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

  1. select product_type,sum(sale_price), sum(purchase_price)from product
  2. group by product_type having sum(sale_price) > 1.5*sum(purchase_price);

image.png

3.7.3

此前我们曾经使用SELECT语句选取出了product(商品)表中的全部记录。当时我们使用了ORDERBY子句来指定排列顺序,但现在已经无法记起当时如何指定的了。请根据下列执行结果,思考ORDERBY子句的内容。
image.png
按照regist_date空值置顶,regist_date倒序,相同regist_date,按照sale_price升序。
注意缺了这句会导致时间乱序:regist_date desc

  1. SELECT * from product order by regist_date is null desc,regist_date desc,sale_price asc;

image.png

在 MySQL 中,NULL 值被认为低于任何非 NULL 值,因此,当顺序是 ASC (升序)时,NULL 值首先出现,当顺序是 DESC (降序)时,NULL 值最后出现。我们将讨论以下两种情况,因为在这两种情况下,对 NULL 值进行排序可能并不简单。
参考资料:https://www.designcise.com/web/tutorial/how-to-order-null-values-first-or-last-in-mysql

4.复杂查询

4.1视图

视图与表的区别:是否保存实际数据。视图可理解为一个窗口,用来看到数据库表中真实数据。
比如一个select查询出来的结果就是视图。
视图基于真值表,也可以基于视图。但多重视图会降低sql性能。
一般dbms视图定义不能使用order by,因为视图和表一样,数据行没有顺序。
mysql视图定义可使用order by。

4.1.1视图创建

CREATE VIEW <视图名称>(<列名1>,<列名2>,…) AS

4.1.2视图更新

  • 如果原表可以更新,那么 视图中的数据也可以更新。反之亦然,如果视图发生了改变,而原表没有进行相应更新的话,就无法保证数据的一致性了。
  • 对于一个视图来说,如果包含以下结构的任意一种都是不可以被更新的:
    • 聚合函数 SUM()、MIN()、MAX()、COUNT() 等。
    • DISTINCT 关键字。
    • GROUP BY 子句。
    • HAVING 子句。
    • UNION 或 UNION ALL 运算符。
    • FROM 子句中包含多个表。
  • 视图更新,原表也会被更新。但更新的只是视图里可以被看到的数据

4.1.3删除视图

DROP VIEW <视图名1> [ , <视图名2> …]

  1. -- 创建视图
  2. CREATE VIEW <视图名称>(<列名1>,<列名2>,...) AS <SELECT语句>
  3. -- 基于单表视图
  4. CREATE VIEW productsum (product_type, cnt_product)
  5. AS
  6. SELECT product_type, COUNT(*)
  7. FROM product
  8. GROUP BY product_type ;
  9. -- 基于多表视图
  10. CREATE VIEW view_shop_product(product_type, sale_price, shop_name)
  11. AS
  12. SELECT product_type, sale_price, shop_name
  13. FROM product,
  14. shop_product
  15. WHERE product.product_id = shop_product.product_id;
  16. -- 在上面的视图上查询
  17. SELECT sale_price, shop_name
  18. FROM view_shop_product
  19. WHERE product_type = '衣服';
  20. -- 修改视图结构,视图名需要是数据库唯一。
  21. -- 也可以通过将当前视图删除然后重新创建的方式达到修改的效果。
  22. ALTER VIEW <视图名> AS <SELECT语句>
  23. ALTER VIEW productSum
  24. AS
  25. SELECT product_type, sale_price
  26. FROM Product
  27. WHERE regist_date > '2009-09-11';
  28. -- 视图更新
  29. UPDATE productsum
  30. SET sale_price = '5000'
  31. WHERE product_type = '办公用品';
  32. -- 删除视图:需要权限
  33. DROP VIEW <视图名1> [ , <视图名2> …]
  34. DROP VIEW productSum;

4.2子查询

嵌套查询,select的from来自另一个select(子查询)的结果。
子查询是一次性的,不会存储的介质中。
但视图会保存下来。
多层嵌套,导致难以理解以及效率下降,尽量避免。

  1. SELECT stu_name
  2. FROM (
  3. SELECT stu_name, COUNT(*) AS stu_cnt
  4. FROM students_info
  5. GROUP BY stu_age) AS studentSum;

4.2.1标量子查询

只返回一个值的查询。
场景:

  1. 查询出销售单价高于平均销售单价的商品
  2. 查询出注册日期最晚的那个商品

标量子查询不仅仅局限于 WHERE 子句中,通常任何可以使用单一值的位置都可以使用。也就是说, 能够使用常数或者列名的地方,无论是 SELECT 子句、GROUP BY 子句、HAVING 子句,还是 ORDER BY 子句,几乎所有的地方都可以使用。

  1. SELECT product_id, product_name, sale_price
  2. FROM product
  3. WHERE sale_price > (SELECT AVG(sale_price) FROM product);
  4. SELECT product_id,
  5. product_name,
  6. sale_price,
  7. (SELECT AVG(sale_price)
  8. FROM product) AS avg_price
  9. FROM product;

4.2.2关联子查询

通过一些标志将内外两层的查询连接起来起到过滤数据的目的

  1. SELECT product_type, product_name, sale_price
  2. FROM product AS p1
  3. WHERE sale_price > (SELECT AVG(sale_price)
  4. FROM product AS p2
  5. WHERE p1.product_type = p2.product_type
  6. GROUP BY product_type);
  7. --
  8. SELECT product_type, product_name, sale_price
  9. FROM product AS p1
  10. WHERE sale_price > (SELECT AVG(sale_price)
  11. FROM product AS p2
  12. WHERE p1.product_type =p2.product_type
  13. GROUP BY product_type);

4.3练习题

4.3.1

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

  • 条件 1:销售单价大于等于 1000 日元。
  • 条件 2:登记日期是 2009 年 9 月 20 日。
  • 条件 3:包含商品名称、销售单价和登记日期三列。

对该视图执行 SELECT 语句的结果如下所示。

  1. SELECT * FROM ViewPractice5_1;

执行结果

  1. product_name | sale_price | regist_date
  2. --------------+------------+------------
  3. T恤衫 | 1000 | 2009-09-20
  4. 菜刀 | 3000 | 2009-09-20
  1. -- 作业3.1
  2. create view ViewPractice5_1(prodouct_name, sale_price, regist_date) as
  3. select product_name, sale_price, regist_date
  4. from product
  5. where sale_price >= 1000 and regist_date = '2009-09-20';
  6. select * from ViewPractice5_1;

image.png

4.3.2

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

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

执行报错,提示:Field of view ‘shop.viewpractice5_1’ underlying table doesn’t have a default value.
显式的:INSERTINTOViewPractice5_1(prodouct_name, sale_price, regist_date) VALUES (‘ 刀子 ‘, 300, ‘2009-11-02’);
依然如此。
product表的列定义:
image.png
如果在product表设置视图对应字段拥有默认值,
查询资料,向视图插入数据,首先要有插入资格,其次拥有以下条件之一:

  • 该字段允许空值。
  • 该字段设有默认值。
  • 该字段是标识字段,可根据标识种子和标识增量自动填充数据。
  • 该字段的数据类型为timestamp或uniqueidentifier。

4.3.3

请根据如下结果编写 SELECT 语句,其中 sale_price_all 列为全部商品的平均销售单价。

  1. product_id | product_name | product_type | sale_price | sale_price_all
  2. ------------+-------------+--------------+------------+---------------------
  3. 0001 | T恤衫 | 衣服 | 1000 | 2097.5000000000000000
  4. 0002 | 打孔器 | 办公用品 | 500 | 2097.5000000000000000
  5. 0003 | 运动T | 衣服 | 4000 | 2097.5000000000000000
  6. 0004 | 菜刀 | 厨房用具 | 3000 | 2097.5000000000000000
  7. 0005 | 高压锅 | 厨房用具 | 6800 | 2097.5000000000000000
  8. 0006 | 叉子 | 厨房用具 | 500 | 2097.5000000000000000
  9. 0007 | 擦菜板 | 厨房用具 | 880 | 2097.5000000000000000
  10. 0008 | 圆珠笔 | 办公用品 | 100 | 2097.5000000000000000
  1. -- 注意子查询里的括号
  2. select product_id, product_name, product_type, sale_price,
  3. (select AVG(sale_price) from product) as sale_price_all
  4. from product;

image.png

4.3.4

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

  1. product_id | product_name | product_type | sale_price | avg_sale_price
  2. ------------+-------------+--------------+------------+---------------------
  3. 0001 | T恤衫 | 衣服 | 1000 |2500.0000000000000000
  4. 0002 | 打孔器 | 办公用品 | 500 | 300.0000000000000000
  5. 0003 | 运动T | 衣服 | 4000 |2500.0000000000000000
  6. 0004 | 菜刀 | 厨房用具 | 3000 |2795.0000000000000000
  7. 0005 | 高压锅 | 厨房用具 | 6800 |2795.0000000000000000
  8. 0006 | 叉子 | 厨房用具 | 500 |2795.0000000000000000
  9. 0007 | 擦菜板 | 厨房用具 | 880 |2795.0000000000000000
  10. 0008 | 圆珠笔 | 办公用品 | 100 | 300.0000000000000000

提示:其中的关键是 avg_sale_price 列。与习题三不同,这里需要计算出的 是各商品种类的平均销售单价。这与使用关联子查询所得到的结果相同。 也就是说,该列可以使用关联子查询进行创建。问题就是应该在什么地方使用这个关联子查询。

解答:先完成查询句,再套上创建视图的语法。
关于各商品种类的平均销售单价,参考前面的例子。
关键在于对子查询两个表的别名的定义。最外层是表p1,内层是表p2。

  1. CREATE view AvgPriceByType (product_id, product_name, product_type, sale_price,avg_sale_price)
  2. as
  3. select product_id, product_name, product_type, sale_price,
  4. (SELECT AVG(sale_price)
  5. FROM product AS p2
  6. WHERE p1.product_type =p2.product_type) as avg_sale_price
  7. from product as p1;
  8. select * from AvgPriceByType;

image.png

4.4函数

函数总个数超过200个,不需要完全记住,常用函数有 30~50 个,其他不常用的函数使用时查阅文档即可。

4.4.1算术函数 (用来进行数值计算的函数)

+,-,*,/

  1. -- DDL :创建表
  2. USE shop;
  3. DROP TABLE IF EXISTS samplemath;
  4. CREATE TABLE samplemath
  5. (m float(10,3),
  6. n INT,
  7. p INT);
  8. -- DML :插入数据
  9. START TRANSACTION; -- 开始事务
  10. INSERT INTO samplemath(m, n, p) VALUES (500, 0, NULL);
  11. INSERT INTO samplemath(m, n, p) VALUES (-180, 0, NULL);
  12. INSERT INTO samplemath(m, n, p) VALUES (NULL, NULL, NULL);
  13. INSERT INTO samplemath(m, n, p) VALUES (NULL, 7, 3);
  14. INSERT INTO samplemath(m, n, p) VALUES (NULL, 5, 2);
  15. INSERT INTO samplemath(m, n, p) VALUES (NULL, 4, NULL);
  16. INSERT INTO samplemath(m, n, p) VALUES (8, NULL, 3);
  17. INSERT INTO samplemath(m, n, p) VALUES (2.27, 1, NULL);
  18. INSERT INTO samplemath(m, n, p) VALUES (5.555,2, NULL);
  19. INSERT INTO samplemath(m, n, p) VALUES (NULL, 1, NULL);
  20. INSERT INTO samplemath(m, n, p) VALUES (8.76, NULL, NULL);
  21. COMMIT; -- 提交事务
  22. -- 查询表内容
  23. SELECT * FROM samplemath;
  24. +----------+------+------+
  25. | m | n | p |
  26. +----------+------+------+
  27. | 500.000 | 0 | NULL |
  28. | -180.000 | 0 | NULL |
  29. | NULL | NULL | NULL |
  30. | NULL | 7 | 3 |
  31. | NULL | 5 | 2 |
  32. | NULL | 4 | NULL |
  33. | 8.000 | NULL | 3 |
  34. | 2.270 | 1 | NULL |
  35. | 5.555 | 2 | NULL |
  36. | NULL | 1 | NULL |
  37. | 8.760 | NULL | NULL |
  38. +----------+------+------+
  39. 11 rows in set (0.00 sec)

4.4.1.1ABS — 绝对值

语法:ABS( 数值 )
ABS 函数用于计算一个数字的绝对值,表示一个数到原点的距离。
当 ABS 函数的参数为NULL时,返回值也是NULL

4.4.1.2MOD — 求余数

语法:MOD( 被除数,除数 )
MOD 是计算除法余数(求余)的函数,是 modulo 的缩写。小数没有余数的概念,只能对整数列求余数。
注意:主流的 DBMS 都支持 MOD 函数,只有SQL Server 不支持该函数,其使用%符号来计算余数。

4.4.1.3ROUND — 四舍五入

语法:ROUND( 对象数值,保留小数的位数 )
ROUND 函数用来进行四舍五入操作。
注意:当参数 保留小数的位数 为变量时,可能会遇到错误,请谨慎使用变量。

  1. SELECT m,
  2. ABS(m)ASabs_col ,
  3. n, p,
  4. MOD(n, p) AS mod_col,
  5. ROUND(m,1)ASround_colS
  6. FROM samplemath;
  7. +----------+---------+------+------+---------+-----------+
  8. | m | abs_col | n | p | mod_col | round_col |
  9. +----------+---------+------+------+---------+-----------+
  10. | 500.000 | 500.000 | 0 | NULL | NULL | 500.0 |
  11. | -180.000 | 180.000 | 0 | NULL | NULL | -180.0 |
  12. | NULL | NULL | NULL | NULL | NULL | NULL |
  13. | NULL | NULL | 7 | 3 | 1 | NULL |
  14. | NULL | NULL | 5 | 2 | 1 | NULL |
  15. | NULL | NULL | 4 | NULL | NULL | NULL |
  16. | 8.000 | 8.000 | NULL | 3 | NULL | 8.0 |
  17. | 2.270 | 2.270 | 1 | NULL | NULL | 2.3 |
  18. | 5.555 | 5.555 | 2 | NULL | NULL | 5.6 |
  19. | NULL | NULL | 1 | NULL | NULL | NULL |
  20. | 8.760 | 8.760 | NULL | NULL | NULL | 8.8 |
  21. +----------+---------+------+------+---------+-----------+
  22. 11 rows in set (0.08 sec)

4.4.2字符串函数 (用来进行字符串操作的函数)

4.4.2.1CONCAT — 拼接

语法:CONCAT(str1, str2, str3)
MySQL中使用 CONCAT 函数进行拼接。

4.4.2.2LENGTH — 字符串长度

语法:LENGTH( 字符串 )

4.4.2.3LOWER — 小写转换

LOWER 函数只能针对英文字母使用,它会将参数中的字符串全都转换为小写。该函数不适用于英文字母以外的场合,不影响原本就是小写的字符。
类似的, UPPER 函数用于大写转换。

4.4.2.4REPLACE — 字符串的替换

语法:REPLACE( 对象字符串,替换前的字符串,替换后的字符串 )

4.4.2.5SUBSTRING — 字符串的截取

语法:SUBSTRING (对象字符串 FROM 截取的起始位置 FOR 截取的字符数)
使用 SUBSTRING 函数 可以截取出字符串中的一部分字符串。截取的起始位置从字符串最左侧开始计算,索引值起始为1。

4.4.2.6SUBSTRING_INDEX — 字符串按索引截取

语法:SUBSTRING_INDEX (原始字符串, 分隔符,n)
该函数用来获取原始字符串按照分隔符分割后,第 n 个分隔符之前(或之后)的子字符串,支持正向和反向索引,索引起始值分别为 1 和 -1。

  1. -- DDL :创建表
  2. USE shop;
  3. DROP TABLE IF EXISTS samplestr;
  4. CREATE TABLE samplestr
  5. (str1 VARCHAR (40),
  6. str2 VARCHAR (40),
  7. str3 VARCHAR (40)
  8. );
  9. -- DML:插入数据
  10. START TRANSACTION;
  11. INSERT INTO samplestr (str1, str2, str3) VALUES ('opx', 'rt', NULL);
  12. INSERT INTO samplestr (str1, str2, str3) VALUES ('abc', 'def', NULL);
  13. INSERT INTO samplestr (str1, str2, str3) VALUES ('太阳', '月亮', '火星');
  14. INSERT INTO samplestr (str1, str2, str3) VALUES ('aaa', NULL, NULL);
  15. INSERT INTO samplestr (str1, str2, str3) VALUES (NULL, 'xyz', NULL);
  16. INSERT INTO samplestr (str1, str2, str3) VALUES ('@!#$%', NULL, NULL);
  17. INSERT INTO samplestr (str1, str2, str3) VALUES ('ABC', NULL, NULL);
  18. INSERT INTO samplestr (str1, str2, str3) VALUES ('aBC', NULL, NULL);
  19. INSERT INTO samplestr (str1, str2, str3) VALUES ('abc哈哈', 'abc', 'ABC');
  20. INSERT INTO samplestr (str1, str2, str3) VALUES ('abcdefabc', 'abc', 'ABC');
  21. INSERT INTO samplestr (str1, str2, str3) VALUES ('micmic', 'i', 'I');
  22. COMMIT;
  23. -- 确认表中的内容
  24. SELECT * FROM samplestr;
  25. +-----------+------+------+
  26. | str1 | str2 | str3 |
  27. +-----------+------+------+
  28. | opx | rt | NULL |
  29. | abc | def | NULL |
  30. | 太阳 | 月亮 | 火星 |
  31. | aaa | NULL | NULL |
  32. | NULL | xyz | NULL |
  33. | @!#$% | NULL | NULL |
  34. | ABC | NULL | NULL |
  35. | aBC | NULL | NULL |
  36. | abc哈哈 | abc | ABC |
  37. | abcdefabc | abc | ABC |
  38. | micmic | i | I |
  39. +-----------+------+------+
  40. 11 rows in set (0.00 sec)
  41. --获取第一个元素
  42. SELECT SUBSTRING_INDEX('www.mysql.com', '.', 2);
  43. +------------------------------------------+
  44. | SUBSTRING_INDEX('www.mysql.com', '.', 2) |
  45. +------------------------------------------+
  46. | www.mysql |
  47. +------------------------------------------+
  48. 1 row in set (0.00 sec)
  49. SELECT SUBSTRING_INDEX('www.mysql.com', '.', -2);
  50. +-------------------------------------------+
  51. | SUBSTRING_INDEX('www.mysql.com', '.', -2) |
  52. +-------------------------------------------+
  53. | mysql.com |
  54. +-------------------------------------------+
  55. 1 row in set (0.00 sec)
  56. -- 获取第2个元素/第n个元素可以采用二次拆分的写法
  57. SELECT SUBSTRING_INDEX('www.mysql.com', '.', 1);
  58. +------------------------------------------+
  59. | SUBSTRING_INDEX('www.mysql.com', '.', 1) |
  60. +------------------------------------------+
  61. | www |
  62. +------------------------------------------+
  63. 1 row in set (0.00 sec)
  64. SELECT SUBSTRING_INDEX(SUBSTRING_INDEX('www.mysql.com', '.', 2), '.', -1);
  65. +--------------------------------------------------------------------+
  66. | SUBSTRING_INDEX(SUBSTRING_INDEX('www.mysql.com', '.', 2), '.', -1) |
  67. +--------------------------------------------------------------------+
  68. | mysql |
  69. +--------------------------------------------------------------------+
  70. 1 row in set (0.00 sec)

image.png

4.4.3日期函数 (用来进行日期操作的函数)

不同DBMS的日期函数语法各有不同,下面介绍一些被标准 SQL 承认的可以应用于绝大多数 DBMS 的函数。
特定DBMS的日期函数查阅文档即可。

4.4.3.1CURRENT_DATE — 获取当前日期

4.4.3.2CURRENT_TIME — 当前时间

4.4.3.3CURRENT_TIMESTAMP — 当前日期和时间

4.4.3.4EXTRACT — 截取日期元素

语法:EXTRACT(日期元素 FROM 日期)
使用 EXTRACT 函数可以截取出日期数据中的一部分,例如“年”
“月”,或者“小时”“秒”等。该函数的返回值并不是日期类型而是数值类型

  1. SELECT CURRENT_DATE;
  2. +--------------+
  3. | CURRENT_DATE |
  4. +--------------+
  5. | 2020-08-08 |
  6. +--------------+
  7. 1 row in set (0.00 sec)
  8. SELECT CURRENT_TIME;
  9. +--------------+
  10. | CURRENT_TIME |
  11. +--------------+
  12. | 17:26:09 |
  13. +--------------+
  14. 1 row in set (0.00 sec)
  15. SELECT CURRENT_TIMESTAMP;
  16. +---------------------+
  17. | CURRENT_TIMESTAMP |
  18. +---------------------+
  19. | 2020-08-08 17:27:07 |
  20. +---------------------+
  21. 1 row in set (0.00 sec)
  22. SELECT CURRENT_TIMESTAMP as now,
  23. EXTRACT(YEAR FROM CURRENT_TIMESTAMP) AS year,
  24. EXTRACT(MONTH FROM CURRENT_TIMESTAMP) AS month,
  25. EXTRACT(DAY FROM CURRENT_TIMESTAMP) AS day,
  26. EXTRACT(HOUR FROM CURRENT_TIMESTAMP) AS hour,
  27. EXTRACT(MINUTE FROM CURRENT_TIMESTAMP) AS MINute,
  28. EXTRACT(SECOND FROM CURRENT_TIMESTAMP) AS second;
  29. +---------------------+------+-------+------+------+--------+--------+
  30. | now | year | month | day | hour | MINute | second |
  31. +---------------------+------+-------+------+------+--------+--------+
  32. | 2020-08-08 17:34:38 | 2020 | 8 | 8 | 17 | 34 | 38 |
  33. +---------------------+------+-------+------+------+--------+--------+
  34. 1 row in set (0.00 sec)

4.4.4转换函数 (用来转换数据类型和值的函数)

一是数据类型的转换,简称为类型转换,在英语中称为cast
另一层意思是值的转换。

4.4.4.1CAST — 类型转换

语法:CAST(转换前的值 AS 想要转换的数据类型)

4.4.4.2COALESCE — 将NULL转换为其他值

语法:COALESCE(数据1,数据2,数据3……)
COALESCE 是 SQL 特有的函数。该函数会返回可变参数 A 中左侧开始第 1个不是NULL的值。
参数个数是可变的,因此可以根据需要无限增加。
在 SQL 语句中将 NULL 转换为其他值时就会用到转换函数。

  1. -- 将字符串类型转换为数值类型
  2. SELECT CAST('0001' AS SIGNED INTEGER) AS int_col;
  3. +---------+
  4. | int_col |
  5. +---------+
  6. | 1 |
  7. +---------+
  8. 1 row in set (0.00 sec)
  9. -- 将字符串类型转换为日期类型
  10. SELECT CAST('2009-12-14' AS DATE) AS date_col;
  11. +------------+
  12. | date_col |
  13. +------------+
  14. | 2009-12-14 |
  15. +------------+
  16. 1 row in set (0.00 sec)
  17. SELECT COALESCE(NULL, 11) AS col_1,
  18. COALESCE(NULL, 'hello world', NULL) AS col_2,
  19. COALESCE(NULL, NULL, '2020-11-01') AS col_3;
  20. +-------+-------------+------------+
  21. | col_1 | col_2 | col_3 |
  22. +-------+-------------+------------+
  23. | 11 | hello world | 2020-11-01 |
  24. +-------+-------------+------------+
  25. 1 row in set (0.00 sec)

4.4.5聚合函数 (用来进行数据聚合的函数)

4.5谓词

谓词就是返回值为真值的函数。包括TRUE / FALSE / UNKNOWN
谓词主要有以下几个:

4.5.1LIKE,字符串部分一致性查询

对字符串的一部分进行一致性查询时使用。

  • _下划线匹配任意 1 个字符
  • %是代表“零个或多个任意字符串”的特殊符号

根据%放的位置,分为前方一致,中间一致和后方一致3种类型。

  1. -- DDL :创建表
  2. CREATE TABLE samplelike
  3. ( strcol VARCHAR(6) NOT NULL,
  4. PRIMARY KEY (strcol)
  5. samplelike);
  6. -- DML :插入数据
  7. START TRANSACTION; -- 开始事务
  8. INSERT INTO samplelike (strcol) VALUES ('abcddd');
  9. INSERT INTO samplelike (strcol) VALUES ('dddabc');
  10. INSERT INTO samplelike (strcol) VALUES ('abdddc');
  11. INSERT INTO samplelike (strcol) VALUES ('abcdd');
  12. INSERT INTO samplelike (strcol) VALUES ('ddabc');
  13. INSERT INTO samplelike (strcol) VALUES ('abddc');
  14. COMMIT; -- 提交事务
  15. SELECT * FROM samplelike;
  16. +--------+
  17. | strcol |
  18. +--------+
  19. | abcdd |
  20. | abcddd |
  21. | abddc |
  22. | abdddc |
  23. | ddabc |
  24. | dddabc |
  25. +--------+
  26. 6 rows in set (0.00 sec)
  27. -- 前方一致:选取出“dddabc
  28. SELECT * FROM samplelike
  29. WHERE strcol LIKE 'ddd%';
  30. +--------+
  31. | strcol |
  32. +--------+
  33. | dddabc |
  34. +--------+
  35. 1 row in set (0.00 sec)
  36. -- 中间一致:选取出“abcddd”“dddabc”“abdddc
  37. SELECT *
  38. FROM samplelike
  39. WHERE strcol LIKE '%ddd%';
  40. +--------+
  41. | strcol |
  42. +--------+
  43. | abcddd |
  44. | abdddc |
  45. | dddabc |
  46. +--------+
  47. 3 rows in set (0.00 sec)
  48. -- 后方一致:选取出“abcddd
  49. SELECT *
  50. FROM samplelike
  51. WHERE strcol LIKE '%ddd';
  52. +--------+
  53. | strcol |
  54. +--------+
  55. | abcddd |
  56. +--------+
  57. 1 row in set (0.00 sec)
  58. --_下划线匹配任意 1 个字符
  59. SELECT *
  60. FROM samplelike
  61. WHERE strcol LIKE 'abc__';
  62. +--------+
  63. | strcol |
  64. +--------+
  65. | abcdd |
  66. +--------+
  67. 1 row in set (0.00 sec)

4.5.2BETWEEN,用于范围查询

范围查询,需要3个参数。闭区间。
不包括临界值,则使用< 和 >

  1. -- 选取销售单价为100 1000元的商品
  2. SELECT product_name, sale_price
  3. FROM product
  4. WHERE sale_price BETWEEN 100 AND 1000;
  5. +--------------+------------+
  6. | product_name | sale_price |
  7. +--------------+------------+
  8. | T | 1000 |
  9. | 打孔器 | 500 |
  10. | 叉子 | 500 |
  11. | 擦菜板 | 880 |
  12. | 圆珠笔 | 100 |
  13. +--------------+------------+
  14. 5 rows in set (0.00 sec)
  15. -- SELECT product_name, sale_price
  16. FROM product
  17. WHERE sale_price > 100
  18. AND sale_price < 1000;
  19. +--------------+------------+
  20. | product_name | sale_price |
  21. +--------------+------------+
  22. | 打孔器 | 500 |
  23. | 叉子 | 500 |
  24. | 擦菜板 | 880 |
  25. +--------------+------------+
  26. 3 rows in set (0.00 sec)

4.5.3IS NULL、IS NOT NULL, 判断是否为null

不能使用=来获得是null的值。

  1. SELECT product_name, purchase_price
  2. FROM product
  3. WHERE purchase_price IS NULL;
  4. +--------------+----------------+
  5. | product_name | purchase_price |
  6. +--------------+----------------+
  7. | 叉子 | NULL |
  8. | 圆珠笔 | NULL |
  9. +--------------+----------------+
  10. 2 rows in set (0.00 sec)
  11. SELECT product_name, purchase_price
  12. FROM product
  13. WHERE purchase_price IS NOT NULL;
  14. +--------------+----------------+
  15. | product_name | purchase_price |
  16. +--------------+----------------+
  17. | T | 500 |
  18. | 打孔器 | 320 |
  19. | 运动T | 2800 |
  20. | 菜刀 | 2800 |
  21. | 高压锅 | 5000 |
  22. | 擦菜板 | 790 |
  23. +--------------+----------------+
  24. 6 rows in set (0.00 sec)

4.5.4IN:or的简便用法

也有not in,但不能选出null。

  1. SELECT product_name, purchase_price
  2. FROM product
  3. WHERE purchase_price IN (320, 500, 5000);
  4. +--------------+----------------+
  5. | product_name | purchase_price |
  6. +--------------+----------------+
  7. | T | 500 |
  8. | 打孔器 | 320 |
  9. | 高压锅 | 5000 |
  10. +--------------+----------------+
  11. 3 rows in set (0.00 sec)
  12. SELECT product_name, purchase_price
  13. FROM product
  14. WHERE purchase_price NOT IN (320, 500, 5000);
  15. +--------------+----------------+
  16. | product_name | purchase_price |
  17. +--------------+----------------+
  18. | 运动T | 2800 |
  19. | 菜刀 | 2800 |
  20. | 擦菜板 | 790 |
  21. +--------------+----------------+
  22. 3 rows in set (0.00 sec)

作为子查询的参数

  1. -- DDL :创建表
  2. DROP TABLE IF EXISTS shopproduct;
  3. CREATE TABLE shopproduct
  4. ( shop_id CHAR(4) NOT NULL,
  5. shop_name VARCHAR(200) NOT NULL,
  6. product_id CHAR(4) NOT NULL,
  7. quantity INTEGER NOT NULL,
  8. PRIMARY KEY (shop_id, product_id) -- 指定主键
  9. );
  10. -- DML :插入数据
  11. START TRANSACTION; -- 开始事务
  12. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0001', 30);
  13. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0002', 50);
  14. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000A', '东京', '0003', 15);
  15. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0002', 30);
  16. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0003', 120);
  17. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0004', 20);
  18. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0006', 10);
  19. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000B', '名古屋', '0007', 40);
  20. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0003', 20);
  21. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0004', 50);
  22. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0006', 90);
  23. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000C', '大阪', '0007', 70);
  24. INSERT INTO shopproduct (shop_id, shop_name, product_id, quantity) VALUES ('000D', '福冈', '0001', 100);
  25. COMMIT; -- 提交事务
  26. SELECT * FROM shopproduct;
  27. +---------+-----------+------------+----------+
  28. | shop_id | shop_name | product_id | quantity |
  29. +---------+-----------+------------+----------+
  30. | 000A | 东京 | 0001 | 30 |
  31. | 000A | 东京 | 0002 | 50 |
  32. | 000A | 东京 | 0003 | 15 |
  33. | 000B | 名古屋 | 0002 | 30 |
  34. | 000B | 名古屋 | 0003 | 120 |
  35. | 000B | 名古屋 | 0004 | 20 |
  36. | 000B | 名古屋 | 0006 | 10 |
  37. | 000B | 名古屋 | 0007 | 40 |
  38. | 000C | 大阪 | 0003 | 20 |
  39. | 000C | 大阪 | 0004 | 50 |
  40. | 000C | 大阪 | 0006 | 90 |
  41. | 000C | 大阪 | 0007 | 70 |
  42. | 000D | 福冈 | 0001 | 100 |
  43. +---------+-----------+------------+----------+
  44. 13 rows in set (0.00 sec)
  45. -- 取出大阪在售商品的销售单价
  46. -- step1:取出大阪门店的在售商品 `product_id`
  47. SELECT product_id
  48. FROM shopproduct
  49. WHERE shop_id = '000C';
  50. +------------+
  51. | product_id |
  52. +------------+
  53. | 0003 |
  54. | 0004 |
  55. | 0006 |
  56. | 0007 |
  57. +------------+
  58. 4 rows in set (0.00 sec)
  59. -- step2:取出大阪门店在售商品的销售单价 `sale_price`
  60. SELECT product_name, sale_price
  61. FROM product
  62. WHERE product_id IN (SELECT product_id
  63. FROM shopproduct
  64. WHERE shop_id = '000C');
  65. +--------------+------------+
  66. | product_name | sale_price |
  67. +--------------+------------+
  68. | 运动T | 4000 |
  69. | 菜刀 | 3000 |
  70. | 叉子 | 500 |
  71. | 擦菜板 | 880 |
  72. +--------------+------------+
  73. 4 rows in set (0.00 sec)
  74. -- 子查询展开后的结果
  75. SELECT product_name, sale_price
  76. FROM product
  77. WHERE product_id IN ('0003', '0004', '0006', '0007');
  78. +--------------+------------+
  79. | product_name | sale_price |
  80. +--------------+------------+
  81. | 运动T | 4000 |
  82. | 菜刀 | 3000 |
  83. | 叉子 | 500 |
  84. | 擦菜板 | 880 |
  85. +--------------+------------+
  86. 4 rows in set (0.00 sec)
  87. -- not in
  88. -- NOT IN 使用子查询作为参数,取出未在大阪门店销售的商品的销售单价
  89. SELECT product_name, sale_price
  90. FROM product
  91. WHERE product_id NOT IN (SELECT product_id
  92. FROM shopproduct
  93. WHERE shop_id = '000A');
  94. +--------------+------------+
  95. | product_name | sale_price |
  96. +--------------+------------+
  97. | 菜刀 | 3000 |
  98. | 高压锅 | 6800 |
  99. | 叉子 | 500 |
  100. | 擦菜板 | 880 |
  101. | 圆珠笔 | 100 |
  102. +--------------+------------+
  103. 5 rows in set (0.00 sec)

4.5.5EXISTS:判断是否存在满足条件的记录

谓词的作用就是 “判断是否存在满足某种条件的记录”
如果存在这样的记录就返回真(TRUE),如果不存在就返回假(FALSE)。
EXIST,基本上也都可以使用 IN(或者 NOT IN)来代替
EXIST(存在)谓词的主语是“记录”。
只有1个参数,右侧,通常是一个子查询。

  1. SELECT product_name, sale_price
  2. FROM product AS p
  3. WHERE EXISTS (SELECT 1 -- 这里可以书写适当的常数
  4. FROM shopproduct AS sp
  5. WHERE sp.shop_id = '000C'
  6. AND sp.product_id = p.product_id);
  7. +--------------+------------+
  8. | product_name | sale_price |
  9. +--------------+------------+
  10. | 运动T | 4000 |
  11. | 菜刀 | 3000 |
  12. | 叉子 | 500 |
  13. | 擦菜板 | 880 |
  14. +--------------+------------+
  15. 4 rows in set (0.00 sec)

用not exist替换not in

  1. SELECT product_name, sale_price
  2. FROM product AS p
  3. WHERE NOT EXISTS (SELECT *
  4. FROM shopproduct AS sp
  5. WHERE sp.shop_id = '000A'
  6. AND sp.product_id = p.product_id);
  7. +--------------+------------+
  8. | product_name | sale_price |
  9. +--------------+------------+
  10. | 菜刀 | 3000 |
  11. | 高压锅 | 6800 |
  12. | 叉子 | 500 |
  13. | 擦菜板 | 880 |
  14. | 圆珠笔 | 100 |
  15. +--------------+------------+
  16. 5 rows in set (0.00 sec)

4.6case表达式

条件分支。
语法分为简单CASE表达式和搜索CASE表达式两种。
搜索CASE表达式包含简单CASE表达式的全部功能。
依次判断 when 表达式是否为真值,是则执行 THEN 后的语句,如果所有的 when 表达式均为假,则执行 ELSE 后的语句。
无论多么庞大的 CASE 表达式,最后也只会返回一个值。
else可以省略,会认为是else null,但end不能省略。

行转列:

  • 当待转换列为数字时,可以使用SUM AVG MAX MIN等聚合函数;
  • 当待转换列为文本时,可以使用MAX MIN等聚合函数 ```sql CASE WHEN <求值表达式> THEN <表达式> WHEN <求值表达式> THEN <表达式> WHEN <求值表达式> THEN <表达式> . . . ELSE <表达式> END

应用场景1:根据不同分支得到不同列值 SELECT product_name, CASE WHEN product_type = ‘衣服’ THEN CONCAT(‘A : ‘,product_type) WHEN product_type = ‘办公用品’ THEN CONCAT(‘B : ‘,product_type) WHEN product_type = ‘厨房用具’ THEN CONCAT(‘C : ‘,product_type) ELSE NULL END AS abc_product_type FROM product; +———————+—————————+ | product_name | abc_product_type | +———————+—————————+ | T恤 | A : 衣服 | | 打孔器 | B : 办公用品 | | 运动T恤 | A : 衣服 | | 菜刀 | C : 厨房用具 | | 高压锅 | C : 厨房用具 | | 叉子 | C : 厨房用具 | | 擦菜板 | C : 厨房用具 | | 圆珠笔 | B : 办公用品 | +———————+—————————+ 8 rows in set (0.00 sec)

应用场景2:实现列方向上的聚合 SELECT product_type, SUM(sale_price) AS sum_price FROM product GROUP BY product_type;
+———————+—————-+ | product_type | sum_price | +———————+—————-+ | 衣服 | 5000 | | 办公用品 | 600 | | 厨房用具 | 11180 | +———————+—————-+ 3 rows in set (0.00 sec)

— 在列的方向上展示不同种类额聚合值 sum_price_clothes | sum_price_kitchen | sum_price_office —————————+—————————-+————————- 5000 | 11180 | 600

— 对按照商品种类计算出的销售单价合计值进行行列转换 SELECT SUM(CASE WHEN product_type = ‘衣服’ THEN sale_price ELSE 0 END) AS sum_price_clothes, SUM(CASE WHEN product_type = ‘厨房用具’ THEN sale_price ELSE 0 END) AS sum_price_kitchen, SUM(CASE WHEN product_type = ‘办公用品’ THEN sale_price ELSE 0 END) AS sum_price_office FROM product; +—————————-+—————————-+—————————+ | sum_price_clothes | sum_price_kitchen | sum_price_office | +—————————-+—————————-+—————————+ | 5000 | 11180 | 600 | +—————————-+—————————-+—————————+ 1 row in set (0.00 sec)

— CASE WHEN 实现数字列 score 行转列 SELECT name, SUM(CASE WHEN subject = ‘语文’ THEN score ELSE null END) as chinese, SUM(CASE WHEN subject = ‘数学’ THEN score ELSE null END) as math, SUM(CASE WHEN subject = ‘外语’ THEN score ELSE null END) as english FROM score GROUP BY name; +———+————-+———+————-+ | name | chinese | math | english | +———+————-+———+————-+ | 张三 | 93 | 88 | 91 | | 李四 | 87 | 90 | 77 | +———+————-+———+————-+ 2 rows in set (0.00 sec)

— CASE WHEN 实现文本列 subject 行转列 SELECT name, MAX(CASE WHEN subject = ‘语文’ THEN subject ELSE null END) as chinese, MAX(CASE WHEN subject = ‘数学’ THEN subject ELSE null END) as math, MIN(CASE WHEN subject = ‘外语’ THEN subject ELSE null END) as english FROM score GROUP BY name; +———+————-+———+————-+ | name | chinese | math | english | +———+————-+———+————-+ | 张三 | 语文 | 数学 | 外语 | | 李四 | 语文 | 数学 | 外语 | +———+————-+———+————-+ 2 rows in set (0.00 sec

  1. <a name="zk1QV"></a>
  2. ## 4.7练习题
  3. <a name="EDwxt"></a>
  4. ### 4.7.1
  5. 运算或者函数中含有 NULL 时,结果全都会变为NULL ?(判断题)
  6. <a name="W8KNu"></a>
  7. ### 4.7.2
  8. 对本章中使用的 product(商品)表执行如下 2 条 SELECT 语句,能够得到什么样的结果呢?
  9. ```sql
  10. -- 1
  11. SELECT product_name, purchase_price
  12. FROM product
  13. WHERE purchase_price NOT IN (500, 2800, 5000);
  14. -- 2
  15. SELECT product_name, purchase_price
  16. FROM product
  17. WHERE purchase_price NOT IN (500, 2800, 5000, NULL);

-1的输出结果:
image.png
2的输出结果:
image.png

4.7.3

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

  • 低档商品:销售单价在1000日元以下(T恤衫、办公用品、叉子、擦菜板、 圆珠笔)
  • 中档商品:销售单价在1001日元以上3000日元以下(菜刀)
  • 高档商品:销售单价在3001日元以上(运动T恤、高压锅)

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

  1. low_price | mid_price | high_price
  2. ----------+-----------+------------
  3. 5 | 1 | 2