MySQL语法学习笔记(持续更)

MySQL语法学习笔记

学习之道,非尽心竭力者不能进也!我是唧唧又唧唧,欢迎查看我的笔记,有问题欢迎交流探讨。


SQL是一种结构查询语言,用于查询关系数据库的标准语言,包括若干关键字和一致的语法,便于数据库元件(表、索引、字段等)的建立和操纵。

目录



1.MySQL建库

1.1建库语句

create database emp   #建库 名为 emp
default character set utf-8   #设置该库的默认编码格式为 utf-8
collate utf8_general_ci;     #设置数据库校对规则。不区分大小写

示例中 utf8_bin将字符串中的每一个字符用二进制数据存储,区分大小写。utf8_genera_ci不区分大小写,ci为case insensitive的缩写,即大小写不敏感。utf8_general_cs区分大小写,cs为case sensitive的缩写,即大小写敏感。

1.2删除库

drop database emp;

2.建表

2.1建表模板

CREATE TABLE customers
(
  cust_id      int       NOT NULL AUTO_INCREMENT,
  cust_name    char(50)  NOT NULL ,
  cust_address char(50)  NULL ,
  cust_city    char(50)  NULL ,
  cust_state   char(5)   NULL ,
  cust_zip     char(10)  NULL ,
  cust_country char(50)  NULL ,
  cust_contact char(50)  NULL ,
  cust_email   char(255) NULL ,
  PRIMARY KEY (cust_id)
) ENGINE=InnoDB;

2.2 主键(PRIMARY KEY)

主键值必须唯一:表中的每个行必须具有唯一的主键值。
如果主键使用单个列,则它的值必须唯一。
迄今为止我们看到的CREATE TABLE例子都是用单个列作为主键。

PRIMARY KEY (cust_id)

如果使用多个列,则这些列的组合值必须唯一 。
创建由多个列组成的主键,应该以逗号分隔的列表给出各列名。

PRIMARY KEY (order_num,order_item)

主键中只能使用不允许NULL值的列,允许NULL值的列不能作为唯一标识。

2.3AUTO_INCREMENT

AUTO_INCREMENT定义列为自增的属性,一般用于主键,每次执行一个INSERT操作时,数值会自动加1
使用的最简单的编号是下一个编号,所谓下一个编号是大于当前最大编号的编号。例如,如果cust_id的最大编号为10005,则插入表中的下一个顾客,可以具有等于10006的 cust_id 。

2.4 默认值

如果在插入行时没有给出值,MySQL允许指定此时使用的默认值。默认值 用CREATE TABLE语句的列定义中的DEFAULT关键字指定

2.5 存储引擎(ENGINE=InnoDB)

与其他DBMS一样,MySQL有一个具体管理和处理数据的内部引擎。
在你使用CREATE TABLE语句时,该引擎具体创建表。
而在你使用SELECT语句 或进行其他数据库处理时,该引擎在内部处理你的请求。
多数时候,此引擎 都隐藏在DBMS内,不需要过多关注它。

以下是几个需要知道的引擎:

  • InnoDB是一个可靠的事务处理引擎,它不支持全文本搜索;

  • MEMORY在功能等同于MyISAM,但由于数据存储在内存(不是磁盘)中,速度很快(特别适合于临时表);

  • MyISAM是一个性能极高的引擎,它支持全文本搜索但不支持事务处理 。

2.6 ALTER TABLE 语句

ALTER TABLE 语句用于在已有的表中添加、删除或修改列。

-- 表中添加列
ALTER TABLE customers
ADD cust_phone char(10)

-- 删除表中的列(请注意,某些数据库系统不允许这种在数据库表中删除列的方式)
ALTER TABLE customers
DROP COLUMN cust_phone

-- 改变表中列的数据类型
-- 修改数量字段的默认值为80
ALTER TABLE customers
MODIFY COLUMN quantity int(11) null default 80 after prod_id

2.7 删除表

DROP TABLE customers

3.SQL处理数据的基本方法

3.1 数据检索(select 语句)

Select 语句语法
Select 检索所有列
Select 检索单个列
Select 检索不同行
Select 的结果限定

# 描述表
DESC products;
# select 语句 检索所有列
SELECT * FROM products;
# select 语句 检索指定的多个列
SELECT prod_id,prod_name,prod_price FROM products;
# select 语句 检索指定的一个列
SELECT vend_id FROM products;
# 去重,DISTINCT 关键词用于返回唯一不同的值
SELECT distinct vend_id FROM products;
# 第1行开始,取前3行
SELECT * FROM products limit 3;
# 第4行开始,取3行
SELECT * FROM products limit 3,3;

3.2 数据排序

ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。
ORDER BY 关键字默认按照升序对记录进行排序。如果需要按照降序对记录进行排序,可以使用 DESC 关键字。

# 先查看一下这次用的products表
SELECT * from products;
# 单个字段的排序 升序 降序
SELECT prod_id,prod_price from products
ORDER BY prod_price; 
# 默认(ASC)是升序,desc 是降序,默认可以不写
SELECT prod_id,prod_price from products
ORDER BY prod_price desc; 
# 默认排序a-z ,反序就是z-a 
SELECT prod_id,prod_price from products
ORDER BY prod_name desc; 
# 多字段排序
SELECT prod_name,prod_price from products
ORDER BY prod_price,prod_name;
# 找出最便宜的产品
SELECT prod_name,prod_price from products
ORDER BY prod_price LIMIT 1; 
# 找出最贵的产品
SELECT * from products
ORDER BY prod_price DESC LIMIT 1; 

3.3 数据过滤

数据库表一般包含大量的数据,很少需要检索表中所有行。
通常只会根据特定操作或报告的需要提取表数据的子集。
只检索所需数据需要指定搜索条件(search criteria)
搜索条件也称为过滤条件(filter condition)。
在SELECT语句中,数据根据WHERE子句中指定的搜索条件进行过滤。
WHERE子句在表名 (FROM子句)之后给出。

3.3.1 使用Where子句

检查单个值
不匹配检查

# 筛选价格是2.5的产品
select * from products where prod_price = 2.5;
# 筛选价格是2.5的产品,并只显示特定的字段
select prod_name,prod_price from products 
where prod_price = 2.5;

# WHERE 条件操作符:=、>、<、>=、<=、<>/!=、between * and *(包含首尾)
select prod_name,prod_price from products 
where prod_price >= 2.5 and prod_price <10
order by prod_price;

select prod_name,prod_price from products 
where prod_price BETWEEN 2.5 and 10 
order by prod_price;

练习:
找出价格低于10元的产品
找出价格不是供应商1003制造的产品
找出供应商1001,1003 制造的产品

select prod_name,prod_price from products 
where prod_price < 10
order by prod_price;

select vend_id,prod_name,prod_price from products 
where vend_id != 1003
order by prod_price;

select vend_id,prod_name,prod_price from products 
where vend_id = 1001 or vend_id = 1003;
order by prod_price;

3.3.2 空值检查

select * from products
where prod_desc is null

3.3.3 And、Or、In、Not操作及其计算次序

  • AND & OR 运算符用于基于一个以上的条件对记录进行过滤。
  • 如果第一个条件和第二个条件都成立,则 AND 运算符显示一条记录。
  • 如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。
  • 先and语句后执行or语句
# where 组合子句之 and 操作(交集)
select * from products where vend_id=1003 and prod_price <=10
ORDER BY prod_price;
# where 组合子句之 or 操作(并集)
select * from products where vend_id=1003 or vend_id =1002# where 组合子句之 AND 和 OR 的结合使用(使用圆括号来组成复杂的表达式)
SELECT * FROM products
WHERE prod_price <=10
AND (vend_id=1003 or vend_id =1002);
  • IN 操作符允许在 WHERE 子句中规定多个值
  • ‘=’ 规定一个值
# where 组合子句之 in 操作
select * from products where 
vend_id in (1001,1005,1002);
# where 组合子句之 in 与  = 的转换
select * from products where 
vend_id = 1001 or vend_id = 1005 or vend_id = 1002;
# where 组合子句之 not 操作
select * from products where 
vend_id not in (1002);
  • not操作
# where 组合子句之 not 操作
select * from products where 
vend_id not in (1002);

3.3.4 通配符(模糊匹配)

  • 通配符可用于替代字符串中的任何其他字符。
  • % 替代 0 个或多个字符
  • “_” 替代1个字符。
  • LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式
# 找到以 jet开头的产品
select prod_id ,prod_name from products
where prod_name like 'jet%'

在这里插入图片描述

select prod_id ,prod_name from products
where prod_name like '%anvil%'

select prod_id ,prod_name from products
where prod_name like 's%e'

select prod_id ,prod_name from products
where prod_name like '_ ton anvil'

select prod_id ,prod_name from products
where prod_name like '__ ton anvil'

注意:

  • 不要过度使用通配符,如果其他操作符能达到相同的目的,应优先使用其他操作符。
  • 非必要情况下,不要把通配符用在搜索模式的开始处,因为这样搜索起来是最慢的。
  • 仔细注意通配符的位置,如果放错地方可能不会返回想要的数据。

4.SQL处理数据高级方法

4.1正则表达式

正则表达式 用特殊的字符集合与一个文本串进行比较,过滤检索出想要的数据

  • 正则表达式是用来匹配文本的特殊的串(字符集合)
  • 如果你想从一个文本文件中提取电话号码,可以使用正则表达式。
  • 如果你需要查找名字中间有数字的所有文件,可以使用一个 正则表达式。
  • 如果你想在一个文本块中找到所有重复的单词,可以使用一 个正则表达式。
  • 如果你想替换一个页面中的所有URL为这些URL的实际 HTML链接,也可以使用一个正则表达式(对于最后这个例子,或者是两个正则表达式)。

4.1.1基本字符串匹配

# 使用 like 关键字和通配符 % 
select prod_name from products
where prod_name like '%1000';
# 使用正则表达式 REGEXP
select prod_name from products
where prod_name REGEXP '1000';
# 可以检索出prod_name 中所有含有‘1000’的行

在这里插入图片描述

4.1.2 特殊字符“ .” 的使用

“ .” 在正则表达式中表示匹配任意一个字符

# 使用 like 关键字和通配符 _
select prod_name from products
where prod_name like 'JetPack _000';
# 使用正则表达式 REGEXP 和特殊字符“.”
select prod_name from products
where prod_name REGEXP '.000';

在这里插入图片描述

  • 在这里会不会有人觉得奇怪,“.” 表示的是匹配任意一个字符,但结果显示的 JetPack 1000、JetPack
    2000,000的前面可不止一个字符,为什么能这样匹配呢?
  • 好好理解这句话 “正则表达式是用来匹配文本的特殊的串”
    你会发现利用正则表达式我们可以匹配出相应字段中所有含有需要匹配的文本的行。例子中我们需要匹配的是‘.000’,.
    可以代表任何一个字符,所以我们把 JetPack 1000、JetPack 2000
    匹配出来了,1000,2000就是我们想要匹配的文本。不管他们前后还有没有别的字符都会被查询的到。
  • 对比 关键字 like 与通配符的联合使用就无法达到这种效果了。

4.1.3 使用 or 进行匹配(条件匹配)

此 or 不是真的 or ,而是使用竖线 “|” 表示搜索 两个匹配文本串的其中一个

# 检索prod_name 中所有含有‘1000’或者‘2000’的行
select prod_name from products
where prod_name REGEXP '1000|2000';

在这里插入图片描述
正则表达式中可使用多个 or 条件

select prod_name from products
where prod_name REGEXP '1000|2000|anvil';

在这里插入图片描述

4.1.4 []匹配

”[]“代表需要匹配[]中所包含的任意一个字符

4.1.4.1 匹配几个字符之一

select prod_name from products
where prod_name REGEXP '[12] ton';

这里使用正则表达式 [12] ton 。 [12] 定义了一组字符1,2;可以匹配 1 ton 或者 2 ton.
其实,[] 是另一种形式的 or 语句

4.1.4.2 匹配范围

[]匹配[]中所包含的任意一个字符
[0-9] 匹配0到9的任意数字字符
[1-3] 匹配1到3的任意数字字符
[6-9] 匹配6到9的任意数字字符
[a-z] 匹配a到z 的任意字母字符

select prod_name from products
where prod_name REGEXP '[1-5] ton';
# 匹配1到5任意一个数字,所以返回了1 ton、2 ton、5 ton

在这里插入图片描述

4.1.4.3 排他符[^]

  • [^] 匹配未包含在[]中的任意字符。即,将匹配除指定字符外的任何东西。
  • 在集合开始处放置一个 ^
  • 例如,[^12],会匹配除1,2外的任何东西
select prod_name from products
where prod_name REGEXP '[^345] ton'
# 不会出现 3 ton、4 ton、5 ton

在这里插入图片描述

4.1.5 特殊字符的匹配(. [] | -)

匹配特殊字符(. [] | -)时,需要使用 转义符(两个反斜杠\\)
\\- 表示查找 -,\\.表示查找 .

select prod_name from products
where prod_name REGEXP '\\.';
#查找 .

在这里插入图片描述
\\也用来引用元字符(具有特殊含义的字符)

元字符 说明
\\f 换页
\\n 换行
\\r 回车
\\t 制表
\\v 纵向制表

4.1.6 匹配字符类

[:digit:] 任意数字 (同[0-9])
[:alnum:] 任意字母和数字 (同[a-zA-Z0-9])
[:alpha:] 任意字母 (同[a-zA-Z])
[:lower:] 任意小写字母 (同[a-z])
[:upper:] 任意大写字母 (同[A-Z])

select prod_name from products
where prod_name REGEXP '[[:digit:]]{4}'

4.1.7 匹配多个实例,重复元字符

元字符 说明
* 0个或多个匹配
+ 1个或多个匹配 (同{1,})
? 0个或1个匹配(同{0,1})
{n} 指定数目的匹配
{n,} 不少于指定数目的匹配
{n,m} 匹配数目的范围(m不超过255)
#匹配连在一起的任意4位数字的行
select prod_name from products
here prod_name REGEXP '[[:digit:]]{4}';

select prod_name from products
where prod_name REGEXP '[0-9][0-9][0-9][0-9]';

在这里插入图片描述

select prod_name from products
where prod_name REGEXP '\\([0-9] sticks?\\)';
# \\( 、\\) 使用转义符,[0-9]匹配1到9任意数字
#s? 中 使用 ?表示 s 可以出现0次或1次,所以出现了结果stick、sticks

在这里插入图片描述

  • ? 匹配它前面的任何字符的0次或1次出现,换句话说,?前面的字符有(1个)或者没有(0个)都能被匹配

4.1.8 定位符,元字符

元字符 说明
^ 文本的开始
$ 文本的结尾
[[:<:]] 词的开始
[[:>:]] 词的结尾
select prod_name from products
where prod_name REGEXP '^[\\.]'

在这里插入图片描述
在这里,我们可以回顾一下排他符[^ ]
总结一下^ 的用途:
1.在集合中 [^],表示不匹配集合所包含的字符
2.匹配输入字符串的开始位置。

4.2 计算字段

4.2.1 什么是计算字段

  • 有时候,我们需要的是直接从数据库中检索出转换、计算或格式化过的数据;比如物品订单表存储的是物品的价格和数量,而有时候我们需要的是总价格,这时我们就可以通过计算字段创建我们需要的数据了。
  • 计算字段不实际存在于数据库表中,而是运行时在select语句内创建的字段(列),可以使用别名。

4.2.2 拼接字段

  • 将值联结到一起构成单个值
  • concat() 函数,可以拼接字符串,可以拼接两个列。拼接的串之间用逗号分隔
SELECT * FROM vendors;
SELECT CONCAT(vend_city,' (',vend_country,')') from vendors;

在这里插入图片描述

  • 使用别名:使用 as 关键字
  • as 后接着你想要赋予字段的名字
SELECT CONCAT(vend_city,' (',vend_country,')') as '位置' 
from vendors;

在这里插入图片描述

4.2.3 执行算术计算

  • 对检索出来的数据进行算术计算
select prod_id,quantity * item_price as total 
from orderitems
ORDER BY total;

在这里插入图片描述

  • MySQL算术操作符
操作符 说明
+
-
*
/

4.3 MySQL数据处理函数

函数类型 说明
文本函数 处理文本串(如删除或填充值,转换值得大小写)
数值函数 用于数值数据上的算术操作(如返回绝对值,进行代数计算 )
日期和时间函数 处理日期和时间值并从这些值中提取特定的成分(如返回两个日期之差,检查日期有效性等)
系统函数 返回DBSM正使用的特殊信息(如返回用户登录信息,检查版本细节)

4.3.1 文本处理函数

函数 作用
Rtrim(s) 去除字符串右边的空格
Ltrim(s) 去除字符串左边的空格
TRIM(s) 去除字符串开头结尾的空格
upper(s) 定义字符串全部大写
UCASE(s) 将字符串转成大写
lower(s) 定义字符串全部小写
LCASE(s) 将字符串全部转成小写
ASCII(s) 返回字符串 s 的第一个字符的 ASCII 码
CHAR_LENGTH(s) 返回字符串s的字符数
CHARACTER_LENGTH(s) 返回字符串s的字符数
CONCAT(s1,s2…sn) 合并字符串
MID(s,n,len) 从字符串 s 的 n 位置截取长度为 len 的子字符串,同 SUBSTRING(s,n,len)
substring(s,n,len) 从字符串 s 的 n 位置截取长度为 len 的子字符串
SPACE(n) 返回n个空格
RIGHT(s,n) 返回字符串 s 的后 n 个字符
LPAD(s1,len,s2) 在字符串 s1 的开始处填充字符串 s2,使字符串长度达到 len
RPAD(s1,len,s2) 在字符串 s1 的结尾处添加字符串 s2,使字符串的长度达到 len
INSERT(s1,x,len,s2) 字符串 s2 替换 s1 的 x 位置开始长度为 len 的字符串
FORMAT(x,n) 函数可以将数字 x 进行格式化 “#,###.##”, 将 x 保留到小数点后 n 位,最后一位四舍五入。
SELECT UPPER(RTRIM(vend_name))as vend_name FROM vendors;
SELECT vend_name,ASCII(vend_name) FROM vendors;

4.3.2 数值函数

函数 说明
ABS(x) 返回 x 的绝对值
ACOS(x) 求反余弦值(参数是弧度)
ASIN(x) 求反正弦值(参数是弧度)
ATAN(x) 求反正切值(参数是弧度)
AVG(expression) 返回一个表达式的平均值,expression 是一个字段
CEIL(x) 返回大于或等于 x 的最小整数
CEILING(x) 返回大于或等于 x 的最小整数
COS(x) 求余弦值(参数是弧度)
COT(x) 求余切值(参数是弧度)
COUNT(expression) 返回查询的记录总数,expression 参数是一个字段或者 * 号
DEGREES(x) 将弧度转换为角度
n DIV m 整除,n 为被除数,m 为除数
EXP(x) 返回 e 的 x 次方
FLOOR(x) 返回小于或等于 x 的最大整数
GREATEST(expr1, expr2, expr3, …) 返回列表中的最大值
LEAST(expr1, expr2, expr3, …) 返回列表中的最小值
LN() 返回数字的自然对数,以 e 为底。
LOG(x) 或 LOG(base, x) 返回自然对数(以 e 为底的对数),如果带有 base 参数,则 base 为指定带底数。
LOG10(x) 返回以 10 为底的对数
LOG2(x) 返回以 2 为底的对数
MAX(expression) 返回字段 expression 中的最大值
MIN(expression) 返回字段 expression 中的最小值
MOD(x,y) 返回 x 除以 y 以后的余数
PI() 返回圆周率(3.141593)
POW(x,y) 返回 x 的 y 次方
POWER(x,y) 返回 x 的 y 次方
RADIANS(x) 将角度转换为弧度
RAND() 返回 0 到 1 的随机数
ROUND(x) 返回离 x 最近的整数
SIGN(x) 返回 x 的符号,x 是负数、0、正数分别返回 -1、0 和 1
SIN(x) 求正弦值(参数是弧度)
SQRT(x) 返回x的平方根
SUM(expression) 返回指定字段的总和
TAN(x) 求正切值(参数是弧度)
TRUNCATE(x,y) 返回数值 x 保留到小数点后 y 位的值(与 ROUND 最大的区别是不会进行四舍五入)

4.3.3 日期函数

函数 说明
ADDDATE(d,n) 计算起始日期 d 加上 n 天的日期
ADDTIME(t,n) n 是一个时间表达式,时间 t 加上时间表达式 n
CURDATE() 返回当前日期
CURRENT_DATE() 返回当前日期
CURRENT_TIME 返回当前时间
CURRENT_TIMESTAMP() 返回当前日期和时间
CURTIME() 返回当前时间
DATE() 从日期或日期时间表达式中提取日期值
DATEDIFF(d1,d2) 计算日期 d1->d2 之间相隔的天数
DAY(d) 返回日期值 d 的日期部分
DAYNAME(d) 返回日期 d 是星期几,如 Monday,Tuesday
DAYOFMONTH(d) 计算日期 d 是本月的第几天
DAYOFWEEK(d) 日期 d 今天是星期几,1 星期日,2 星期一,以此类推
DAYOFYEAR(d) 计算日期 d 是本年的第几天
NOW() 返回当前日期和时间
WEEK(d) 计算日期 d 是本年的第几个星期,范围是 0 到 53
YEAR(d) 返回年份

4.3.4 系统函数

函数 说明
VERSION() 返回数据库的版本号
USER() 返回当前用户
NULLIF(expr1, expr2) 比较两个字符串,如果字符串 expr1 与 expr2 相等 返回 NULL,否则返回 expr1
ISNULL(expression) 判断表达式是否为 NULL
IFNULL(v1,v2) 如果 v1 的值不为 NULL,则返回 v1,否则返回 v2。

4.4 数据汇聚

  • 确定表中行数(或者满足某个条件或包含某个特定值的行数)。
  • 获得表中行组的和。
  • 找出表列(或所有行或某些特定的行)的最大值、最小值和平均值。
  • SQL汇聚函数 AVG()、COUNT()、MAX()、MIN()、SUN()
  • 汇聚函数:运行在行组上,计算和返回单个值的函数
汇聚函数 说明
AVG() 返回某列的平均值
COUNT() 返回某列的行数
MAX() 返回某列的最大值
MIN() 返回某列的最小值
SUN() 返回某列值之和

4.4.1 AVG() 函数

#计算产品表中所有产品的平均价格
SELECT AVG(prod_price) as avg_price from products;

#计算特定供应商所提供产品的平均价格
SELECT AVG(prod_price) as avg_price 
from products
WHERE vend_id = 1003

注意:AVG()函数忽略列值为 null 的行

4.4.2 count() 函数

  • COUNT() 确定表中行的数目或符合特定条件的行的数目
  • 使用COUNT(*)对表中行的数目进行计数,不管表列中包含的是空值(NULL)还是非空值。
  • 使用COUNT(column)对特定列中具有值的行进行计数,忽略NULL值。
SELECT * from customers;
#对所有行进行计数
SELECT COUNT(*) as num_cust from customers;
# cust_email 为null的行不被计数
SELECT COUNT(cust_email) as num_cust from customers;

4.4.3 MAX() 函数

MAX() 返回指定列中最大值,要求指定列名

SELECT MAX(prod_price) as max_price from products;

4.4.4 MIN() 函数

MIN() 返回指定列中最小值,要求指定列名

SELECT MIN(prod_price) as max_price from products;

4.4.5 SUM() 函数

返回指定列值得和

select sum(quantity) from orderitems where order_num = 20005

4.4.6 聚集不同值(distinct)

对所有行计算,检索出包含不同的值

SELECT distinct vend_id FROM products;
SELECT AVG(DISTINCT prod_price) as avg_price from products WHERE vend_id = 1003;

4.4.7 组合聚集函数

select count(*) as num_items, MIN(prod_price) as min_price, 
max(prod_price) as max_price, avg(prod_price) as avg_price from products;

在这里插入图片描述

单个select 语句执行了4个聚集函数

4.5 数据分组

  • group by 子句
  • having 子句

4.5.1 创建分组

  • 利用group by 子句创建分组轻松知道哪个供应商提供了多少个产品。
SELECT vend_id,COUNT(*) as num_prods from products
GROUP BY vend_id;

在这里插入图片描述

  • 使用关键字 with rollup 得到汇总数
SELECT vend_id,COUNT(*) as num_prods from products
GROUP BY vend_id with rollup;

在这里插入图片描述

4.5.2 过滤分组

  • 使用 having 子句,having 必须跟在 group by 子句之后。
  • 对比where 子句,where 子句只是过滤指定的行,而 having 子句过滤的是分组,把不符合条件的组过滤掉。
  • where 分组前过滤,having 分组后过滤。
  • having 与 where 句法相同
SELECT vend_id,COUNT(*) as num_prods from products
GROUP BY vend_id
having num_prods >2;
#把供应产品数目小于2的供应商过滤掉
SELECT vend_id,COUNT(*) as num_prods from products
WHERE prod_price >=10
GROUP BY vend_id
having num_prods >=2
ORDER BY num_prods;
#使用where 把产品单价小于10的行过滤掉,然后按照供应商id分组,再把供应产品数目小于2的供应商过滤掉,最后排序。

4.5.3 分组与排序

ORDER BY GROUP BY
排序产生的输出 分组行。输出可能不是分组的顺序
任何列都可以使用(非选择的列也可以使用) 只能使用选择列或表达式列,而且必须使用每个选择列表达式
SELECT order_num,SUM(quantity*item_price) as order_total
from orderitems
GROUP BY order_num
HAVING order_total>=50;

4.5.4 SELECT 子句顺序

子句 说明 是否必须使用
SELECT 要返回的列或表达式
FROM 从中检索数据的表 仅在从表选择数据时使用
WHERE 行级过滤
GROUP BY 分组说明 仅在按组计算聚集时使用
HAVING 组级过滤
ORDER BY 输出排序顺序
LIMIT 要检索的行数
SELECT order_num,SUM(quantity*item_price) as order_total
from orderitems
GROUP BY order_num
HAVING order_total>=50
ORDER BY order_total
LIMIT 3;

在这里插入图片描述

4.6 子查询

  • 子查询:嵌套在其他查询中的查询

4.6.1 利用子查询进行过滤

  • 以订单录入系统表为例,假如需要列出订购物品TNT2的所有客户,应该怎样检索?
#(1)检索包含物品TNT2的所有订单的编号。
SELECT order_num,prod_id from orderitems
where prod_id = 'tnt2';
#(2) 检索具有前一步骤列出的订单编号的所有客户的ID。
SELECT cust_id,order_num FROM orders
WHERE order_num in (20005,20007);
#(3) 检索前一步骤返回的所有客户ID的客户信息。
SELECT * FROM customers
WHERE cust_id in (10001,10004);
  • 上述每个步骤都可以单独作为一个查询来执行。
  • 可以把一条SELECT语句返回的结果用于另一条SELECT语句的WHERE子句。
SELECT cust_id FROM orders
WHERE order_num in (SELECT order_num from orderitems
where prod_id = 'tnt2');
SELECT * FROM customers
WHERE cust_id in (SELECT cust_id FROM orders
WHERE order_num in (SELECT order_num from orderitems
where prod_id = 'tnt2'));
  • 在select 语句中,子查询总是从内向外处理。
  • SQL对于能嵌套的子查询的数目没有限制,不过实际时有性能的限制,不能嵌套太多的子查询。
  • where 子句中使用子查询时需要注意列必须匹配

4.6.2 作为计算字段使用子查询

  • 假如现在需要显示customers表中每个客户的订单总数,你会怎么做呢?
#(1) 查看客户表有哪些客户
SELECT cust_id,cust_name FROM customers c;

在这里插入图片描述

#(2)根据第一步结果相应的去查找哪个客户下了多少订单
SELECT COUNT(*) as '10001的订单数' from orders
WHERE cust_id = 10001;

在这里插入图片描述

  • 是不是感觉很费力!那就看看怎么使用子查询作为计算字段吧!
SELECT cust_id,cust_name,(SELECT COUNT(*) FROM orders s# s 是orders表的别名,代表的是orders表
WHERE c.cust_id = s.cust_id) as '订单总数' FROM customers c;# o 是customers表的别名,代表的是customers表

在这里插入图片描述

  • 这个例子中,“订单总数”就是我们利用圆括号中的子查询创建的计算字段。
(SELECT COUNT(*) FROM orders s WHERE c.cust_id = s.cust_id) as '订单总数' 
  • 跟子查询过滤不太一样的是,我们这里使用了限定列名 WHERE c.cust_id = s.cust_id 这个where语句告诉SQL
    需要比较orders表中的cust_id 与customers表中的cust_id,当他们相同时把订单的数目返回来。
  • 在这个例子中,该子查询其实执行了5次,检索了5个客户。 当c.cust_id=10001时,在s 中查找s.cust_id
    ,所以查找到了2个订单编号,当c.cust_id=10002时,在s
    中没找到s.cust_id=10002,所以返回了0,说明10001这个客户没有订单。

4.7 联结表

4.7.1 创建联结表

SELECT * FROM vendors v,products p
WHERE v.vend_id = p.vend_id;

在这里插入图片描述
当然你也可以只创建你想要的字段

SELECT v.vend_id,p.prod_name,p.prod_price FROM vendors v,products p
WHERE v.vend_id = p.vend_id;

在这里插入图片描述
再看一下多表联结

SELECT * FROM customers c,orders o,orderitems oi
WHERE c.cust_id=o.cust_id and o.order_num =oi.order_num
#通过外键主键关系联结了3个表

SELECT c.cust_name,o.order_num,oi.prod_id FROM customers c,orders o,orderitems oi
WHERE c.cust_id=o.cust_id and o.order_num =oi.order_num
# 创建我想要的数据字段

SELECT c.cust_name,o.order_num,oi.prod_id FROM customers c,orders o,orderitems oi
WHERE c.cust_id=o.cust_id and o.order_num =oi.order_num
and c.cust_name = 'Coyote Inc.';
#找到某个客户所购买的产品id

4.7.2 自联结

  • 引入例子:假如你发现某物品(其ID为DTNTR)存在问题,因此想知道生产该物品的供应商生产的其他物品是否也存在这些问题。
  • 此查询要求首先找到生产ID为DTNTR的物品的供应商,然后找出这个供应商生产的其他物品。
SELECT prod_id,prod_name,vend_id FROM products
WHERE vend_id in (SELECT vend_id FROM products WHERE prod_id='dtntr');
#使用子查询,返回生产了DTNTR物品的供应商id。
#该id用于外部查询的where子句中,检索出这个供应商生产的所有物品。
  • 接着,使用自联结看看效果
SELECT p1.prod_id,p1.prod_name,p1.vend_id 
FROM products p1,products p2
WHERE p1.vend_id = p2.vend_id
AND p2.prod_id = 'dtntr';
# where 通过vend_id 匹配表p1、p2,
#按照p2.prod_id='dtntr'过滤数据,把需要的vend_id号传达给p1表,
#从而找出对应vend_id号的相关产品信息。

在这里插入图片描述

4.7.3 自然联结

  • 无论何时,我们对表进行联结时,都至少有一个列不止一次出现在一个表中。
  • 自然联结关注的是一一对应,通过主键与外键的关联,把相关联的信息显示出来,而关联不上的数据会被过滤掉。比如客户表id为1009,如果在订单表找不到cust_id=1009 ,则这条数据不会显示出来。
  • 事实上,我们建立的每个内部联结都是自然联结
select c.* ,o.order_num,o.order_date from customers c , orders o ,orderitems oi
where c.cust_id = o.cust_id and oi.order_num = o.order_num
and oi.prod_id ='FB'

4.7.4 外部联结

  • 对比内部联结,外部联结有时候可以包含没有关联的行

以下例子,联结包含了哪些在相关表中没有关联行的行,这种类型的联结称为外部联结

  • 对每个客户下了多少订单进行计数,包括那些至今尚未下订单的客户;
  • 列出所有产品以及订购数量,包括没有人订购的产品;
  • 计算平均销售规模,包括那些至今尚未下订单的客户。
#内部联结,检索所有客户及其订单
SELECT c.cust_id,o.order_num FROM customers c
INNER JOIN orders o
ON c.cust_id = o.cust_id;
# INNER JOIN 内部联结,INNER表示是内部联结,省略inner也可以
# 另一种写法
SELECT c.cust_id,o.order_num FROM customers c,orders o
WHERE c.cust_id = o.cust_id;

在这里插入图片描述

SELECT c.cust_id,o.order_num FROM customers c
LEFT JOIN orders o
ON c.cust_id = o.cust_id;
# LEFT JOIN 左边联结,实际上是LEFT OUTER JOIN,表示这是外部联结,此处省略了OUTER

在这里插入图片描述

SELECT c.cust_id,o.order_num FROM customers c
RIGHT JOIN orders o
ON c.cust_id = o.cust_id;
# RIGHT JOIN 右边联结

在这里插入图片描述

  • 外部联结必须使用 RIGHT JOIN、LEFT JOIN 关键字
  • 左外部联结与右外部联结唯一的差别是所关联的表的顺序不一样。
  • 顾名思义,LEFT指在from子句之后的左边表(customers表)中选择所有的行,RIGHT是指在from子句之后的右边表(orders)中选择所有的行。
  • 换种说法,from之后有两个表,customers、orders,相对来说customers是左边的表,而orders是右边的表。
  • 为什么右边联结与左边联结的结果不一样,不必明说也可意会了吧!

4.7.5 带聚集函数的联结

  • 引入例子:如果要检索所有客户及每个客户所下的订单数,你会怎么做?
#(1)内部联结
SELECT c.cust_id,c.cust_name,COUNT(o.order_num) AS '订单数' FROM customers c,orders o
WHERE c.cust_id = o.cust_id
GROUP BY o.cust_id;

在这里插入图片描述

#(2)外部联结
SELECT c.cust_id,c.cust_name,COUNT(o.order_num) AS '订单数' FROM customers c LEFT JOIN orders o
ON c.cust_id = o.cust_id
GROUP BY c.cust_id;

在这里插入图片描述

5.SQL高级数据查询

5.1 组合查询

  • 利用关键字union 将多条select语句组合成一个结果集
  • 在单个查询中从不同的表返回类似结构的数据;
  • 对单个表执行多个查询,按单个查询返回数据。

5.1.1 创建组合查询

  • 引入例子:假如需要价格小于等于5的所有物品的一个列表,而且还想包括供应商1001和1002生产的所有物品(不考虑价格)。
  • 可以利用WHERE子句完成,不过这次主要是使用UNION。
#使用where子句
SELECT prod_id,vend_id,prod_price FROM products
WHERE prod_price <=5 OR vend_id IN (1001,1002)
ORDER BY vend_id,prod_price;
# 使用组合语句 union
SELECT prod_id,vend_id,prod_price FROM products
WHERE prod_price <=5 
UNION
SELECT prod_id,vend_id,prod_price FROM products
WHERE vend_id IN (1001,1002)
ORDER BY vend_id,prod_price;
  • 创建组合查询即在两条select语句之间加入关键字 UNION

5.1.2 UNION的使用规则

  • UNION必须由两条或两条以上的SELECT语句组成,语句之间用关键字UNION分隔(因此,如果组合4条SELECT语句,将要使用3个UNION关键字)。
  • UNION中的每个查询必须包含相同的列、表达式或聚集函数(不过各个列不需要以相同的次序列出)。
  • 列数据类型必须兼容:类型不必完全相同,但必须是DBMS可以隐含地转换的类型(例如,不同的数值类型或不同的日期类型)。
  • UNION的查询结果会自动去除重复的行
  • 如果需要返回所有匹配的行,可以使用 UNION ALL
SELECT prod_id,vend_id,prod_price FROM products
WHERE prod_price <=5 
UNION ALL
SELECT prod_id,vend_id,prod_price FROM products
WHERE vend_id IN (1001,1002)
ORDER BY vend_id,prod_price;
  • 对组合查询的排序,order by 必须只能使用一次且只能放在最后一条select 语句之后,以上例子都可观察到。

5.2 全文本搜索

  • MySQL最常用的数据库引擎为 myisam 和 inodb
  • MyISAM 支持全文本搜索
  • INODB 不支持全文本搜索
  • 在使用全文本搜索时,MySQL不需要分别查看每个行,不需要分别分析和处理每个词。MySQL创建指定列中各词的一个索引,搜索可以针对这些词进行。这样,MySQL可以快速有效地决定哪些词匹配(哪些行包含它们),哪些词不匹配,它们匹配的频率,等等。
  • 为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断地重新索引
  • 在索引之后,select 可与match() 和 against() 一起使用以实际执行搜索。
  • 一般会在创建表时启用全文本搜索
CREATE TABLE `productnotes`  (
  `note_id` int(11) NOT NULL AUTO_INCREMENT,
  `prod_id` char(10) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `note_date` datetime NOT NULL,
  `note_text` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  PRIMARY KEY (`note_id`) USING BTREE,
  FULLTEXT INDEX `note_text`(`note_text`)
) ENGINE = MyISAM AUTO_INCREMENT = 115 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
# 以上的代码中根据以下这条子句启用了全文索引
  FULLTEXT INDEX `note_text`(`note_text`)
  • 在定义之后,MySQL自动维护该索引,在增加、更新或删除行时,索引随之自动更新。

5.2.1 全文本搜索基本应用

  • 在索引之后,使用两个函数 Match()、Against() 执行全文本搜索。
  • Match() 指定被搜索的列
  • Against() 指定要使用的搜索表达式
SELECT note_text FROM productnotes
WHERE MATCH(note_text) against('rabbit');
  • 注意:传递给 Match() 的值必须与FULLTEXT() 定义中的相同,如果指定多个列,则必须列出它们(而且次序必须正确)。
# 使用正则表达式
SELECT note_text FROM productnotes
WHERE note_text REGEXP 'rabbit';
# 使用关键字 like 
SELECT note_text FROM productnotes
WHERE note_text LIKE '%rabbit%';
  • 通过对比三个结果,你会发现全文本搜索的一个重要部分就是对结果排序(匹配rabbit时,全文本搜索中先返回rabbit 作为第3个词的行,因为包含词rabbit作为第3个词的行的等级比作为第20个词的行高)。具有较高等级的行先返回(因为这些行很可能是你真正想要的行)。而like语句及正则表达式不具有这种特别的排序功能。
  • 如果还不太理解,那再继续看以下例子。
SELECT note_text,MATCH(note_text) against('rabbit') AS rank_notetext FROM productnotes;

在这里插入图片描述

  • 在这里,Match()和Against() 作为一个计算字段被返回,别名是rank_notetext。此列包含全文本搜索计算出来的等级值,也就是匹配率。这里每个行以及每个行与rabbit这个词的等级值都显示出来,不包含词rabbit的行,等级值为0,文本中词越靠前的行等级值就越高。
  • 这里体现了全文本搜索提供了简单的LIKE搜素不能提供的功能,而且由于数据是索引的,全文本搜索还相当快。

5.2.2 查询扩展

  • 查询扩展用来设法放宽所返回的全文本搜索结果的范围。
  • 使用查询扩展时,MySQL对数据和索引进行两遍臊面来完成搜索:
  • 首先,进行一个基本的全文本搜索,找出与搜索条件匹配的所有行;
  • 其次,MySQL检查这些匹配行并选择所有有用的词(我们将会简要地解释MySQL如何断定什么有用,什么无用)。
  • 再其次,MySQL再次进行全文本搜索,这次不仅使用原来的条件,而且还使用所有有用的词。
  • 利用查询扩展,能找出可能相关的结果,即使它们并不精确包含所查找的词。
# 这是简单的全文本查询,没有查询扩展,结果返回了一行包含词anvils的行
SELECT note_text FROM productnotes
WHERE MATCH(note_text) against('anvils');
# 使用了查询扩展
SELECT note_text FROM productnotes
WHERE MATCH(note_text) against('anvils' WITH QUERY expansion);

在这里插入图片描述

  • 这次返回了7行。第一行包含词anvils,因此等级最高。第二行与anvils无关,但因为它包含第一行中的两个词(customer和recommend),所以也被检索出来了。
  • 查询扩展的行越多越好

5.2.3 布尔文本查询

  • 要匹配的词;
  • 要排斥的词(如果某行包含这个词,则不返回该行,即使它包含其他指定的词也是如此);
  • 排列提示(指定某些词比其他词更重要,更重要的词等级更高);
  • 表达式分组;
  • 另外一些内容。
布尔操作符 说明
+ 必须包含
- 必须不包含
> 增加优先等级
< 降低优先等级
* 词尾通配符
“” 定义短句
  • 为了匹配包含heavy但不包含任意以rope开始的词的行,可使用以下查询:
SELECT note_text FROM productnotes
WHERE MATCH(note_text) against('heavy -rope*' in boolean mode);

5.2.4 全文本搜索说明

  • 在索引全文本数据时,短词被忽略且从索引中排除。短词定义为那些具有3个或3个以下字符的词(如果需要,这个数目可以更改)。
  • MySQL带有一个内建的非用词(stopword)列表,这些词在索引全文本数据时总是被忽略。如果需要,可以覆盖这个列表(请参阅MySQL文档以了解如何完成此工作)。
  • 许多词出现的频率很高,搜索它们没有用处(返回太多的结果)。因此,MySQL规定了一条50%规则,如果一个词出现在50%以上的行中,则将它作为一个非用词忽略。50%规则不用于 IN BOOLEAN MODE。
  • 如果表中的行数少于3行,则全文本搜索不返回结果(因为每个词或者不出现,或者至少出现在50%的行中)。
  • 忽略词中的单引号。例如,don’t索引为dont。
  • 不具有词分隔符(包括日语和汉语)的语言不能恰当地返回全文本搜索结果。
  • 如前所述,仅在MyISAM数据库引擎中支持全文本搜索。

5.3 插入数据

猜你喜欢

转载自blog.csdn.net/Lalalalazy/article/details/112683595
今日推荐