您的位置:首页 > 数据库 > MySQL

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

2016-10-05 23:36 639 查看
【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的两条冗余的记录已经被删除!

(完)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: