This slide is based on Jeffrey D. Ullman's work, which can be download from his website.
$R_1:=\delta(R_2)$.
将关系 $R_2$ 中重复的元组去掉, 仅保留一个拷贝在 $R_1$ 中.
A | B |
---|---|
1 | 2 |
3 | 4 |
1 | 2 |
A | B |
---|---|
1 | 2 |
3 | 4 |
$R_1:=\tau_{L}(R_2)$
$R_1$ 是 $R_2$ 中所有元组依次根据 $L$ 中第一、二、$\ldots$ 个属性的值进行排序所得的结果关系.
排序算子 $\tau$ 的结果关系既不是一个集合也不是一个包, 它是仅有的这样一个算子.
A | B |
---|---|
1 | 2 |
3 | 4 |
5 | 2 |
$\tau_{B}(R)=[(1,2),(5,2),(3,4)]$
聚集操作符(aggregation operator)不是关系代数的操作符, 但却是被分组操作符所使用的操作.
聚集操作符应用到关系的整个属性列上, 并生成单独的一个结果.
最重要的例子是:
A | B |
---|---|
1 | 3 |
3 | 4 |
3 | 2 |
SUM(A) = 7 COUNT(A) = 3 MAX(B) = 4 AVG(B) = 3
mysql> select count(*) from bars; +----------+ | count(*) | +----------+ | 7 | +----------+ 1 row in set (0.00 sec)
mysql> insert into bars (addr, license) values ("文昌路1","test1"); Query OK, 1 row affected, 1 warning (0.00 sec) mysql> select count(*) from bars; +----------+ | count(*) | +----------+ | 8 | +----------+ 1 row in set (0.01 sec) mysql> select count(name) from bars; +-------------+ | count(name) | +-------------+ | 8 | +-------------+ 1 row in set (0.00 sec)
mysql> show create table bars\G; *************************** 1. row *************************** Table: bars Create Table: CREATE TABLE `bars` ( `name` varchar(20) NOT NULL, `addr` varchar(255) DEFAULT NULL, `license` varchar(255) DEFAULT NULL, PRIMARY KEY (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk 1 row in set (0.00 sec) mysql> insert into bars values (NULL, "文昌路2","test2"); ERROR 1048 (23000): Column 'name' cannot be null
mysql> create table bars2( -> name varchar(20), -> addr varchar(255), -> license varchar(255) -> )ENGINE=InnoDB DEFAULT CHARSET=gbk; Query OK, 0 rows affected (0.03 sec) mysql> insert into bars2 values ("WestSide1", "文昌路1","test1"); Query OK, 1 row affected (0.00 sec) mysql> insert into bars2 values (NULL, "文昌路2","test2"); Query OK, 1 row affected (0.00 sec)
mysql> select count(*) from bars2; +----------+ | count(*) | +----------+ | 2 | +----------+ 1 row in set (0.00 sec) mysql> select count(name) from bars2; +-------------+ | count(name) | +-------------+ | 1 | +-------------+ 1 row in set (0.00 sec)
分组操作(grouping), 根据元组在一个或多个属性上的值把关系的元组拆分成“组”. 这样, 聚集操作就是对分好组的各个列进行计算.
这给我们提供了在经典关系代数表达式中不能表达的多个查询的描述方式.
分组操作符(grouping operator) $\gamma$ 是组合了分组和聚集操作的一个算子.
$R_1:=\gamma_L(R_2)$.
其中 $L$ 是下面情形之一:
根据 $L$ 中的分组属性将关系 R 进行分组.
在每个组内, 对于列表 $L$ 的聚集属性, 计算 $AGG(A)$.
对于每一组, 产生如下内容的元组:
A | B | C |
---|---|---|
1 | 2 | 3 |
4 | 5 | 6 |
1 | 2 | 5 |
$\gamma_{A,B,AVG(C)\rightarrow X}(R)=??$
首先, 对于关系 R, 根据属性 A, B 进行分组:
A | B | C |
---|---|---|
4 | 5 | 6 |
然后, 在每个组内, 对于属性列 $C$, 进行求平均值的聚集操作.
A | B | X |
---|---|---|
4 | 5 | 6 |
假设我们要进行($\theta$-)连接: $R\bowtie_C S$.
R 中某个元组如果找不到 S 中元组与其连接, 则称之为
外连接(Outerjoin) $R\stackrel{\circ}{\bowtie}S$ 首先进行的操作是自然连接 $R\bowtie S$, 然后再把来自 R 或 S 的悬浮元组加入其中. 加入的元组用 null 符号 $\perp$ 补齐那些出现在结果中但不具有值的属性.
A | B |
---|---|
1 | 2 |
4 | 5 |
B | C |
---|---|
2 | 3 |
6 | 7 |
A | B | C |
---|---|---|
1 | 2 | 3 |
4 | 5 | NULL |
NULL | 6 | 7 |
每个(扩展的)关系代数表达式都对应到一个与之等价的 SQL 语句.
mysql> select * from R natural left OUTER JOIN S; +------+------+------+ | B | A | C | +------+------+------+ | 2 | 1 | 3 | | 5 | 4 | NULL | +------+------+------+ 2 rows in set (0.00 sec)
MySQL supports the following JOIN syntaxes for the table_references part of SELECT statements and multiple-table DE- LETE and UPDATE statements:
table_references: table_reference, table_reference | table_reference [INNER | CROSS] JOIN table_reference [join_condition] | table_reference STRAIGHT_JOIN table_reference | table_reference LEFT [OUTER] JOIN table_reference join_condition | table_reference NATURAL [LEFT [OUTER]] JOIN table_reference | { OJ table_reference LEFT OUTER JOIN table_reference ON conditional_expr } | table_reference RIGHT [OUTER] JOIN table_reference join_condition | table_reference NATURAL [RIGHT [OUTER]] JOIN table_reference table_reference: tbl_name [[AS] alias] [index_hint)] join_condition: ON conditional_expr | USING (column_list) index_hint: USE {INDEX|KEY} (index_list)] | IGNORE {INDEX|KEY} (index_list)] | FORCE {INDEX|KEY} (index_list)] index_list: index_name [, index_name] ...
对于关系
SELECT AVG(price) FROM Sells WHERE beer='Bud';
mysql> SELECT AVG(price) -> FROM Sells -> WHERE beer='百威'; +--------------------+ | AVG(price) | +--------------------+ | 31.666666666666668 | +--------------------+ 1 row in set (0.00 sec)
检验一下
mysql> SELECT price FROM Sells WHERE beer='百威'; +-------+ | price | +-------+ | 30 | | 32 | | 32 | | 32 | | 32 | | 32 | +-------+ 6 rows in set (0.00 sec)
mysql> select (30+32*5)/6; +-------------+ | (30+32*5)/6 | +-------------+ | 31.6667 | +-------------+ 1 row in set (0.00 sec)
在聚集操作中加入
例如对于关系
例子: 对于关系
SELECT COUNT(DISTINCT price) FROM Sells WHERE beer='Bud';
mysql> SELECT COUNT(DISTINCT price) -> FROM Sells -> WHERE beer='贝克';
+-----------------------+ | COUNT(DISTINCT price) | +-----------------------+ | 4 | +-----------------------+ 1 row in set (0.00 sec)
但如果某个列没有非NULL值, 则对该列进行聚集操作的结果是
SELECT count(*) FROM Sells WHERE beer='Bud';
返回销售 Bud 啤酒的酒吧个数.
SELECT count(price) FROM Sells WHERE beer='Bud';
返回销售 Bud 啤酒并且有明确售价的酒吧个数.
mysql> SELECT count(*) -> FROM Sells -> WHERE beer='贝克'; +----------+ | count(*) | +----------+ | 5 | +----------+ 1 row in set (0.00 sec)
mysql> SELECT count(price) -> FROM Sells -> WHERE beer='贝克'; +--------------+ | count(price) | +--------------+ | 5 | +--------------+ 1 row in set (0.00 sec)
这说明贝克啤酒都有明确售价. 现在加入一条记录到表
mysql> INSERT INTO Sells (bar,beer) -> VALUES ("宝莱纳餐厅","贝克");
再执行上面的查询就会发现不同了.
在
从
对于关系
SELECT beer, AVG(price) FROM Sells GROUP BY beer;
beer | AVG(price) |
---|---|
Bud | 2.33 |
... | ... |
对于关系
SELECT drinker, AVG(price) FROM Frequents, Sells WHERE beer='Bud' AND Frequents.bar=Sells.bar GROUP BY drinker;
上面使用了 $\theta$-连接, 也可以改用自然连接.
SELECT drinker, AVG(price) FROM Frequents NATURAL JOIN Sells WHERE beer='Bud' GROUP BY drinker;
mysql> SELECT beer, AVG(price) -> FROM Sells -> GROUP BY beer; +-----------------+--------------------+ | beer | AVG(price) | +-----------------+--------------------+ | Stella Artois | 36 | | 健力士黑啤 | 36.4 | | 南非啤酒 | 36 | | 喜力 | 28.8 | | 嘉士伯 | 35 | | 安贝夫 | 39.75 | | 朝日 | 26 | | 生力 | 31.5 | | 百威 | 31.666666666666668 | | 科罗娜 | 40 | | 米勒 | 41 | | 纯种苦啤酒 | 33 | | 贝克 | 34.6 | | 麒麟 | 34.5 | +-----------------+--------------------+ 14 rows in set (0.00 sec)
mysql> SELECT drinker, AVG(price) -> FROM Frequents, Sells -> WHERE beer='贝克' AND -> Frequents.bar=Sells.bar -> GROUP BY drinker; +---------------------+------------+ | drinker | AVG(price) | +---------------------+------------+ | Alan Turing | 33 | | Andrew Chi-Chih Yao | NULL | | Charles Babbage | 35 | | Claude Shannon | 35 | | David Patterson | 42 | | Donald Ervin Knuth | 35 | | Edsger Dijkstra | 33 | | Gerald Jay Sussman | NULL | | Guy Steele Jr. | 35 | | atzjg | 28 | | Herbert A. Simon | 28 | | John Hennessy | 35 | | John von Neumann | 42 | | Lynn Conway | 28 | | Tim Berners-Lee | 33 | | Tony Hoare | NULL | +---------------------+------------+ 16 rows in set (0.00 sec)
也可以使用 Natural Join
mysql-> select drinker, AVG(price) -> FROM Frequents NATURAL JOIN Sells -> WHERE beer='贝克' -> GROUP BY drinker;
如果不加上
mysql> SELECT drinker, AVG(price) -> FROM Frequents, Sells -> WHERE beer='贝克' AND -> Frequents.bar=Sells.bar; +-------------+------------+ | drinker | AVG(price) | +-------------+------------+ | Alan Turing | 34 | +-------------+------------+ 1 row in set (0.00 sec)
它计算的是酒客经常去的有贝克啤酒出售的酒吧关于贝克的平均售价.
mysql> SELECT avg(price) FROM Frequents,Sells -> WHERE beer="贝克" AND Frequents.bar=Sells.bar; +------------+ | avg(price) | +------------+ | 34 | +------------+ 1 row in set (0.00 sec)
如果执行下面的命令, 会怎样?
mysql> SELECT drinker FROM Frequents,Sells -> WHERE beer="贝克" -> AND Frequents.bar=Sells.bar; +---------------------+ | drinker | +---------------------+ | Alan Turing | | Andrew Chi-Chih Yao | | Charles Babbage | | Claude Shannon | | David Patterson | | Donald Ervin Knuth | | Edsger Dijkstra | | Gerald Jay Sussman | | Guy Steele Jr. | | atzjg | | atzjg | | Herbert A. Simon | | John Hennessy | | John von Neumann | | Lynn Conway | | Tim Berners-Lee | | Tony Hoare | +---------------------+ 17 rows in set (0.00 sec)
发现多了一条记录, 是用户
mysql> INSERT INTO Sells (bar,beer) -> VALUES ("宝莱纳餐厅","贝克");
使得其价格属性值为
在 SELECT 语句中如果使用了聚集操作, 则 SELECT 列表中的每个元素只能是下列两种情形之一:
你也许会认为可以根据下面的查询语句找到 Bud 啤酒卖得最便宜的酒吧:
SELECT bar, MIN(price) FROM Sells WHERE beer='Bud';
但是这个查询在 SQL 中是不合法的.
mysql> SELECT bar, MIN(price) -> FROM Sells -> WHERE beer='贝克'; +----------+------------+ | bar | MIN(price) | +----------+------------+ | 3DArtBar | 28 | +----------+------------+ 1 row in set (0.00 sec)
看上去在
mysql> select * from Sells where beer="贝克"; +--------------------------+--------+-------+ | bar | beer | price | +--------------------------+--------+-------+ | 3DArtBar | 贝克 | 35 | | HardRock | 贝克 | 33 | | Westside | 贝克 | 28 | | 宝莱纳餐厅 | 贝克 | NULL | | 扬州老啤酒厂酒吧 | 贝克 | 42 | | 木板房啤酒吧 | 贝克 | 35 | +--------------------------+--------+-------+ 6 rows in set (0.00 sec)
可见在进行聚集操作后,
如果查询价格最低的啤酒信息, 则可以使用下面的语句.
mysql> SELECT * FROM Sells -> WHERE price <= all -> (SELECT price FROM Sells);
但是现在要查询的是卖贝克啤酒最便宜的酒吧. 因此要缩小查询的范围.
mysql> SELECT * FROM -> (SELECT * FROM Sells WHERE beer="贝克") AS Beike -> WHERE price <= all -> (SELECT price FROM Sells WHERE beer="贝克"); +----------+--------+-------+ | bar | beer | price | +----------+--------+-------+ | Westside | 贝克 | 28 | +----------+--------+-------+ 1 row in set (0.00 sec)
如果加了
对于关系
SELECT beer, AVG(price) FROM Sells GROUP BY beer HAVING COUNT(bar)>=3 OR beer IN (SELECT name FROM Beers WHERE manf='Peter''s');
mysql> SELECT beer, AVG(price) -> FROM Sells -> GROUP BY beer -> HAVING COUNT(bar)>=3 OR -> beer IN (SELECT name -> FROM Beers -> WHERE manf='百威英博啤酒集团'); +-----------------+--------------------+ | beer | AVG(price) | +-----------------+--------------------+ | Stella Artois | 36 | | 健力士黑啤 | 36.4 | | 南非啤酒 | 36 | | 喜力 | 28.8 | | 嘉士伯 | 35 | | 安贝夫 | 39.75 | | 朝日 | 26 | | 百威 | 31.666666666666668 | | 科罗娜 | 40 | | 纯种苦啤酒 | 33 | | 贝克 | 34.6 | +-----------------+--------------------+ 11 rows in set (0.00 sec)
不妨分别查看一下分属上面两个条件的记录:
mysql> SELECT beer, AVG(price) -> FROM Sells -> GROUP BY beer -> HAVING COUNT(bar)>=3; +-----------------+--------------------+ | beer | AVG(price) | +-----------------+--------------------+ | 健力士黑啤 | 36.4 | | 南非啤酒 | 36 | | 喜力 | 28.8 | | 嘉士伯 | 35 | | 安贝夫 | 39.75 | | 朝日 | 26 | | 百威 | 31.666666666666668 | | 科罗娜 | 40 | | 纯种苦啤酒 | 33 | | 贝克 | 34.6 | +-----------------+--------------------+ 10 rows in set (0.00 sec)
mysql> SELECT beer, AVG(price) -> FROM Sells -> GROUP BY beer -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf='百威英博啤酒集团'); +---------------+--------------------+ | beer | AVG(price) | +---------------+--------------------+ | Stella Artois | 36 | | 百威 | 31.666666666666668 | | 贝克 | 34.6 | +---------------+--------------------+ 3 rows in set (0.00 sec)
想一想下面的语句会返回什么? 为什么?
mysql> SELECT beer, AVG(price) -> FROM Sells -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf='百威英博啤酒集团');
Empty set (0.00 sec)
此时默认只有一个记录, 并且此时返回的
mysql> SELECT beer, AVG(price) FROM Sells; +--------+------------+ | beer | AVG(price) | +--------+------------+ | 喜力 | 34.42 | +--------+------------+ 1 row in set (0.00 sec)
子查询中可以是任意的语句
在子查询外, 属性只能是下面之一:
(与含有聚集操作的
对于 MySQL 来说, 这些语句可以运行, 但是可能有时不是你期望的. 对于下面的语句, 指出所得结果数据的含义. 特别是为什么它们的
mysql> SELECT beer,bar, AVG(price) -> FROM Sells -> GROUP BY beer -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf = '喜利得(中国)有限公司'); +--------+----------+------------+ | beer | bar | AVG(price) | +--------+----------+------------+ | 喜力 | 3DArtBar | 28.8 | +--------+----------+------------+ 1 row in set (0.01 sec)
mysql> SELECT beer,bar, AVG(price) -> FROM Sells -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf='喜利得(中国)有限公司'); +--------+----------+------------+ | beer | bar | AVG(price) | +--------+----------+------------+ | 喜力 | 3DArtBar | 34.42 | +--------+----------+------------+ 1 row in set (0.00 sec)
上面的语句没有
mysql> select count(*), SUM(price) from Sells; +----------+------------+ | count(*) | SUM(price) | +----------+------------+ | 51 | 1721 | +----------+------------+ 1 row in set (0.00 sec) mysql> select 1721/51; +---------+ | 1721/51 | +---------+ | 33.7451 | +---------+ 1 row in set (0.00 sec)
别忘了, 我们曾经插入一条记录: 某个酒吧销售某种啤酒但没有售价.
mysql> select SUM(price),count(price), SUM(price)/count(price) AS AVGPrice from Sells; +------------+--------------+----------+ | SUM(price) | count(price) | AVGPrice | +------------+--------------+----------+ | 1721 | 50 | 34.42 | +------------+--------------+----------+ 1 row in set (0.00 sec)
mysql> SELECT beer,bar, AVG(price) -> FROM Sells -> GROUP BY bar -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf LIKE '%公司%'); +-----------------+-----------------+-------------------+ | beer | bar | AVG(price) | +-----------------+-----------------+-------------------+ | 喜力 | 3DArtBar | 32.4 | | 健力士黑啤 | HardRock | 34.42857142857143 | | 健力士黑啤 | Westside | 33.42857142857143 | | 健力士黑啤 | 宝莱纳餐厅 | 33.42857142857143 | | 健力士黑啤 | 苏荷酒吧 | 34 | +-----------------+-----------------+-------------------+ 5 rows in set (0.01 sec)
mysql> SELECT beer,bar, AVG(price) -> FROM Sells -> GROUP BY beer -> HAVING beer IN -> (SELECT name FROM Beers -> WHERE manf like '%公司%'); +-----------------+--------------------------+------------+ | beer | bar | AVG(price) | +-----------------+--------------------------+------------+ | 健力士黑啤 | HardRock | 36.4 | | 喜力 | 3DArtBar | 28.8 | | 安贝夫 | 3DArtBar | 39.75 | | 生力 | 木板房啤酒吧 | 31.5 | | 科罗娜 | HardRock | 40 | | 米勒 | 扬州老啤酒厂酒吧 | 41 | | 纯种苦啤酒 | Westside | 33 | | 麒麟 | 扬州老啤酒厂酒吧 | 34.5 | +-----------------+--------------------------+------------+ 8 rows in set (0.00 sec)
mysql> select beer,bar,AVG(price) -> from Sells -> group by bar; +-----------------+--------------------------+--------------------+ | beer | bar | AVG(price) | +-----------------+--------------------------+--------------------+ | 喜力 | 3DArtBar | 32.4 | | 健力士黑啤 | HardRock | 34.42857142857143 | | 健力士黑啤 | Westside | 33.42857142857143 | | 健力士黑啤 | 宝莱纳餐厅 | 33.42857142857143 | | Stella Artois | 扬州老啤酒厂酒吧 | 37.3 | | 嘉士伯 | 木板房啤酒吧 | 34.166666666666664 | | 健力士黑啤 | 苏荷酒吧 | 34 | +-----------------+--------------------------+--------------------+ 7 rows in set (0.00 sec)
mysql> select beer,bar,AVG(price) from Sells group by beer; +-----------------+--------------------------+--------------------+ | beer | bar | AVG(price) | +-----------------+--------------------------+--------------------+ | Stella Artois | 扬州老啤酒厂酒吧 | 36 | | 健力士黑啤 | HardRock | 36.4 | | 南非啤酒 | Westside | 36 | | 喜力 | 3DArtBar | 28.8 | | 嘉士伯 | 3DArtBar | 35 | | 安贝夫 | 3DArtBar | 39.75 | | 朝日 | Westside | 26 | | 生力 | 木板房啤酒吧 | 31.5 | | 百威 | 3DArtBar | 31.666666666666668 | | 科罗娜 | HardRock | 40 | | 米勒 | 扬州老啤酒厂酒吧 | 41 | | 纯种苦啤酒 | Westside | 33 | | 贝克 | 3DArtBar | 34.6 | | 麒麟 | 扬州老啤酒厂酒吧 | 34.5 | +-----------------+--------------------------+--------------------+ 14 rows in set (0.00 sec)
mysql> select beer,bar,price from Sells; +-----------------+--------------------------+-------+ | beer | bar | price | +-----------------+--------------------------+-------+ | 喜力 | 3DArtBar | 25 | | 嘉士伯 | 3DArtBar | 32 | | 安贝夫 | 3DArtBar | 40 | | 百威 | 3DArtBar | 30 | | 贝克 | 3DArtBar | 35 | | 健力士黑啤 | HardRock | 39 | | 喜力 | HardRock | 28 | | 嘉士伯 | HardRock | 30 | | 安贝夫 | HardRock | 39 | | 百威 | HardRock | 32 | | 科罗娜 | HardRock | 40 | | 贝克 | HardRock | 33 | | 健力士黑啤 | Westside | 39 | | 南非啤酒 | Westside | 36 | | 朝日 | Westside | 26 | | 百威 | Westside | 32 | | 科罗娜 | Westside | 40 | | 纯种苦啤酒 | Westside | 33 | | 贝克 | Westside | 28 | | 健力士黑啤 | 宝莱纳餐厅 | 39 | | 南非啤酒 | 宝莱纳餐厅 | 36 | | 喜力 | 宝莱纳餐厅 | 28 | | 朝日 | 宝莱纳餐厅 | 26 | | 百威 | 宝莱纳餐厅 | 32 | | 科罗娜 | 宝莱纳餐厅 | 40 | | 纯种苦啤酒 | 宝莱纳餐厅 | 33 | | 贝克 | 宝莱纳餐厅 | NULL | | Stella Artois | 扬州老啤酒厂酒吧 | 36 | | 健力士黑啤 | 扬州老啤酒厂酒吧 | 26 | | 喜力 | 扬州老啤酒厂酒吧 | 35 | | 安贝夫 | 扬州老啤酒厂酒吧 | 40 | | 百威 | 扬州老啤酒厂酒吧 | 32 | | 科罗娜 | 扬州老啤酒厂酒吧 | 40 | | 米勒 | 扬州老啤酒厂酒吧 | 50 | | 纯种苦啤酒 | 扬州老啤酒厂酒吧 | 33 | | 贝克 | 扬州老啤酒厂酒吧 | 42 | | 麒麟 | 扬州老啤酒厂酒吧 | 39 | | 嘉士伯 | 木板房啤酒吧 | 43 | | 安贝夫 | 木板房啤酒吧 | 40 | | 生力 | 木板房啤酒吧 | 25 | | 米勒 | 木板房啤酒吧 | 32 | | 贝克 | 木板房啤酒吧 | 35 | | 麒麟 | 木板房啤酒吧 | 30 | | 健力士黑啤 | 苏荷酒吧 | 39 | | 南非啤酒 | 苏荷酒吧 | 36 | | 喜力 | 苏荷酒吧 | 28 | | 朝日 | 苏荷酒吧 | 26 | | 生力 | 苏荷酒吧 | 38 | | 百威 | 苏荷酒吧 | 32 | | 科罗娜 | 苏荷酒吧 | 40 | | 纯种苦啤酒 | 苏荷酒吧 | 33 | +-----------------+--------------------------+-------+ 51 rows in set (0.00 sec)
如果
mysql> SELECT beer,bar,price -> FROM Sells -> GROUP BY bar; +-----------------+--------------------------+-------+ | beer | bar | price | +-----------------+--------------------------+-------+ | 喜力 | 3DArtBar | 25 | | 健力士黑啤 | HardRock | 39 | | 健力士黑啤 | Westside | 39 | | 健力士黑啤 | 宝莱纳餐厅 | 39 | | Stella Artois | 扬州老啤酒厂酒吧 | 36 | | 嘉士伯 | 木板房啤酒吧 | 43 | | 健力士黑啤 | 苏荷酒吧 | 39 | +-----------------+--------------------------+-------+ 7 rows in set (0.00 sec)
与查询不同, 更新命令不返回结果, 但以某种方式改变数据库.
三种更新操作:
插入单个元组:
INSERT INTO <relation> VALUES (<list of values>);
例子: 对于关系
INSERT INTO Likes VALUES ('Sally', 'Bud');
可以在要插入记录的关系名后面加入一列属性名.
这样做有两个原因:
下面是另一种方式, 将 Sally 喜欢 Bud 啤酒这一事实添加到关系
INSERT INTO Likes(beer, drinker) VALUES ('Bud', 'Sally');
MySQL 中提供了一次插入多个元组的方式.
INSERT INTO Likes(beer, drinker) VALUES ('Bud', 'Sally'), ('Bud2', 'Sally2'), ('Bud3', 'Sally3');
在
当要插入的元组在某个属性上没有值时, 缺省值(default value) 就派上用场了.
CREATE TABLE Drinkers( name CHAR(30) PRIMARY KEY, addr CHAR(50) DEFAULT '123 Sesame St.', phone CHAR(16) );
INSERT INTO Drinkers(name) VALUES('Sally');
所得到的元组
name | address | phone |
---|---|---|
Sally | 123 Sesame St. | NULL |
CREATE TABLE PotBuddies( name CHAR(30) PRIMARY KEY, friend_of CHAR(50) DEFAULT 'atzjg' );
INSERT INTO PotBuddies(name) VALUES('Sally');
我们可以将某个查询的整个结果插入到某个关系中, 利用下面的形式:
INSERT INTO <relation> (<subquery>);
利用关系
INSERT INTO PotBuddies(name) (SELECT d2.drinker FROM Frequents d1, Frequents d2 WHERE d1.drinker='Sally' AND d2.drinker <> 'Sally' AND d1.bar = d2.bar );
mysql> INSERT INTO PotBuddies(name) -> (SELECT d2.drinker -> FROM Frequents d1, Frequents d2 -> WHERE d1.drinker='atzjg' AND -> d2.drinker <> 'atzjg' AND -> d1.bar = d2.bar -> ); Query OK, 5 rows affected (0.42 sec) Records: 5 Duplicates: 0 Warnings: 0
mysql> select * from PotBuddies; +---------------------+-----------+ | name | friend_of | +---------------------+-----------+ | Andrew Chi-Chih Yao | atzjg | | Gerald Jay Sussman | atzjg | | Herbert A. Simon | atzjg | | Lynn Conway | atzjg | | Sally | atzjg | | Tony Hoare | atzjg | +---------------------+-----------+ 6 rows in set (0.00 sec)
从某个关系中删除满足某种条件的元组, 使用下面的语句:
DELETE FROM <relation> WHERE <condition>;
从关系
DELETE FROM Likes WHERE drinker='Sally' AND beer='Bud';
将关系
DELETE FROM Likes;
注意不需要
从关系
DELETE FROM Beers b WHERE EXISTS ( SELECT name FROM Beers WHERE manf=b.manf AND name <> b.name);
子查询返回的结果关系是那些是同一厂家生产但与元组
mysql> DELETE FROM PotBuddies -> WHERE name ='Sally';
在做上面最后一个实验前, 先做一些准备. 比如搞清楚要删除的是什么?
mysql> SELECT * FROM Beers b -> WHERE EXISTS ( -> SELECT name FROM Beers -> WHERE manf=b.manf AND -> name <> b.name); +---------------+--------------------------+ | name | manf | +---------------+--------------------------+ | 百威 | 百威英博啤酒集团 | | 贝克 | 百威英博啤酒集团 | | Stella Artois | 百威英博啤酒集团 | +---------------+--------------------------+ 3 rows in set (0.35 sec)
结果表明只有百威、贝克和 Stella Artois 是同一厂家生产的. 你可以使用
如果将上面的
+---------------+--------------------------+ | name | manf | +---------------+--------------------------+ | 百威 | 百威英博啤酒集团 | | 贝克 | 百威英博啤酒集团 | +---------------+--------------------------+
看看下面的语句会发生什么?
mysql> SELECT * FROM Beers b -> WHERE b.name in ( -> SELECT name FROM Beers -> WHERE manf=b.manf AND -> name <> b.name);
Empty set (0.00 sec)
注意如果直接键入子查询, 则mysql不认识这里的b.
mysql> select name from beers where manf=b.manf and -> name<>b.name; ERROR 1054 (42S22): Unknown column 'b.manf' in 'where clause'
正确的应该是
mysql> select * from Beers b -> where b.name in ( -> select Beers.name from Beers, Beers as b -> where Beers.manf=b.manf -> and Beers.name <> b.name); +---------------+--------------------------+ | name | manf | +---------------+--------------------------+ | 百威 | 百威英博啤酒集团 | | 贝克 | 百威英博啤酒集团 | | Stella Artois | 百威英博啤酒集团 | +---------------+--------------------------+ 3 rows in set (0.00 sec)
mysql> SELECT * FROM Beers -> WHERE name in ( -> SELECT name FROM Beers AS b -> WHERE manf=b.manf AND -> name <> b.name);
mysql> SELECT * FROM Beers -> WHERE EXISTS ( -> SELECT name FROM Beers AS b -> WHERE manf=b.manf AND -> name <> b.name);
mysql> DELETE FROM Beers b -> WHERE EXISTS ( -> SELECT name FROM Beers -> WHERE manf=b.manf AND -> name <> b.name); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'b WHERE EXISTS ( SELECT name FROM Beers WHERE manf=b.manf AND name <> b.name)' at line 1
这样的语句在 MySQL 中不能运行. 我们得更改以下. 下面的正确吗?
mysql> DELETE FROM Beers -> WHERE EXISTS ( -> SELECT name FROM Beers AS b -> WHERE b.manf=manf AND -> b.name <> name);
ERROR 1093 (HY000): You can't specify target table 'Beers' for update in FROM clause
正确的应该是:
mysql> SELECT * FROM Beers -> WHERE name IN ( -> SELECT B.name FROM ( -> SELECT B0.name, B0.manf FROM Beers AS B0, Beers AS B1 -> WHERE B1.name <> B0.name AND -> B1.manf=B0.manf -> )B );
mysql> DELETE FROM Beers -> WHERE name IN ( -> SELECT B.name FROM ( -> SELECT B0.name, B0.manf FROM Beers AS B0, Beers AS B1 -> WHERE B1.name <> B0.name AND -> B1.manf=B0.manf -> )B );
下面我将删除的数据重新加入到数据库中.
mysql> INSERT INTO Beers VALUES -> ('百威','百威英博啤酒集团'), -> ('贝克','百威英博啤酒集团'), -> ('Stella Artois','百威英博啤酒集团');
注意上面一下子插入三条记录的方式是
注意
为解释删除操作的具体执行, 我们仅举个简单的例子.
假设 安海斯-布希公司(Anheuser-Busch) 只生产
假设变量
因为有
接着, 假设变量
答案: 我们删除的是
事实上: 删除操作是分两个阶段进行的:
创建表
mysql> create table beers( -> name varchar(20), -> manf varchar(30) -> );
插入必要的数据
mysql> insert into beers values("bud", "Anheuser-Busch"); Query OK, 1 row affected (0.01 sec) mysql> insert into beers values("Bud Lite", "Anheuser-Busch"); Query OK, 1 row affected (0.00 sec) mysql> insert into beers("ABC","my.com"); Query OK, 1 row affected (0.00 sec)
检查数据
mysql> select * from beers; +----------+----------------+ | name | manf | +----------+----------------+ | bud | Anheuser-Busch | | Bud Lite | Anheuser-Busch | | ABC | my.com | +----------+----------------+ 3 rows in set (0.00 sec)
mysql> select * from beers b -> where exists ( -> select name from beers -> where manf=b.manf and name<>b.name); +----------+----------------+ | name | manf | +----------+----------------+ | bud | Anheuser-Busch | | Bud Lite | Anheuser-Busch | +----------+----------------+ 2 rows in set (0.00 sec)
mysql> select * from beers b -> where exists ( -> select name from beers -> where manf=b.manf and name < b.name); +----------+----------------+ | name | manf | +----------+----------------+ | Bud Lite | Anheuser-Busch | +----------+----------------+ 1 row in set (0.00 sec)
这里只有一行数据, 这是因为
mysql> select "bud" < "Bud Lite"; +--------------------+ | "bud" < "Bud Lite" | +--------------------+ | 1 | +--------------------+ 1 row in set (0.00 sec)
也就是说投影的是表 b 中的数据 "Bud Lite". 但是下面的命令返回的是空集.
mysql> select * from beers b -> where b.name in( -> select name from beers where manf=b.manf and -> name <> b.name); Empty set (0.00 sec)
尝试删除
mysql> delete from beers -> where exists( -> select name from beers as b -> where manf=b.manf and name<>b.name -> ); ERROR 1093 (HY000): You can't specify target table 'beers' for update in FROM clause
提示这种删除方式不可行, (删除后的)目标表 'beers'不可以被指定到 FROM 子句中.
使用之前推荐的方式删除
mysql> delete from beers where name in( -> select b.name from ( -> select b0.name, b0.manf from beers as b0, beers as b1 -> where b0.name <> b1.name and b0.manf=b1.manf -> )b ); Query OK, 2 rows affected (0.01 sec)
检查删除后的数据
mysql> select * from beers; +------+--------+ | name | manf | +------+--------+ | ABC | my.com | +------+--------+ 1 row in set (0.00 sec)
"bud" 和 "Bud Lite" 这两个元组都被删除了.
要更新关系中某个元组的某些属性值, 采用下面的语句:
UPDATE <relation> SET <list of attribute assignments> WHERE <condition on tuples>;
将酒客 Fred 的电话号码更改为
UPDATE Drinkers SET phone='555-1212' WHERE name='Fred';
假设
UPDATE Sells SET price=4.00 WHERE price>4.00;
mysql> UPDATE Drinkers -> SET phone='555-1212' -> WHERE name='atzjg';
mysql> select * from Drinkers where name='atzjg'; +-------+---------------------+----------+ | name | addr | phone | +-------+---------------------+----------+ | atzjg | atzjg.net,atzjg.com | 555-1212 | +-------+---------------------+----------+ 1 row in set (0.01 sec)
在做上面最后一个实验时, 不妨将
mysqldump -uroot -h localhost -p test_bar Sells > Sells.sql
然后不妨更新一下数据, 先查看一下原始的价格.
mysql> select * from Sells where price > 40; +--------------------------+-----------+-------+ | bar | beer | price | +--------------------------+-----------+-------+ | 扬州老啤酒厂酒吧 | 米勒 | 50 | | 扬州老啤酒厂酒吧 | 贝克 | 42 | | 木板房啤酒吧 | 嘉士伯 | 43 | +--------------------------+-----------+-------+ 3 rows in set (0.00 sec)
UPDATE Sells SET price=51.00 WHERE price>40.00;
请使用
Product(maker, model, type) PC(model, speed, ram, hd, price) Laptop(model, speed, ram, hd, screen, price) Printer(model, color, type, price)
通过两条
INSERT INTO Product VALUES ('C', '1100', 'PC'); INSERT INTO PC VALUES ('1100', 3.2, 1024, 160, 2499);
删除所有硬盘容量低于 100GB 的 PC.
DELETE FROM PC WHERE hd < 100;
删除所有不生产打印机的厂商所生成的笔记本电脑.
分析: 我们要删除的是笔记本电脑, 这是 Laptop 表中的信息, 但是相关的在 Product 中的信息也应该被删除. (可以使用外键, 级联删除. 这里我们暂时不用. 因此先删除 Laptop 中的相关元组, 然后再删除 Product 中相应的记录.)
生产打印机的厂商是
使用
test
生产打印机的厂商为
SELECT maker FROM product WHERE type="printer";
将它作为子查询, 那么不生产打印机的厂商可以使用 NOT IN 来判断
WHERE maker NOT IN (SELECT maker FROM product WHERE type="printer")
现在要从 laptop 表中删除那些来自于上述厂商的笔记本电脑
SELECT * FROM Laptop WHERE model IN ( SELECT model FROM Product WHERE type='laptop' AND maker NOT IN (SELECT maker FROM Product WHERE type='printer') );
SELECT * FROM Laptop NATURAL JOIN Product WHERE maker NOT IN (SELECT maker FROM Product WHERE type='printer') ;
DELETE FROM Laptop WHERE model IN ( SELECT model FROM Product WHERE type='laptop' AND maker NOT IN (SELECT maker FROM Product WHERE type='printer') );
注意下面的语句无法运行
DELETE FROM Laptop NATURAL JOIN Product WHERE maker NOT IN (SELECT maker FROM Product WHERE type='printer') ;
但是 Product 表中还有相应的数据, 我们也要将它们删除.
如果采用下面的语句
DELETE FROM Product WHERE model IN ( SELECT model FROM Product WHERE type='laptop' AND maker NOT IN (SELECT maker FROM Product WHERE type='printer') );
将会带来错误. You can't specify target table 'Product' for update in FROM clause.
DELETE FROM Product WHERE type='laptop' AND model NOT IN (SELECT model FROM Laptop);
首先
insert into laptop (model) ( SELECT model FROM Product WHERE type='laptop' AND maker NOT IN (SELECT maker FROM Product WHERE type='printer') )
最后恢复数据
mysql -uroot -p test_products < D:\test_products.sql
UPDATE laptop NATURAL JOIN Product SET screen=1, price=price-100 WHERE maker='B';
如果将