使用多重表

mysql cookbook00012

使用多重表

mysql> select * from aritst;
ERROR 1146 (42S02): Table 'temp.aritst' doesn't exist
mysql> select * from artist;
+------+----------+
| a_id | name |
+------+----------+
| 1 | Da Vince |
| 2 | Monet |
| 4 | Pcasso |
| 5 | Renoir |
| 3 | Van Gogh |
+------+----------+
5 rows in set (0.00 sec)

mysql> insert into painting values(1,null,'The Last Supper','IN',34),(1,null,'Teh Mo
-> (3,NULL,'The Potato Eaters','KY',67),(3,NULL,'The Rocks','IA',33),(5,null,'L
Query OK, 6 rows affected (0.08 sec)
Records: 6 Duplicates: 0 Warnings: 0

mysql> SELECT * FROM PAINTING;
+------+------+--------------------+-------+-------+
| a_id | p_id | title | state | price |
+------+------+--------------------+-------+-------+
| 1 | 1 | The Last Supper | IN | 34 |
| 1 | 2 | Teh Mona Lisa | MI | 87 |
| 3 | 3 | Starry Night | KY | 48 |
| 3 | 4 | The Potato Eaters | KY | 67 |
| 3 | 5 | The Rocks | IA | 33 |
| 5 | 6 | Last Deux Soeurs | NE | 64 |
| 6 | 7 | Three Country | CN | 64 |
+------+------+--------------------+-------+-------+
6 rows in set (0.00 sec)

12.0 引言
使用多重表的几个理由:
1. 将来自多个表中的行结合起来,获取的信息比单个表中所能获取的更加全面
2. 保持多步操作的中间值
3. 根据其他表的信息改变其他行
一个多重表的语句中可能是:连接查询,联合查询,子查询。
本章主题:
1. 连接不同的表以发现他们中相匹配或者不匹配的行
内链接:每某些表中的某些行与另一张表中的行相匹配;外连接:可以显示匹配行,也可以显示不匹配行
2. 将一个与其自身相比较
类似表间连接,使用别名以消除引用的歧义
3. 使用联合以将结果集结合起来
将多个结果集联合起来
4. 删除不匹配行
5. 在不同数据库的表之间进行连接操作
可以跨类联合存储方式数据库表(federated table)以使一个MYSQL服务器能够自动访问位于另外一个服务器上的表,
或者为每个服务器打开连接并自己手动将其中信息组合起来。

12.1 在表中找到与另外一个表中的行相匹配的行
使用连接——即一个查询
连接的本质:将一张表与另外一张表中的行相关联,对于多重表的来说,如果他们中的每个表只含有你所感兴趣的部分信息,那么连接使你能够
将这些表中的信息结合起来,连接的输出行所包含的信息要多于其中任何一个表中单独取出来的信息。
一个完全连接将可能产生笛卡尔积。所以连接通常包含ON/USING子句以指定如何在表间进行连接。你也可以在连接中包含WHERE子句进行xi
下面列举两张表的连接查询:article 和 painting
第一种:SELECT * FROM artist painting WHERE artist.a_id = painting.a_id;
另一种:SELECT * FROM artist INNER JOINpainting ON artist.a_id = painting.a_id;
在特殊的情况下,即两个表中匹配列名字相同,并且是使用=号操作符进行比较的话,可使用INNER JOIN USING
mysql> select * from artist inner join painting using(a_id);
+------+----------+------+--------------------+-------+-------+
| a_id | name | p_id | title | state | price |
+------+----------+------+--------------------+-------+-------+
| 1 | Da Vince | 1 | The Last Supper | IN | 34 |
| 1 | Da Vince | 2 | Teh Mona Lisa | MI | 87 |
| 5 | Renoir | 6 | Last Deux Soeurs | NE | 64 |
| 3 | Van Gogh | 3 | Starry Night | KY | 48 |
| 3 | Van Gogh | 4 | The Potato Eaters | KY | 67 |
| 3 | Van Gogh | 5 | The Rocks | IA | 33 |
+------+----------+------+--------------------+-------+-------+
第三种:SELECT * FROM artist INNER JOIN painting USING(a_id);
注意:当你使用USING子句时,SELECT *只会返回所有连接列中的一个实例(a_id),在ON,WHERE,USING都可以放比较操作,那么怎样放入什么
样的连接条件呢?
根据经验规则,通常使用ON或者USING来指定如何建立连接表,使用WHERE子句限制选择哪些连接的行。
多表连接也是可以的:SELECT * FROM artistINNER JOIN paintingINNER JOIN stateON artist.a_id = painting.a_id
AND painting.state = states.abbrev;
注意:1. 通过逗号或者INNER JOIN 所编写的连接是内链接,这意味着他们只是为某个表中与其他表的值相匹配产生的结果
2. 外连接同样可以产生这些匹配,此外还可以为你显示某个表中的哪些值与另外表中的某些不符合的值。
3. 相同的列名在不同表中需要表名进行区分
连接和索引
因为连接会很容易导致MYSQL产生大量的行组合,所以确认你要比较的列已经被索引是个好的方式。否则,在表现模增长时性能会下降的很快。对于artist和painting表连接是基于每个表中的a_id列值产生的,如果int向前查看这些表的建表语句,你会发现在每个表中a_id都建立了索引
*************************** 1. row ***************************
Table: artist
Create Table: CREATE TABLE `artist` (
`a_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
PRIMARY KEY (`a_id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.06 sec)

ERROR:
No query specified

mysql> SHOW CREATE TABLE PAINTING\G
*************************** 1. row ***************************
Table: PAINTING
Create Table: CREATE TABLE `painting` (
`a_id` int(10) unsigned NOT NULL,
`p_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(30) NOT NULL,
`state` varchar(30) NOT NULL,
`price` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`p_id`),
KEY `a_id` (`a_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
1 row in set (0.00 sec)


12.2 查找与其他表中不匹配的行
左外连接:显示不重复的左边表的行与右边表的行不存在匹配关系的组合关系,右侧显示NULL值
mysql> select * from artist left join painting on artist.a_id = painting.a_id;
+------+----------+------+------+--------------------+-------+-------+
| a_id | name | a_id | p_id | title | state | price |
+------+----------+------+------+--------------------+-------+-------+
| 1 | Da Vince | 1 | 1 | The Last Supper | IN | 34 |
| 1 | Da Vince | 1 | 2 | Teh Mona Lisa | MI | 87 |
| 2 | Monet | NULL | NULL | NULL | NULL | NULL |
| 4 | Pcasso | NULL | NULL | NULL | NULL | NULL |
| 5 | Renoir | 5 | 6 | Last Deux Soeurs | NE | 64 |
| 3 | Van Gogh | 3 | 3 | Starry Night | KY | 48 |
| 3 | Van Gogh | 3 | 4 | The Potato Eaters | KY | 67 |
| 3 | Van Gogh | 3 | 5 | The Rocks | IA | 33 |
+------+----------+------+------+--------------------+-------+-------+
8 rows in set (0.00 sec)
右外连接:显示不重复的右边表的行与左侧表中的行不存在匹配关系的组合关系,左侧显示NULL值
mysql> select * from artist right join painting on artist.a_id = painting.a_id;
+------+----------+------+------+--------------------+-------+-------+
| a_id | name | a_id | p_id | title | state | price |
+------+----------+------+------+--------------------+-------+-------+
| 1 | Da Vince | 1 | 1 | The Last Supper | IN | 34 |
| 1 | Da Vince | 1 | 2 | Teh Mona Lisa | MI | 87 |
| 3 | Van Gogh | 3 | 3 | Starry Night | KY | 48 |
| 3 | Van Gogh | 3 | 4 | The Potato Eaters | KY | 67 |
| 3 | Van Gogh | 3 | 5 | The Rocks | IA | 33 |
| 5 | Renoir | 5 | 6 | Last Deux Soeurs | NE | 64 |
| NULL | NULL | 6 | 7 | Three Coutry | CN | 100 |
+------+----------+------+------+--------------------+-------+-------+
对于左外连接或者右外连接他们都必须确定ON连接的条件,因为假如你不显示给个连接的条件他就不知道怎么样去显示另外一张表哪列不存在才为空值
编写LEFT JOIN 或者 RIGHT JOIN 查询的其他方法
和内链接一样有这样简便的写法,假如在两个表中存在相同名称的列,而且你又是通过这相同的列进行连接,那么就可以写成下面的形式:
select * from tab1 left join tab2 on tab1.col = tab2.col;select * from tab1 left join tab2 using(col);
select * from tab1 right join tab2 on tab1.col = tab2.col;select * from tab1 right join tab2 using(col);
在特殊的情况下,你希望根据两个表中的所有列进行比较,那么可以使用NATURALLEFT JOIN 或者 NATURAL RIGHT JOIN 并且省略ON或 USING
mysql> select * from artist NATURAL left join painting
-> ;
+------+----------+------+--------------------+-------+-------+
| a_id | name | p_id | title | state | price |
+------+----------+------+--------------------+-------+-------+
| 1 | Da Vince | 1 | The Last Supper | IN | 34 |
| 1 | Da Vince | 2 | Teh Mona Lisa | MI | 87 |
| 2 | Monet | NULL | NULL | NULL | NULL |
| 4 | Pcasso | NULL | NULL | NULL | NULL |
| 5 | Renoir | 6 | Last Deux Soeurs | NE | 64 |
| 3 | Van Gogh | 3 | Starry Night | KY | 48 |
| 3 | Van Gogh | 4 | The Potato Eaters | KY | 67 |
| 3 | Van Gogh | 5 | The Rocks | IA | 33 |
+------+----------+------+--------------------+-------+-------+

12.3 将表与自身进行比较
mysql> select * from painting p1 inner join painting p2 on p1.a_id = p2.a_idand p1.title='The Rocks'
and p1.title !=p2.title;//要求:取出作品为The Rocks 并且删除自身的那条记录
+------+------+-----------+-------+-------+------+------+--------------------+-------+-------+
| a_id | p_id | title | state | price | a_id | p_id | title | state | price |
+------+------+-----------+-------+-------+------+------+--------------------+-------+-------+
| 3 | 5 | The Rocks | IA | 33 | 3 | 3 | Starry Night | KY | 48 |
| 3 | 5 | The Rocks | IA | 33 | 3 | 4 | The Potato Eaters | KY | 67 |
+------+------+-----------+-------+-------+------+------+--------------------+-------+-------+
2 rows in set (0.00 sec)
12.4产生主从列表和摘要
要求:产生每个作家的总的作品数、总共的价格、平均价格
mysql> select artist.name 'painter' ,count(*)'number of painting',sum(painting.price)
'total price',avg(painting.price) 'average price' from artist left
-> join painting on artist.a_id = painting.a_id group by artist.name;
+----------+--------------------+-------------+---------------+
| painter | number of painting | total price | average price |
+----------+--------------------+-------------+---------------+
| Da Vince | 2 | 121 | 60.5000 |
| Monet | 1 | NULL | NULL |
| Pcasso | 1 | NULL | NULL |

| Renoir | 1 | 64 | 64.0000 |
| Van Gogh | 3 | 148 | 49.3333 |
+----------+--------------------+-------------+---------------+
5 rows in set (0.16 sec)
从上面你会发现两行特别的记录:他们的count值为1但是他们价值、平均值都为NULL
因为这count(*)是对所有的连接计数,甚至那些包含了无画作的画家行,而其他的摘要函数是不会对NULL记录进行处理的。
所以要使用另外一种方式来进行计数:count(expr)仅仅针对于非空计数
mysql> select artist.name 'painter' ,count(painting.a_id)'number of painting',sum(painting.price)
'total price',avg(painting.price) 'average price' from artist left
-> join painting on artist.a_id = painting.a_id group by artist.name;
+----------+--------------------+-------------+---------------+
| painter | number of painting | total price | average price |
+----------+--------------------+-------------+---------------+
| Da Vince | 2 | 121 | 60.5000 |
| Monet | 0 | NULL | NULL |
| Pcasso | 0 | NULL | NULL |
| Renoir | 1 | 64 | 64.0000 |
| Van Gogh | 3 | 148 | 49.3333 |
+----------+--------------------+-------------+---------------+
12.5 列举多对多的关系
零件<-->厂商的多对多关系,对于多对多关系需要存在一个中间表
12.6 查找每组行中含有最大值或者最小值的行
要求:找出作品中最贵的那个作品的信息(作家,作品名称,价格)
第一种定以变量的方式:
mysql> set @max_val := (select max(painting.price) from painting);
Query OK, 0 rows affected (0.00 sec
mysql> select artist.name,painting.title,painting.price from artist inner join painting on artist.a_id = painting.a_id
-> where painting.price = @max_val;
+----------+---------------+-------+
| name | title | price |
+----------+---------------+-------+
| Da Vince | Teh Mona Lisa | 87 |
+----------+---------------+-------+
第二种使用子查询的方式:
mysql> select artist.name,painting.title,painting.price from artist inner join painting
-> on artist.a_id = painting.a_id
-> where painting.price = (select max(painting.price) from painting);
+----------+---------------+-------+
| name | title | price |
+----------+---------------+-------+
| Da Vince | Teh Mona Lisa | 87 |
+----------+---------------+-------+
1 row in set (0.00 sec)
要求:查找每个作家中最高的作品显示出来
方式一使用子查询的方式:
mysql> select artist.name,painting.title,painting.price
-> from artist inner join painting inner join
-> (select a_id, max(painting.price) max_price from painting group by a_id) as temp
-> on artist.a_id = painting.a_id
-> and painting.a_id = temp.a_id
-> and painting.price = temp.max_price;
+----------+--------------------+-------+
| name | title | price |
+----------+--------------------+-------+
| Da Vince | Teh Mona Lisa | 87 |
| Van Gogh | The Potato Eaters | 67 |
| Renoir | Last Deux Soeurs | 64 |
+----------+--------------------+-------+
3 rows in set (0.63 sec)
方式二使用左外连接查询:
1.怎样求得一张表中分组最大值:使用LEFT JOIN以将表与其自身相连接,下面的语句确定了每个画家ID的最高价格的作品
(我们使用 IS NULL 来选择属于p1的并且在p2中没有比其价格更高的那些行)://这种方式很巧妙
select a_id,max(painting.price) from painting p1 left join painting p2 on p1.a' at line 2
mysql> select p1.a_id , p1.title,p1.price from painting p1 left join painting p2
on p1.a_id = p2.a_id and p1.price< p2.price
-> where p2.a_id is null;
+------+--------------------+-------+
| a_id | title | price |
+------+--------------------+-------+
| 1 | Teh Mona Lisa | 87 |
| 3 | The Potato Eaters | 67 |
| 5 | Last Deux Soeurs | 64 |
+------+--------------------+-------+
3 rows in set (0.89 sec)
2. 通过上面的示例你可以写下第二种方式:
mysql> select artist.name,temp.title,temp.price from artist inner join
-> (select p1.a_id,p1.title,p1.price from painting p1 left join painting p2
-> on p1.a_id = p2.a_id
-> and p1.price < p2.price
-> where p2.a_id is null) as temp
-> on artist.a_id = temp.a_id;
+----------+--------------------+-------+
| name | title | price |
+----------+--------------------+-------+
| Da Vince | Teh Mona Lisa | 87 |
| Van Gogh | The Potato Eaters | 67 |
| Renoir | Last Deux Soeurs | 64 |
+----------+--------------------+-------+
3 rows in set (0.00 sec)
mysql>

12.7 计算队伍排名
问题:需要根据球队的输赢记录来计算球队的排名,包括比赛落后(GAMDES-BEHIND)GB值
解决:先确定哪只球队处于第一位,然后将结果与原来的行进行连接。
处理两个列的值:1. 获胜率:WINS/(WINS+LOSSES);这样的计算是有问题的,假如还未参加过比赛的那么就存在除0异常了
所以应该是: IF(WINS = 0,0,WINS/(WINS+LOSSES));
2. 比赛落后值通过下面的两个值进行计算求平均:
1. 第二名队伍如果要达到与第一名同样多的胜场数还需要赢下多少场
2. 第一名队伍如果与第二名具有同样的负场数需要输多少场
通过上面的逻辑: BG = (winsA - lossesA)-(winsB - lossesB)/2
例子:假如1997年北部联赛具有两个分区(东西部),此外由于每半个赛季进行的胜利者相互比赛,以角逐冠军的资格。一个赛季有前后两个部分
mysql> select * from standing;
+------+----------+-------+------+--------+
| half | division | team | wins | losses |
+------+----------+-------+------+--------+
| 1 | EASTERN | ateam | 24 | 18 |
| 1 | EASTERN | bteam | 18 | 24 |
| 1 | EASTERN | cteam | 17 | 24 |
| 1 | EASTERN | dteam | 15 | 27 |
| 1 | WESTERN | eteam | 29 | 12 |
| 1 | WESTERN | fteam | 28 | 14 |
| 1 | WESTERN | hteam | 21 | 21 |
| 1 | WESTERN | iteam | 15 | 27 |
| 2 | EASTERN | jteam | 22 | 20 |
| 2 | EASTERN | kteam | 21 | 21 |
| 2 | EASTERN | lteam | 19 | 23 |
| 2 | EASTERN | mteam | 18 | 24 |
| 2 | WESTERN | nteam | 26 | 16 |
| 2 | WESTERN | oteam | 24 | 18 |
| 2 | WESTERN | pteam | 22 | 20 |
| 2 | WESTERN | qteam | 16 | 26 |
+------+----------+-------+------+--------+
16 rows in set (0.00 sec)
由于该数据是分了,时期、地方、团队的一张记录表
所以不能简单的处理这些数据,首先要创建一张临时表,该表保存了某个时期、某个地方的最大值
mysql> select * ,truncate(wl.wins/(wl.wins + wl.losses),3) as pct,
-> if(tm.wl_diff = wl.wins - wl.losses,'-',truncate((tm.wl_diff-(wl.wins-wl.losses))/2,1)) as gb
-> from temp_max tm inner join standing wl
-> on tm.half = wl.half
-> and tm.division = wl.division
-> order by wl.half,wl.division,(wl.wins-wl.losses) desc,pct desc;
+------+----------+---------+------+----------+-------+------+--------+-------+------+
| half | division | wl_diff | half | division | team | wins | losses | pct | gb |
+------+----------+---------+------+----------+-------+------+--------+-------+------+
| 1 | EASTERN | 6 | 1 | EASTERN | ateam | 24 | 18 | 0.571 | - |
| 1 | EASTERN | 6 | 1 | EASTERN | bteam | 18 | 24 | 0.428 | 6.0 |
| 1 | EASTERN | 6 | 1 | EASTERN | cteam | 17 | 24 | 0.414 | 6.5 |
| 1 | EASTERN | 6 | 1 | EASTERN | dteam | 15 | 27 | 0.357 | 9.0 |
| 1 | WESTERN | 17 | 1 | WESTERN | eteam | 29 | 12 | 0.707 | - |
| 1 | WESTERN | 17 | 1 | WESTERN | fteam | 28 | 14 | 0.666 | 1.5 |
| 1 | WESTERN | 17 | 1 | WESTERN | hteam | 21 | 21 | 0.500 | 8.5 |
| 1 | WESTERN | 17 | 1 | WESTERN | iteam | 15 | 27 | 0.357 | 14.5 |
| 2 | EASTERN | 2 | 2 | EASTERN | jteam | 22 | 20 | 0.523 | - |
| 2 | EASTERN | 2 | 2 | EASTERN | kteam | 21 | 21 | 0.500 | 1.0 |
| 2 | EASTERN | 2 | 2 | EASTERN | lteam | 19 | 23 | 0.452 | 3.0 |
| 2 | EASTERN | 2 | 2 | EASTERN | mteam | 18 | 24 | 0.428 | 4.0 |
| 2 | WESTERN | 10 | 2 | WESTERN | nteam | 26 | 16 | 0.619 | - |
| 2 | WESTERN | 10 | 2 | WESTERN | oteam | 24 | 18 | 0.571 | 2.0 |
| 2 | WESTERN | 10 | 2 | WESTERN | pteam | 22 | 20 | 0.523 | 4.0 |
| 2 | WESTERN | 10 | 2 | WESTERN | qteam | 16 | 26 | 0.380 | 10.0 |
+------+----------+---------+------+----------+-------+------+--------+-------+------+
16 rows in set (0.00 sec)
12.8 使用连接不全或者识别列表的缺口
问题:你需要为每个类别产生摘要,但是其中的一些并没有要被摘要的数据。因此摘要就缺失了一些类别。
解决:通过创建一张引用表列出所有的类别,并且根据在此列表与包含数据的表之间的LEFT JOIN产生摘要,从而引用表中的每个种类都会
出现在结果中,甚至包括哪些数据中没有类别的也会在摘要中出现。
mysql> select * from email;
+---------------------+---------+---------+---------+---------+-------+
| t | srcuser | srchost | dstuser | dsthost | size |
+---------------------+---------+---------+---------+---------+-------+
| 2014-12-10 01:00:00 | aa | aaa | aaaa | aaaaa | 11111 |
| 2014-12-10 02:00:00 | bb | bbb | bbbb | bbbbb | 22222 |
| 2014-12-10 03:00:00 | cc | ccc | cccc | ccccc | 33333 |
| 2014-12-10 04:00:00 | dd | ddd | dddd | ddddd | 44444 |
| 2014-12-10 05:00:00 | ee | eee | eeee | eeeee | 55555 |
| 2014-12-10 05:00:00 | ff | fff | ffff | fffff | 66666 |
+---------------------+---------+---------+---------+---------+-------+
6 rows in set (0.00 sec)

mysql> select * from ref;
+------+
| h |
+------+
| 1 |
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
| 21 |
| 22 |
| 23 |
| 24 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
+------+
24 rows in set (0.00 sec)

mysql>
通过连接补全列表:
mysql> select h ,count(hour(t)) from ref left joinemail on ref.h = hour(email.t) group by h
+------+----------------+
| h | count(hour(t)) |
+------+----------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 2 |
| 6 | 0 |
| 7 | 0 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
| 11 | 0 |
| 12 | 0 |
| 13 | 0 |
| 14 | 0 |
| 15 | 0 |
| 16 | 0 |
| 17 | 0 |
| 18 | 0 |
| 19 | 0 |
| 20 | 0 |
| 21 | 0 |
| 22 | 0 |
| 23 | 0 |
| 24 | 0 |
+------+----------------+
24 rows in set (0.00 sec)
mysql>
发现数据集中的缺口:
mysql> select h from ref left join email on ref.h=hour(email.t) wher e email.t is null;
+------+
| h |
+------+
| 12 |
| 13 |
| 14 |
| 15 |
| 16 |
| 17 |
| 18 |
| 19 |
| 20 |
| 21 |
| 22 |
| 23 |
| 24 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
| 11 |
+------+
19 rows in set (0.00 sec)

12.9 计算连续行的差值
解决:使用自连接以匹配相邻的每对行,并计算每个行对成员的差值
mysql> select p1.*,p2.*,(p2.ab -p1.ab) as 'p_ab',(p2.h-p1.h) as 'p_h',
truncate(if(p2.ab-p1.ab = 0,0,(p2.h-p1.h)/(p2.ab-p1.ab)),3) as ba
-> from player_state p1 inner join player_state p2
-> on p1.id +1 = p2.id
-> order by p1.id;
+----+------------+------+------+----+------------+------+------+------+------+-------+
| id | date | ab | h | id | date | ab | h | p_ab | p_h | ba |
+----+------------+------+------+----+------------+------+------+------+------+-------+
| 1 | 2014-12-11 | 0 | 0 | 2 | 2014-12-12 | 38 | 13 | 38 | 13 | 0.342 |
| 2 | 2014-12-12 | 38 | 13 | 3 | 2014-12-13 | 109 | 31 | 71 | 18 | 0.253 |
| 3 | 2014-12-13 | 109 | 31 | 4 | 2014-12-14 | 196 | 49 | 87 | 18 | 0.206 |
| 4 | 2014-12-14 | 196 | 49 | 5 | 2014-12-15 | 304 | 98 | 108 | 49 | 0.453 |
+----+------------+------+------+----+------------+------+------+------+------+-------+

12.10 发现累积和与动态均值
mysql> select * from rainfall;.//数据案例
+------------+--------+
| date | precip |
+------------+--------+
| 2014-12-11 | 12 |
| 2014-12-12 | 10 |
| 2014-12-13 | 5 |
| 2014-12-14 | 15 |
| 2014-12-15 | 16 |
+------------+--------+
mysql> select r1.date ,sum(r1.precip) from rainfall r1 inner join rainfall r2
-> on r1.date >=r2.date
-> group by r1.date;
+------------+----------------+
| date | sum(r1.precip) |
+------------+----------------+
| 2014-12-11 | 12 |
| 2014-12-12 | 20 |
| 2014-12-13 | 15 |
| 2014-12-14 | 60 |
| 2014-12-15 | 80 |
+------------+----------------+
5 rows in set (0.00 sec)
假如说其中中间缺失了部分日期下雨量的记载:
mysql> select * from rainfall;
+------------+--------+
| date | precip |
+------------+--------+
| 2014-12-11 | 12 |
| 2014-12-13 | 5 |
| 2014-12-15 | 16 |
+------------+--------+
3 rows in set (0.00 sec)
//假如有缺省的下雨量记录那么就必须要,通过下面的方式才能够得到平均值
mysql> select r1.* ,sum(r1.precip),datediff(max(r2.date),min(r2.date))+1 as 'datediff',
-> sum(r1.precip)/(datediff(max(r2.date),min(r2.date))+1)as 'day of precip'
-> from rainfall r1 inner join rainfall r2 on r1.date >= r2.date group by r1.date;
+------------+--------+----------------+----------+---------------+
| date | precip | sum(r1.precip) | datediff | day of precip |
+------------+--------+----------------+----------+---------------+
| 2014-12-11 | 12 | 12 | 1 | 12.0000 |
| 2014-12-13 | 5 | 10 | 3 | 3.3333 |
| 2014-12-15 | 16 | 48 | 5 | 9.6000 |
+------------+--------+----------------+----------+---------------+

12.11 使用连接控制查询输出的顺序
问题:你需要对某个语句的输出进行排序,但是所排序的特征无法使用ORDER BY 来指定。
解决:可以通过取出排序所需要的信息并将其存储在辅助表中,然后建立连接原始表与辅助表,并且使用辅助表来进行排序
使用如下表:mysql> select * from rainfall;中的平均下雨量来进行排序,但是均值是没有出现在列中的,所以建立临时表
+------------+--------+
| date | precip |
+------------+--------+
| 2014-12-11 | 12 |
| 2014-12-13 | 5 |
| 2014-12-15 | 16 |
+------------+--------+
3 rows in set (0.06 sec)
1. 建立临时表
create table tempselect r1.date,sum(r1.precip),truncate(sum(r1.precip)/
datediff(max(r2.date),min(r2.date))+ 1)as 'day of avg' from rainfall r1 inner join rainfall r2
on r1.date > r2.date group by r1.date;
2. 连接查询
select temp.('day of avg' ),rainfall.date,rainfall.precip from temp inner join rainfall
on temp.date = rainfall.date order by temp.('day of avg' ); //不能是temp.('day of avg')/temp.(day of avg)

12.12 在单个查询中整合几个结果集
问题:只是希望能在几个表中,或者一个表中选择几个行的集合——并且把他们作为结果集
解决:使用UNION进行结果集整合
注意:在联合查询的时候,union默认会去除重复项,假如使用union all来进行联合查询结果将不会去除重复项
12.13 识别并删除失配或独立行
问题:在具有两个相关的数据集,但是他们的关系可能并不完全相同。你需要确定其中一个数据集中是否存在独立的记录,存在就删除他
解决:通过使用left join 或者 not in 来进行处理。
讨论:内部连接可以在用于识别关联的发生,而外部连接可以在用于世界关联的缺失。知道不匹配数据的存在是非常有价值的,因为你可以警示给你
发送数据的一方。这样在数据采集方法中通常是一个必须被纠正的错误信号。例如:假如对于销售数据,在一个区域数据的缺失可能说明某些
区域经理没有报告,并且这个疏忽没有被注意到。
mysql> select * from sales_region;
+-----------+------+
| region_id | name |
+-----------+------+
| 1 | aaaa |
| 2 | bbbb |
| 3 | cccc |
| 4 | dddd |
+-----------+------+
mysql> select * from sales_volume;
+-----------+------+---------+--------+
| region_id | year | quarter | volume |
+-----------+------+---------+--------+
| 1 | 2006 | 1 | 100400 |
| 1 | 2006 | 2 | 120000 |
| 3 | 2006 | 1 | 280000 |
| 3 | 2006 | 2 | 250000 |
| 5 | 2006 | 1 | 18000 |
| 5 | 2006 | 2 | 32000 |
+-----------+------+---------+--------+
6 rows in set (0.00 sec)
mysql> select * from sales_region sr left join sales_volume svon sr.region_id = sv.region_id
-> where sv.region_id is null;
+-----------+------+-----------+------+---------+--------+
| region_id | name | region_id | year | quarter | volume |
+-----------+------+-----------+------+---------+--------+
| 2 | bbbb | NULL | NULL | NULL | NULL |
| 4 | dddd | NULL | NULL | NULL | NULL |
+-----------+------+-----------+------+---------+--------+
2 rows in set (0.00 sec)




mysql> select * from sales_region sr left join sales_volume sv on sr.region_id = sv.region_id
-> where sv.region_id is null;
+-----------+------+-----------+------+---------+--------+
| region_id | name | region_id | year | quarter | volume |
+-----------+------+-----------+------+---------+--------+
| 2 | bbbb | NULL | NULL | NULL | NULL |
| 4 | dddd | NULL | NULL | NULL | NULL |
+-----------+------+-----------+------+---------+--------+
2 rows in set (0.00 sec)
删除的方式有两中,其一://叫作多表删除。
mysql>delete sales_volume from sales_volume svleft join sales_region sr
-> on sv.region_id = sr.region_id
-> where sr.region_id is null;
ERROR 1109 (42S02): Unknown table 'sales_volume' in MULTI DELETE
//上面的信息表名假如被重命名了就不能在使用原来的名字
mysql>delete sv from sales_volume svleft join sales_region sr
-> on sv.region_id = sr.region_id
-> where sr.region_id is null;
Query OK, 2 rows affected (0.06 sec)

mysql>
删除的方式有两中,其一://使用NOT IN 子查询来识别和删除不匹配的行。
mysql> delete from sales_region where region_id not in(select region_id from sales_volume );
Query OK, 2 rows affected (2.36 sec)

mysql> delete sr from sales_region sr where sr.region_id not in(select region_id from sales_volume sv);
Query OK, 1 row affected (0.08 sec)
mysql>

//对于下面的这条子句:mysql> delete from sales_region as srwhere sr.region_id not in(select region_id from sales_volume sv);
会报错,报的是an error in your SQL syntax;语法错误;在红色标注的地方

MYSQL中的InnoDB存储引擎支持外关键字级联删除与更新

12.14 为不同数据库键的表执行连接
解决:使用数据库名称限定符以告知MYSQL到何处查找这些表
12.15 同时使用不同的MYSQL服务器
解决: 建立FEDERATED表,该表能够使MYSQL服务器访问位于另一个服务器的表另一种方式是为每个服务器打开一个独立的连接,然后手动的
将两个表中的信息结合起来 或者将其中一个表拷贝到另外一个服务器上,以使你能够在单个服务器中操作两个表。
由于MYSQL支持FEDERATED存储引擎,以使你能够远程访问另外一个远程MYSQL服务器上的表。对于一个FEDERATED表,本地的MYSQL服务器充当
客户端的角色,连接至另外一个MYSQL服务器,以使其能够根据你的需要访问远程表,使远程表的内容如同本地的一样
下面通过artist和painting表举例该问题,假如painting存在于远程服务器中,那么我们可以建立FEDERTED表访问远程表,FEDERTERD表结构
和远程表同样,而且FEDERATED存储引擎,使用connection字符串告诉本地服务器怎样连接远程服务器定位表
FEDERATED表定义如下:
painting CREATE TABLE `painting` (
`a_id` int(10) unsigned NOT NULL,
`p_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(30) NOT NULL,
`state` varchar(30) NOT NULL,
`price` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`p_id`),
KEY `a_id` (`a_id`)
) ENGINE=FEDERATED CONNECTION='mysql://username:password@hostname/dbname/tabname'
然后和FEDERATED建立连接关系,不过到目前为止,FEDERATED表只能用来访问其MYSQL服务器 ,而不能访问其他数据库引擎的数据库





















































你可能感兴趣的:(使用)