1、创建练习使用的数据表
goods_id —— 商品编号
goods_name —— 商品名称
goods_cate —— 商品分类
brand_name —— 商品品牌
goods_price —— 商品价格
is_show —— 商品是否上架,默认为在售
is_saleoff —— 商品是否售罄,默认为否
由于商品中有中文字符出现,因此需要首先设置客户端显示数据的编码为“gbk”,但实际数据库中的存储数据的编码格式仍然是之前设定的“utf8”,不会受影响。
此次举例共填入了22条记录,手动输入过于繁琐,因此提供源码,点击下载使用。
2、子查询简介
子查询(Subquery)是指出现在其他SQL语句内的SELECT子句。
例如:
SELECT*FROM t1 WHERE col1 = ( SELECT col2 FROM t2);
其中“SELECT*FROM t1”被称为Outer Query/Outer Statement,即外层查询;
而“SELECT col2 FROM t2”被称为SubQuery,即子查询。
- 子查询指嵌套在查询内部,且必须始终出现在圆括号内;
- 子查询可以包含多个关键字或条件,例如:DISTINCT、GROUP BY、ORDER BY、LIMIT、函数等;
- 子查询的外层查询可以是:SELECT、INSERT、UPDATE、SET、DO。
注意:这里的“外层查询”并不仅仅指“SELECT”查找,而是所有的SQL命令的统称,因为SQL语言被称为是结构化查询语言,包括增、删、改、查等。 - 子查询可以返回标量、一行、一列或子查询。
3、由比较运算符引发的子查询
语法结构:
operand comparison_operator subquery
比较运算符包括:
=、>、<、>=、<=、<>、!=、<=>
查询所有商品的平均价格,还可以对查询的结果进行四舍五入,保留2位小数:
在所有商品中,查询售价大于或等于平均价格的商品,显示其编号、名称及价格:
可以看到,在查找大于等于平均价格的商品中,“5636.36”其实就是上一条SQL语句所求得的结果,将这两条语句合并,就是子查询:
通过查询可知在所有商品中属于“超级本”分类的商品共有3件,售价分别是“4999”、“4299”、“7999”。如果想使用子查询的方式,查找售价大于“超级本”的商品,结果报错:
出现错误的原因是之前提到的,子查询的返回值只能是一行,虽然要查找售价大于“超级本”价格的商品,但“超级本”的价格有3个,系统无法得知要与哪个进行比较,此时就需要使用ANY、SOME、ALL关键字来修饰比较运算符:
语法结构:
operand comparison_operator ANY (subquery)
operand comparison_operator SOME (subquery)
operand comparison_operator ALL (subquery)
运算符\关键字 | ANY | SOME| ALL
----|------|----
、>= | 最小值 | 最小值 | 最大值
<、<= | 最大值 | 最大值| 最小值
= | 任意值 | 任意值|
<>、!= | | | 任意值
根据上表修改之前的SQL语句,例如添加“ALL”关键字,要求比所有的“超级本”价格都高,即大于最大值“7999”即可:
4、由[NOT] IN / EXISTS引发的子查询
使用[NOT] IN的语法结构:
operand comparison_operator [NOT] IN (subquery)
= ANY 运算符与 IN 等效;
!= ALL 或<> ALL运算符与 NOT IN 等效。
对于上条SQL语句,首先修改为“!= ALL”关键字,即除去3款“超级本”商品,查询剩余的全部19款商品:
可见结果确实是19款:
再修改为“NOT IN”关键字:
结果与之前相同:
使用[NOT] EXISTS的语法结构:
operand comparison_operator [NOT] EXISTS (subquery)
EXISTS:当子查询返回任何行时,EXISTS返回TRUE,触发外层查询;否则返回FALSE。
NOT EXISTS:与EXISTS相反。
例如,子查询中“id”为100的商品不存在,因此不返回任何行,EXISTS返回FALSE,外层查询为空:
当查询“id”为10的商品时,可以返回结果,EXISTS返回TRUE,外层查询为全部的22件商品:
NOT EXISTS与EXISTS相反,当查询“id”为10的商品时,可以返回结果,NOT EXISTS返回FALSE,外层查询为空;当子查询中“id”为100的商品不存在,不返回任何行时,NOT EXISTS返回TRUE,外层查询为全部的22件商品:
5、使用INSERT……SELECT插入记录
其实目前用于演示的表在实际使用中,有一个严重的缺陷,即重复数据过多,而且中文字符占有更多的字节,会导致数据表使用效率降低,例如下图所示的“笔记本配件”、“索尼”等等:
针对上述问题,可以使用外键来解决,创建新的数据表“tdb_goods_cates”:
查询“tdb_goods”表的所有记录,并且按"类别"分组:
将查询的分组结果写入到“tdb_goods_cates”数据表中:
6、多表更新
语法结构
UPDATE table_references SET col_name1 = {expr1 | DEFAULT} [,col_name2 = {expr2 | DEFAULT}……
[WHERE where_condition]
在使用多表更新时,需要使用“连接”,这里先简单了解一下:
table_reference {[INNER | CROSS] JOIN | {LEFT | RIGHT} [OUTER] JOIN }
table_reference ON conditional_expr
可以理解为:
表1“table_reference”+参照关系“{[INNER | CROSS] JOIN | {LEFT | RIGHT} [OUTER] JOIN }”
+表2“table_reference”+连接条件“ON conditional_expr”
连接类型分为:
- INNER JOIN(内连接)
在MySQL中,JOIN、CROSS JOIN、INNER JOIN是等价的。 - LEFT [OUTER] JOIN(左外连接)
- RIGHT [OUTER] JOIN(右外链接)
此时需要将新创的“tdb_goods_cates”表中的“id”替换到原“tdb_goods”表的“类别”中:
对于上述SQL语句的简单理解是:要更新“tdb_goods”表,以内连接“INNER JOIN”的方式连接“tdb_goods_cates”表,连接的条件是“tdb_goods”表中的商品类别“goods_cate”等于“tdb_goods_cates”表中的“cate_name”,最后将“tdb_goods”表中的商品类别“goods_cate”更新为“tdb_goods_cates”表中的“cate_id”。
通过查询可见已实现更新:
但对于多表更新,还存在一种更为简便的方法,目前的方法是首先创建数据表,之后查询数据并写入,最后进行连接更新;那么使用如下语句,便可将创建与查询写入合二为一:
CREATE……SELECT
CREATE TABLE [IF NOT EXISTS] tbl_name [(create_definition,……)] select_statement
创建数据表的同时,将查询的结果写入数据表。
首先查询“tdb_goods”表的所有记录,并且按"品牌"分组:
之后创建数据表“tdb_goods_brands”并写入查询数据:
可见数据表以创建完成且存在记录:
此时可以使用之前的语句进行连接:
系统提示出错,因为无法分辨两个“brand_name”分别属于哪一张表,此时可以在字段前添加表名来加以区分,但更常使用别名来区分,例如:
可见所有的类别及品牌都已修改,虽然数字可能相同,但实际意义不同:
再来查看该数据表的结构:
虽然此时已修改了记录,但表的根本结构没有改变,此时的“1、2、3……”仍然是字符型,因此此时建议修改表的结构:
此时数据表才算真正的完成了“瘦身”操作,不过可能会有疑问:之前所说的外键,怎么没看到使用“FOREIGN KEY”,其实在实际开发中,使用物理外键即“FOREIGN KEY”的机会并不多,相反这种事实外键会应用的更广泛一些。
7、连接的语法结构
MySQL在SELECT语句、多表更新、多表删除语句中支持JOIN操作。
table_reference {[INNER | CROSS] JOIN | {LEFT | RIGHT} [OUTER] JOIN }
table_reference ON conditional_expr
可以理解为:
表1“table_reference”+参照关系“{[INNER | CROSS] JOIN | {LEFT | RIGHT} [OUTER] JOIN }”
+表2“table_reference”+连接条件“ON conditional_expr”
连接条件:
使用“ON”关键字来设定连接条件,也可以使用WHERE来代替。
“ON”关键字更多的用来设定连接条件;
“WHERE”关键字则进行结果集记录的过滤。
数据表参照:
table_reference
tbl_name [[AS] alias] | table_subquery [AS] alias
数据表可以使用“tbl_name AS alias_name”或“tbl_name alias_name”赋予别名;
table_subquery可以作为子查询使用在FROM子句中,此类子查询必须为其赋予别名。
连接类型分为:
INNER JOIN(内连接):在MySQL中,JOIN、CROSS JOIN、INNER JOIN是等价的。
LEFT [OUTER] JOIN(左外连接)
RIGHT [OUTER] JOIN(右外链接)
<br />
<br />
内连接 INNER JOIN
在演示内连接之前,先分别向数据表“tdb_goods_cates”、“tdb_goods_brands”中添加三个商品分类及三个品牌:
之后向数据表“tdb_goods”中添加一条记录:
要注意,该记录中“cate_id”添加的“12”仅仅是符合数据类型的要求而没有报错,但实际上根本没有“id”为“12”的商品分类,此时数据表中共有23条记录。
在实际使用中,对于客户来讲,可能根本不清楚例如“cate_id”中的“6”代表的是什么意思:
因此需要联合查询数据表“tdb_goods”以及数据表“tdb_goods_cates”,由于两张数据表都有“cate_id”字段,需要添加表名加以区分:
此时共查询到结果22条,没有新加入的记录的原因是在“tdb_goods_cates”表中不存在“cate_id”为12的商品分类,而“INNER JOIN”只显示符合连接条件的记录,即共有的部分,因此没有第23条记录:
<br />
<br />
外连接 OUTER JOIN
修改之前的SQL语句,将“INNER JOIN”改为“LEFT JOIN”,“OUTER”可以省略:
此时共查询到结果23条,由于是左外连接,因此显示“tdb_goods”表的全部23条记录,但是“tdb_goods_cates”表中没有符合连接条件的记录,即“cate_id”为12的商品分类,因此显示“NULL”:
<br />
修改之前的SQL语句,将“LEFT JOIN”改为“RIGHT JOIN”,“OUTER”可以省略:
此时共查询到结果25条,由于是右外连接,因此显示“tdb_goods_cates”表的全部10条记录,但是“tdb_goods”表中没有符合连接条件的记录,即分类属于“路由器”、“交换机”、“网卡”的商品,因此显示“NULL”,而且不显示“cate_id”为“12”的商品:
关于外链接的几点说明(以左外连接为例):
- tbl_A LEFT JOIN tbl_B ON join_condition
- 数据表B的结果集依赖数据表A,即数据表A中存在的记录,在数据表B中才会显示,而数据表B的其他记录则不会显示;
- 数据表A的结果集根据左连接条件依赖所有数据表(B表除外);
- 左外连接条件决定如何检索数据表B(在没有指定WHERE条件的情况下);
- 如果数据表A的某条记录符合WHERE条件,但是在数据表B不存在符合连接条件的记录,将生成一个所有列为空的额外的B行(例如“右外链接”中的查询结果)。
<br />
<br />
多表连接
除去之前的两张表连接之外,还可以更多的表进行连接:
此时查询的结果与之前最开始的单表结果相同,但此时的意义完全不同:
8、无限级分类表设计
本篇作为案列使用数据表“tdb_goods”中的商品分类,在实际开发中是远远不够的,那么例如“X宝”、“X东”等电商网站的商品分类是如何实现的,可以参考以下案列:
首先创建数据表“tdb_goods_types”:
type_id —— 分类编号
type_name —— 分类名称
parent_id —— 父类编号
插入相关记录:
此次举例共填入了15条记录,手动输入过于繁琐,因此提供源码,点击下载使用。
查看该表的所有记录:
其中:
“家用电器”与“电脑、办公”都是顶级分类,没有父类;
“大家电”与“生活电器”都属于“家用电器”,父类编号为“1”,即“家用电器”的商品编号;
“平板电视”与“空调”都属于“大家电”,父类编号为“3”,以此类推。
对这种数据表进行查询就需要使用“自身连接”,例如使查询结果显示子类商店的编号、名称以及父类商品的名称,就可以这样理解:
想象有两张完全相同的表,分别是父表与子表,至于如何确定父表、子表的方式不唯一,位置可以交换,只是为了满足别名的需要,否则系统将无法区分这些相同的字段;当确定父表与子表后,父表中“parent_id”字段就没有用处了,因为本身就是父表,而子表中“parent_id”其实就是父表中的“type_id”,因此,就可以做如下考虑:
此时的查询结果为:
例如此时需要查询父类的商品编号、名称以及其子类的名称:
此时的查询结果为:
但这种显示方式比较混乱,对此修改为显示父类商品的编号、名称以及其子类商品的数目:
9、多表删除
语法结构
DELETE tbl_name[.*] [,tbl_name[.*]] …… FROM tbl_references [WHERE where_condition]
通过查询发现,在“tdb_goods”数据表中有部分商品名称重复的记录,例如:
细化查询条件,通过商品名称分组,并查询商品名称大于或等于2个的商品,即为重复商品:
与之前的“自身连接”类似,仍然使用这同一张表演示多表删除操作:
输入SQL语句后提示“有两条记录被删除”,此时查询记录只有21条:
10、操作数据表记录的SQL语句汇总:
-
子查询
SELECT*FROM t1 WHERE col1 = ( SELECT col2 FROM t2);
由[NOT] IN 引发的子查询:
operand comparison_operator [NOT] IN (subquery)
= ANY 运算符与 IN 等效;
!= ALL 或<> ALL运算符与 NOT IN 等效。
由[NOT] EXISTS引发的子查询:
operand comparison_operator [NOT] EXISTS (subquery)
EXISTS:当子查询返回任何行时,EXISTS返回TRUE,触发外层查询;否则返回FALSE。
NOT EXISTS:与EXISTS相反。
由比较运算符引发的子查询:
operand comparison_operator ANY (subquery)
operand comparison_operator SOME (subquery)
operand comparison_operator ALL (subquery)
运算符\关键字 | ANY | SOME| ALL
----|------|----
、>= | 最小值 | 最小值 | 最大值
<、<= | 最大值 | 最大值| 最小值
= | 任意值 | 任意值|
<>、!= | | | 任意值
多表更新
UPDATE table_references SET col_name1 = {expr1 | DEFAULT} [,col_name2 = {expr2 | DEFAULT}……
[WHERE where_condition]连接
table_reference {[INNER | CROSS] JOIN | {LEFT | RIGHT} [OUTER] JOIN }
table_reference ON conditional_expr
连接分类:
INNER JOIN(内连接):在MySQL中,JOIN、CROSS JOIN、INNER JOIN是等价的。
LEFT [OUTER] JOIN(左外连接)
RIGHT [OUTER] JOIN(右外链接)
连接条件:
使用“ON”关键字来设定连接条件,也可以使用WHERE来代替。
“ON”关键字更多的用来设定连接条件;
“WHERE”关键字则进行结果集记录的过滤。
数据表参照:
table_reference
tbl_name [[AS] alias] | table_subquery [AS] alias
数据表可以使用“tbl_name AS alias_name”或“tbl_name alias_name”赋予别名;
table_subquery可以作为子查询使用在FROM子句中,此类子查询必须为其赋予别名。使用CREATE……SELECT插入记录
CREATE TABLE [IF NOT EXISTS] tbl_name [(create_definition,……)] select_statement
-
多表删除
DELETE tbl_name[.*] [,tbl_name[.*]] …… FROM tbl_references [WHERE where_condition]
版权声明:欢迎转载,欢迎扩散,但转载时请标明作者以及原文出处,谢谢合作! ↓↓↓