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

【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<ANY(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| 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<ALL(SELECT goods_price FROM tdb_goods WHERE goods_cate='超级本');
+----------+-------------+
| 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的两条冗余的记录已经被删除!

(完)



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值