频道栏目
首页 > 资讯 > MySQL > 正文

MySQL学习笔记5:子查询与连接

16-10-07        来源:[db:作者]  
收藏   我要投稿
【1】数据准备
-- 创建数据表
CREATE TABLE IF NOT EXISTS tdb_goods(
goods_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
goods_name VARCHAR(150) NOT NULL,
goods_cate VARCHAR(40) NOT NULL,
brand_name VARCHAR(40) NOT NULL,
goods_price DECIMAL(15,3) UNSIGNED NOT NULL DEFAULT 0,
is_show BOOLEAN NOT NULL DEFAULT 1,
is_saleoff BOOLEAN NOT NULL DEFAULT 0
)
-- 写入记录
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('R510VC 15.6英寸笔记本','笔记本','华硕','3399',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('Y400N 14.0英寸笔记本电脑','笔记本','联想','4899',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('G150TH 15.6英寸游戏本','游戏本','雷神','8499',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('X550CC 15.6英寸笔记本','笔记本','华硕','2799',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('X240(20ALA0EYCD) 12.5英寸超极本','超级本','联想','4999',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('U330P 13.3英寸超极本','超级本','联想','4299',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('SVP13226SCB 13.3英寸触控超极本','超级本','索尼','7999',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('iPad mini MD531CH/A 7.9英寸平板电脑','平板电脑','苹果','1998',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('iPad Air MD788CH/A 9.7英寸平板电脑 (16G WiFi版)','平板电脑','苹果','3388',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES(' iPad mini ME279CH/A 配备 Retina 显示屏 7.9英寸平板电脑 (16G WiFi版)','平板电脑','苹果','2788',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('IdeaCentre C340 20英寸一体电脑 ','台式机','联想','3499',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('Vostro 3800-R1206 台式电脑','台式机','戴尔','2899',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('iMac ME086CH/A 21.5英寸一体电脑','台式机','苹果','9188',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('AT7-7414LP 台式电脑 (i5-3450四核 4G 500G 2G独显 DVD 键鼠 Linux )','台式机','宏碁','3699',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('Z220SFF F4F06PA工作站','服务器/工作站','惠普','4288',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('PowerEdge T110 II服务器','服务器/工作站','戴尔','5388',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('Mac Pro MD878CH/A 专业级台式电脑','服务器/工作站','苹果','28888',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES(' HMZ-T3W 头戴显示设备','笔记本配件','索尼','6999',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('商务双肩背包','笔记本配件','索尼','99',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('X3250 M4机架式服务器 2583i14','服务器/工作站','IBM','6888',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES(' HMZ-T3W 头戴显示设备','笔记本配件','索尼','6999',DEFAULT,DEFAULT);
INSERT tdb_goods (goods_name,goods_cate,brand_name,goods_price,is_show,is_saleoff) VALUES('商务双肩背包','笔记本配件','索尼','99',DEFAULT,DEFAULT);

写入记录时,可以直接复制上面的内容;如果报如下错误:“Incorrect string value:……”,则解决方法如下:
SET NAMES gbk;
然后,再采用复制的方式录入上述内容就不会出问题了。
当我们重新登录数据库,执行下述命令:mysql> SELECT * FROM tdb_goods\G;
中文可能显示的是一堆乱码,解决方法仍然是:
SET NAMES gbk;--》在客户端以gbk编码来显示数据(只影响到客户端的显示数据,并不影响数据表中真实的数据。

然后,再次执行命令:mysql> SELECT * FROM tdb_goods\G; 此时,中文就能够正常显示了!!

【2】正文
首先,我们了解一下子查询的定义,如下图:

使用子查询时要注意以下几点:
(1)子查询是指 嵌套在查询内部的 查询,且必须始终出现在圆括号内。
(2)子查询可以包含多个关键字或条件,如:DISTINCT / GROUP BY / ORDER BY / LIMIT / 函数等。
(3)子查询的外层查询可以是:SELECT, INSERT, UPDATE , SET或DO 。
(4)子查询可以返回标量、一行、一列或子查询。
由比较运算符引发的子查询,如下图:

mysql> SELECT * FROM tdb_goods\G;#显示数据表tdb_goods中的所有记录。\G表示以网格(grid)形式显示。

mysql> SELECT AVG(goods_price) FROM tdb_goods;#查询所有商品的平均价格
+------------------+
| AVG(goods_price) |
+------------------+
| 5636.3636364 |
+------------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(AVG(goods_price),2) FROM tdb_goods;#对平均值进行四舍五入(保留2位小数)
+---------------------------+
| ROUND(AVG(goods_price),2) |
+---------------------------+
| 5636.36 |
+---------------------------+
1 row in set (0.00 sec)

查询价格大于平均值(5636.36)的商品
mysql> SELECT goods_id,goods_name,goods_price FROM tdb_goods WHERE goods_price>=5636.36;
+----------+----------------------------------+-------------+
| goods_id | goods_name | goods_price |
+----------+----------------------------------+-------------+
| 3 | G150TH 15.6英寸游戏本 | 8499.000 |
| 7 | SVP13226SCB 13.3英寸触控超极本 | 7999.000 |
| 13 | iMac ME086CH/A 21.5英寸一体电脑 | 9188.000 |
| 17 | Mac Pro MD878CH/A 专业级台式电脑 | 28888.000 |
| 18 | HMZ-T3W 头戴显示设备 | 6999.000 |
| 20 | X3250 M4机架式服务器 2583i14 | 6888.000 |
| 21 | HMZ-T3W 头戴显示设备 | 6999.000 |
+----------+----------------------------------+-------------+
7 rows in set (0.00 sec)
查询价格大于平均值(5636.36)的商品(使用子查询)
mysql> SELECT goods_id,goods_name,goods_price FROM tdb_goods WHERE goods_price>=(SELECT ROUND(AVG(goods_price),2) FROM tdb_goods);
+----------+----------------------------------+-------------+
| goods_id | goods_name | goods_price |
+----------+----------------------------------+-------------+
| 3 | G150TH 15.6英寸游戏本 | 8499.000 |
| 7 | SVP13226SCB 13.3英寸触控超极本 | 7999.000 |
| 13 | iMac ME086CH/A 21.5英寸一体电脑 | 9188.000 |
| 17 | Mac Pro MD878CH/A 专业级台式电脑 | 28888.000 |
| 18 | HMZ-T3W 头戴显示设备 | 6999.000 |
| 20 | X3250 M4机架式服务器 2583i14 | 6888.000 |
| 21 | HMZ-T3W 头戴显示设备 | 6999.000 |
+----------+----------------------------------+-------------+
7 rows in set (0.00 sec)

mysql> SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本';#查询超级本的价格
+-------------+
| goods_price |
+-------------+
| 4999.000 |
| 4299.000 |
| 7999.000 |
+-------------+
3 rows in set (0.00 sec)

mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price>(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
ERROR 1242 (21000): Subquery returns more than 1 row
上述命令之所以报错,是因为少了ANY,SOME或ALL关键字。
如果子查询返回多个结果,则需要使用ANY,SOME或ALL关键字来修饰,如下图:

其中ANY和SOME是等价的,只要符合其中一个就可以;而ALL是要符合全部才可以。。。
在使用ANY SOME ALL 的时候,有一个简单的原则,如下图:

使用ANY关键字(SOME关键字的用法与ANY一致)

ANY关键字与 >或>= 组合使用,返回子查询的最小值。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price>ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 2 | 4899.000 |
| 3 | 8499.000 |
| 5 | 4999.000 |
| 7 | 7999.000 |
| 13 | 9188.000 |
| 16 | 5388.000 |
| 17 | 28888.000 |
| 18 | 6999.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
+----------+-------------+
10 rows in set (0.00 sec)
ANY关键字与 <或<= 组合使用,返回子查询的最大值。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price +----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 2 | 4899.000 |
| 4 | 2799.000 |
| 5 | 4999.000 |
| 6 | 4299.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 16 | 5388.000 |
| 18 | 6999.000 |
| 19 | 99.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
| 22 | 99.000 |
+----------+-------------+
18 rows in set (0.00 sec)
ANY关键字与 = 组合使用,查询结果 等价于 子查询的结果。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price=ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 5 | 4999.000 |
| 6 | 4299.000 |
| 7 | 7999.000 |
+----------+-------------+
3 rows in set (0.00 sec)
ANY关键字与 <>或!= 组合使用,查询结果 是 整个数据表。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price<>ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 2 | 4899.000 |
| 3 | 8499.000 |
| 4 | 2799.000 |
| 5 | 4999.000 |
| 6 | 4299.000 |
| 7 | 7999.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 13 | 9188.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 16 | 5388.000 |
| 17 | 28888.000 |
| 18 | 6999.000 |
| 19 | 99.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
| 22 | 99.000 |
+----------+-------------+
22 rows in set (0.00 sec)

使用ALL关键字
ALL 关键字与 >或>= 组合使用,返回子查询的最大值。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price>ALL(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 3 | 8499.000 |
| 13 | 9188.000 |
| 17 | 28888.000 |
+----------+-------------+
3 rows in set (0.00 sec)
ALL 关键字与 <或<= 组合使用,返回子查询的最小值。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price +----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 4 | 2799.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 19 | 99.000 |
| 22 | 99.000 |
+----------+-------------+
11 rows in set (0.00 sec)
ALL 关键字与 = 组合使用,查询结果为空。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price=ALL(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
Empty set (0.00 sec)
ALL关键字与 <>或!= 组合使用,返回结果是 数据表中的所有记录,但不包含子查询的结果。
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price<>ALL(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 2 | 4899.000 |
| 3 | 8499.000 |
| 4 | 2799.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 13 | 9188.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 16 | 5388.000 |
| 17 | 28888.000 |
| 18 | 6999.000 |
| 19 | 99.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
| 22 | 99.000 |
+----------+-------------+
19 rows in set (0.00 sec)
由[NOT]IN引发的子查询,如下图:

=ANY运算符与 IN 等效,实例如下:
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price=ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 5 | 4999.000 |
| 6 | 4299.000 |
| 7 | 7999.000 |
+----------+-------------+
3 rows in set (0.00 sec)
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price IN (SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 5 | 4999.000 |
| 6 | 4299.000 |
| 7 | 7999.000 |
+----------+-------------+
3 rows in set (0.00 sec)
!=ALL或<>ALL运算符与 NOT IN 等效,实例如下:
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price<>ALL(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 2 | 4899.000 |
| 3 | 8499.000 |
| 4 | 2799.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 13 | 9188.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 16 | 5388.000 |
| 17 | 28888.000 |
| 18 | 6999.000 |
| 19 | 99.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
| 22 | 99.000 |
+----------+-------------+
19 rows in set (0.00 sec)
mysql> SELECT goods_id,goods_price FROM tdb_goods WHERE goods_price NOT IN (SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| goods_id | goods_price |
+----------+-------------+
| 1 | 3399.000 |
| 2 | 4899.000 |
| 3 | 8499.000 |
| 4 | 2799.000 |
| 8 | 1998.000 |
| 9 | 3388.000 |
| 10 | 2788.000 |
| 11 | 3499.000 |
| 12 | 2899.000 |
| 13 | 9188.000 |
| 14 | 3699.000 |
| 15 | 4288.000 |
| 16 | 5388.000 |
| 17 | 28888.000 |
| 18 | 6999.000 |
| 19 | 99.000 |
| 20 | 6888.000 |
| 21 | 6999.000 |
| 22 | 99.000 |
+----------+-------------+
19 rows in set (0.00 sec)
还有一种情况很少使用,如下图:

mysql> SELECT * FROM tdb_goods\G;
通过上述命令可以查看数据表tdb_goods中的所有记录。容易看到,表中有很多重复信息,比如
goods_cate字段重复存储了 “笔记本配件”,“服务器/工作站”等信息。
解决该问题的方法是 使用外键!
mysql> CREATE TABLE IF NOT EXISTS tdb_goods_cates( #创建数据表
-> cate_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
-> cate_name VARCHAR(40) NOT NULL
-> );
Query OK, 0 rows affected (0.13 sec)
按照 goods_cate 分组,目的是查询商品总共有多少类。
mysql> SELECT goods_cate FROM tdb_goods GROUP BY goods_cate;
+---------------+
| goods_cate |
+---------------+
| 台式机 |
| 平板电脑 |
| 服务器/工作站 |
| 游戏本 |
| 笔记本 |
| 笔记本配件 |
| 超级本 |
+---------------+
7 rows in set (0.00 sec)
如何将上述分组命令的查询结果 写入到 数据表tdb_goods_cates呢?方法如下:
mysql> DESC tdb_goods_cates;#查看一下数据表的结构
+-----------+----------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------+----------------------+------+-----+---------+----------------+
| cate_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| cate_name | varchar(40) | NO | | NULL | |
+-----------+----------------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)
将商品类别写入到数据表tdb_goods_cates
mysql> INSERT tdb_goods_cates(cate_name) SELECT goods_cate FROM tdb_goods GROUP BY goods_cate;
Query OK, 7 rows affected (0.06 sec)
Records: 7 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM tdb_goods_cates; #可以看到,商品类别写入成功!
+---------+---------------+
| cate_id | cate_name |
+---------+---------------+
| 1 | 台式机 |
| 2 | 平板电脑 |
| 3 | 服务器/工作站 |
| 4 | 游戏本 |
| 5 | 笔记本 |
| 6 | 笔记本配件 |
| 7 | 超级本 |
+---------+---------------+
7 rows in set (0.00 sec)
通过下边的SELECT命令,我们可以看到,数据表tdb_goods中的goods_cate字段存储的仍然是汉字(也就是说,此时还没有用到外键)。
mysql> SELECT * FROM tdb_goods\G;
我们应该参照 分类表tdb_goods_cates,去更新商品表tdb_goods;(多表更新!)
多表更新 语法如下图:

表的参照关系,如下图:


连接类型有3种,如下图:

mysql> UPDATE tdb_goods INNER JOIN tdb_goods_cates ON goods_cate = cate_name
-> SET goods_cate = cate_id; #进行多表更新
Query OK, 22 rows affected (0.07 sec)
Rows matched: 22 Changed: 22 Warnings: 0
mysql> SELECT * FROM tdb_goods\G; #通过该命令可以看到,数据表tdb_goods中的字段goods_cate,已经更新成了数字!
创建数据表的同时将查询结果写入到数据表中,如下图:

mysql> SELECT brand_name FROM tdb_goods GROUP BY brand_name; #查询数据表tdb_goods中,商标名brand_name有哪几种。
+------------+
| brand_name |
+------------+
| IBM |
| 华硕 |
| 宏碁 |
| 惠普 |
| 戴尔 |
| 索尼 |
| 联想 |
| 苹果 |
| 雷神 |
+------------+
9 rows in set (0.00 sec)
mysql> CREATE TABLE tdb_goods_brands #创建数据表的同时将查询结果写入到数据表中
-> (
-> brand_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
-> brand_name VARCHAR(40) NOT NULL
-> )
-> SELECT brand_name FROM tdb_goods GROUP BY brand_name;
Query OK, 9 rows affected (0.16 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM tdb_goods_brands; #可以看到,查询结果已经写入成功。
+----------+------------+
| brand_id | brand_name |
+----------+------------+
| 1 | IBM |
| 2 | 华硕 |
| 3 | 宏碁 |
| 4 | 惠普 |
| 5 | 戴尔 |
| 6 | 索尼 |
| 7 | 联想 |
| 8 | 苹果 |
| 9 | 雷神 |
+----------+------------+
9 rows in set (0.00 sec)
参照品牌表tdb_goods_brands去更新商品表tdb_goods
mysql> SELECT * FROM tdb_goods\G; #查看商品表中的记录
mysql> SELECT * FROM tdb_goods_brands; #查看品牌表中的信息。
mysql> SHOW COLUMNS FROM tdb_goods_brands;#查看品牌表的结构
mysql> SHOW COLUMNS FROM tdb_goods; #查看商品表的结构
mysql> UPDATE tdb_goods INNER JOIN tdb_goods_brands ON brand_name = brand_name
-> SET brand_name = brand_id;
ERROR 1052 (23000): Column 'brand_name' in field list is ambiguous
上述命令报错,是因为商品表tdb_goods和品牌表tdb_goods_brands中都包含brand_name字段,为了避免歧义,
应该在brand_name前面加上数据表的名字作为前缀。
正确的更新命令如下:
mysql> UPDATE tdb_goods AS g INNER JOIN tdb_goods_brands AS b ON g.brand_name = b.brand_name
-> SET g.brand_name = b.brand_id;
Query OK, 22 rows affected (0.13 sec)
Rows matched: 22 Changed: 22 Warnings: 0
mysql> SELECT * FROM tdb_goods\G;#通过该命令可以看到,数据表tdb_goods中的brand_name字段已经被更新成了数字。
mysql> SHOW COLUMNS FROM tdb_goods; #可以看到goods_cate字段和brand_name字段的类型仍然是varchar(40)
+-------------+------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------------+------+-----+---------+----------------+
| goods_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| goods_name | varchar(150) | NO | | NULL | |
| goods_cate | varchar(40) | NO | | NULL | |
| brand_name | varchar(40) | NO | | NULL | |
| goods_price | decimal(15,3) unsigned | NO | | 0.000 | |
| is_show | tinyint(1) | NO | | 1 | |
| is_saleoff | tinyint(1) | NO | | 0 | |
+-------------+------------------------+------+-----+---------+----------------+
7 rows in set (0.01 sec)
下面,我们来修改数据表tdb_goods的结构(修改goods_cate和brand_name这2个字段的名字和类型)。
mysql> ALTER TABLE tdb_goods
-> CHANGE goods_cate cate_id SMALLINT UNSIGNED NOT NULL,
-> CHANGE brand_name brand_id SMALLINT UNSIGNED NOT NULL;
Query OK, 22 rows affected (0.35 sec)
Records: 22 Duplicates: 0 Warnings: 0
mysql> SHOW COLUMNS FROM tdb_goods; #可以看到,相应的结构已经被修改。
+-------------+------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+------------------------+------+-----+---------+----------------+
| goods_id | smallint(5) unsigned | NO | PRI | NULL | auto_increment |
| goods_name | varchar(150) | NO | | NULL | |
| cate_id | smallint(5) unsigned | NO | | NULL | |
| brand_id | smallint(5) unsigned | NO | | NULL | |
| goods_price | decimal(15,3) unsigned | NO | | 0.000 | |
| is_show | tinyint(1) | NO | | 1 | |
| is_saleoff | tinyint(1) | NO | | 0 | |
+-------------+------------------------+------+-----+---------+----------------+
7 rows in set (0.01 sec)
mysql> INSERT tdb_goods_cates(cate_name) VALUES('路由器'),('交换机'),('网卡');#向类别表tdb_goods_cates中插入记录
Query OK, 3 rows affected (0.05 sec)
Records: 3 Duplicates: 0 Warnings: 0

mysql> INSERT tdb_goods_brands(brand_name) VALUES('海尔'),('清华同方'),('神舟');#向品牌表tdb_goods_brands中插入记录
Query OK, 3 rows affected (0.08 sec)
Records: 3 Duplicates: 0 Warnings: 0
向商品表tdb_goods中插入记录。
mysql> INSERT tdb_goods(goods_name,cate_id,brand_id,goods_price) VALUES(' LaserJet Pro P1606dn 黑白激光打印机','12','4','1849');
Query OK, 1 row affected (0.15 sec)
注意,在上述插入命令中,‘12’赋给了cate_id
mysql> SELECT * FROM tdb_goods_cates; #可以看到,cate_id=12的记录不存在!由于我们没有创建物理外键,因此上述插入命令是成功的。
+---------+---------------+
| cate_id | cate_name |
+---------+---------------+
| 1 | 台式机 |
| 2 | 平板电脑 |
| 3 | 服务器/工作站 |
| 4 | 游戏本 |
| 5 | 笔记本 |
| 6 | 笔记本配件 |
| 7 | 超级本 |
| 8 | 路由器 |
| 9 | 交换机 |
| 10 | 网卡 |
+---------+---------------+
10 rows in set (0.00 sec)

mysql> SELECT * FROM tdb_goods\G;
通过上述命令,查看数据表tdb_goods中的记录。可以看到cate_id字段和brand_id字段显示的是数字!!
我们在数据表中,存储数字可以节省存储空间。但是呈现给浏览者的时候,若显示的是数字,浏览者往往不明白这些数字的含义。
我们呈现给浏览者的应该是 明确的商品分类和商品品牌。。。这种需求可以通过 连接 来实现。。。
MySQL在SELECT语句,多表更新,多表删除 语句中支持JOIN操作。
连接的基本的语法结构,如下图:


数据表的参照,如下图:


前面,我们提到连接类型分3种,如下图:


我们一般使用ON关键字来设定连接条件,如下图:

内连接显示左表及右表符合连接条件的记录,入下图:




举一个内连接的例子:
mysql> SELECT goods_id,goods_name,cate_name FROM tdb_goods INNER JOIN tdb_goods_cates
-> ON tdb_goods.cate_id = tdb_goods_cates.cate_id\G; #注意:为了避免歧义,cate_id前面要加上表名作为前缀!

左外连接显示左表的全部记录及右表符合连接条件的记录,如下图:




举一个左外连接的例子:
mysql> SELECT goods_id,goods_name,cate_name FROM tdb_goods LEFT JOIN tdb_goods_cates
-> ON tdb_goods.cate_id = tdb_goods_cates.cate_id\G;
右外连接显示右表的全部记录及左表符合连接条件的记录,如下图:




举一个右外连接的例子:
mysql> SELECT goods_id,goods_name,tdb_goods.cate_id,tdb_goods_cates.cate_id,cate_name FROM tdb_goods RIGHT JOIN tdb_goods_cates
-> ON tdb_goods.cate_id = tdb_goods_cates.cate_id\G;


本文对内连接,左外连接和右外连接只做了简单的说明和示例,更加详细的介绍,请参考:

1》MySQL 内连接 左连接 右连接 外连接(union组合查询)

2》MySQL左连接、右连接和内连接详解

多表连接
mysql> SELECT * FROM tdb_goods\G;#该命令的显示结果中,cate_id字段和brand_id字段的值是数字,浏览者并不知道这些数字的含义。
使用多表连接可以解决上述问题,例子如下:
mysql> SELECT goods_id,goods_name,cate_name,cate_name,brand_name,goods_price FROM tdb_goods AS g
-> INNER JOIN tdb_goods_cates AS c ON g.cate_id = c.cate_id
-> INNER JOIN tdb_goods_brands AS b ON g.brand_id = b.brand_id\G;

无限级分类表设计
mysql> CREATE TABLE tdb_goods_types( #创建数据表
-> type_id SMALLINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
-> type_name VARCHAR(20) NOT NULL,
-> parent_id SMALLINT UNSIGNED NOT NULL DEFAULT 0
-> );
Query OK, 0 rows affected (0.15 sec)
插入记录
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('家用电器',DEFAULT);
Query OK, 1 row affected (0.06 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑、办公',DEFAULT);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('大家电',1);
Query OK, 1 row affected (0.03 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('生活电器',1);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('平板电视',3);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('空调',3);
Query OK, 1 row affected (0.03 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('电风扇',4);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('饮水机',4);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑整机',2);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('电脑配件',2);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('笔记本',9);
Query OK, 1 row affected (0.03 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('超级本',9);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('游戏本',9);
Query OK, 1 row affected (0.03 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('CPU',10);
Query OK, 1 row affected (0.04 sec)
mysql> INSERT tdb_goods_types(type_name,parent_id) VALUES('主机',10);
Query OK, 1 row affected (0.06 sec)
mysql> SELECT * FROM tdb_goods_types;
+---------+------------+-----------+
| type_id | type_name | parent_id |
+---------+------------+-----------+
| 1 | 家用电器 | 0 |
| 2 | 电脑、办公 | 0 |
| 3 | 大家电 | 1 |
| 4 | 生活电器 | 1 |
| 5 | 平板电视 | 3 |
| 6 | 空调 | 3 |
| 7 | 电风扇 | 4 |
| 8 | 饮水机 | 4 |
| 9 | 电脑整机 | 2 |
| 10 | 电脑配件 | 2 |
| 11 | 笔记本 | 9 |
| 12 | 超级本 | 9 |
| 13 | 游戏本 | 9 |
| 14 | CPU | 10 |
| 15 | 主机 | 10 |
+---------+------------+-----------+
15 rows in set (0.03 sec)
自身连接:一个数据表对其自身进行连接。
查找所有分类及其父类
mysql> SELECT s.type_id,s.type_name,p.type_name FROM tdb_goods_types AS s LEFT JOIN tdb_goods_types AS p
-> ON s.parent_id = p.type_id;
+---------+------------+------------+
| type_id | type_name | type_name |
+---------+------------+------------+
| 1 | 家用电器 | NULL |
| 2 | 电脑、办公 | NULL |
| 3 | 大家电 | 家用电器 |
| 4 | 生活电器 | 家用电器 |
| 5 | 平板电视 | 大家电 |
| 6 | 空调 | 大家电 |
| 7 | 电风扇 | 生活电器 |
| 8 | 饮水机 | 生活电器 |
| 9 | 电脑整机 | 电脑、办公 |
| 10 | 电脑配件 | 电脑、办公 |
| 11 | 笔记本 | 电脑整机 |
| 12 | 超级本 | 电脑整机 |
| 13 | 游戏本 | 电脑整机 |
| 14 | CPU | 电脑配件 |
| 15 | 主机 | 电脑配件 |
+---------+------------+------------+
15 rows in set (0.00 sec)
查找所有分类及其子类
mysql> SELECT p.type_id,p.type_name,s.type_name FROM tdb_goods_types AS p LEFT JOIN tdb_goods_types AS s
-> ON p.type_id = s.parent_id;
+---------+------------+-----------+
| type_id | type_name | type_name |
+---------+------------+-----------+
| 1 | 家用电器 | 大家电 |
| 1 | 家用电器 | 生活电器 |
| 2 | 电脑、办公 | 电脑整机 |
| 2 | 电脑、办公 | 电脑配件 |
| 3 | 大家电 | 平板电视 |
| 3 | 大家电 | 空调 |
| 4 | 生活电器 | 电风扇 |
| 4 | 生活电器 | 饮水机 |
| 5 | 平板电视 | NULL |
| 6 | 空调 | NULL |
| 7 | 电风扇 | NULL |
| 8 | 饮水机 | NULL |
| 9 | 电脑整机 | 笔记本 |
| 9 | 电脑整机 | 超级本 |
| 9 | 电脑整机 | 游戏本 |
| 10 | 电脑配件 | CPU |
| 10 | 电脑配件 | 主机 |
| 11 | 笔记本 | NULL |
| 12 | 超级本 | NULL |
| 13 | 游戏本 | NULL |
| 14 | CPU | NULL |
| 15 | 主机 | NULL |
+---------+------------+-----------+
22 rows in set (0.00 sec)
mysql> SELECT p.type_id,p.type_name,s.type_name FROM tdb_goods_types AS p LEFT JOIN tdb_goods_types AS s
-> ON p.type_id = s.parent_id GROUP BY p.type_name ORDER BY p.type_id;
+---------+------------+-----------+
| type_id | type_name | type_name |
+---------+------------+-----------+
| 1 | 家用电器 | 大家电 |
| 2 | 电脑、办公 | 电脑整机 |
| 3 | 大家电 | 平板电视 |
| 4 | 生活电器 | 电风扇 |
| 5 | 平板电视 | NULL |
| 6 | 空调 | NULL |
| 7 | 电风扇 | NULL |
| 8 | 饮水机 | NULL |
| 9 | 电脑整机 | 笔记本 |
| 10 | 电脑配件 | CPU |
| 11 | 笔记本 | NULL |
| 12 | 超级本 | NULL |
| 13 | 游戏本 | NULL |
| 14 | CPU | NULL |
| 15 | 主机 | NULL |
+---------+------------+-----------+
15 rows in set (0.00 sec)
查找所有分类及其子类数量
mysql> SELECT p.type_id,p.type_name,COUNT(s.type_name) AS child_count FROM tdb_goods_types AS p
-> LEFT JOIN tdb_goods_types AS s ON p.type_id = s.parent_id GROUP BY p.type_name
-> ORDER BY p.type_id;
+---------+------------+-------------+
| type_id | type_name | child_count |
+---------+------------+-------------+
| 1 | 家用电器 | 2 |
| 2 | 电脑、办公 | 2 |
| 3 | 大家电 | 2 |
| 4 | 生活电器 | 2 |
| 5 | 平板电视 | 0 |
| 6 | 空调 | 0 |
| 7 | 电风扇 | 0 |
| 8 | 饮水机 | 0 |
| 9 | 电脑整机 | 3 |
| 10 | 电脑配件 | 2 |
| 11 | 笔记本 | 0 |
| 12 | 超级本 | 0 |
| 13 | 游戏本 | 0 |
| 14 | CPU | 0 |
| 15 | 主机 | 0 |
+---------+------------+-------------+
15 rows in set (0.00 sec)
多表删除,语法如下图:

mysql> SELECT * FROM tdb_goods\G;#通过该命令我们可以看到,数据表中存在重复的记录(比如:goods_id: 18与goods_id: 21重复)

现在,我们要把数据表tdb_goods中重复的记录删除掉,保留goods_id比较小的记录。这种需求可以通过 多表删除 来实现。。
COUNT(goods_name)>1 说明该商品重复了!需要删除goods_id较大的记录,保留goods_id较小的记录。
mysql> SELECT goods_id,goods_name,COUNT(goods_name) AS count FROM tdb_goods GROUP BY goods_name HAVING COUNT(goods_name)>1;
+----------+-----------------------+-------+
| goods_id | goods_name | count |
+----------+-----------------------+-------+
| 18 | HMZ-T3W 头戴显示设备 | 2 |
| 19 | 商务双肩背包 | 2 |
+----------+-----------------------+-------+
2 rows in set (0.00 sec)
多表删除命令如下(通过自身的连接删除重复的记录):
mysql> DELETE t1 FROM tdb_goods AS t1 LEFT JOIN (SELECT goods_id,goods_name FROM tdb_goods GROUP BY goods_name HAVING COUNT(goods_name)>1) AS t2
-> ON t1.goods_name = t2.goods_name WHERE t1.goods_id>t2.goods_id;
Query OK, 2 rows affected (0.06 sec)

【上面的多表删除命令也可以写成这样:

DELETE t1 FROM tdb_goods AS t1 INNER JOIN (SELECT goods_id,goods_name FROM tdb_goods GROUP BY goods_name HAVING COUNT(*)>1) AS t2
ON (t1.goods_name = t2.goods_name AND t1.goods_id>t2.goods_id);



mysql> SELECT * FROM tdb_goods\G;#通过该命令可以看到,数据表tdb_goods中,goods_id=21和goods_id=22的两条冗余的记录已经被删除!

相关TAG标签
上一篇:计算机网络和http权威指南读书笔记
下一篇:TCP DUP ACK的研究
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站