3.6. 常用查询的例子
在一些例子中,使用数据库表“shop”来储存某个商人(经销商)的每件物品(物品号)的价格。假定每个商人对每项物品有一个固定价格,那么(物品,商人)即为该记录的主关键字。
启动命令行工具mysql并选择数据库:
shell> mysql your-database-name
(在大多数MySQL中,你可以使用test数据库)。
你可以使用以下语句创建示例表:
mysql> CREATE TABLE shop (
-> article INT(4)UNSIGNED ZEROFILL DEFAULT '0000' NOT NULL,
-> dealer CHAR(20) DEFAULT '' NOT NULL,
-> price DOUBLE(16,2) DEFAULT '0.00' NOT NULL,
-> PRIMARYKEY(article, dealer));
mysql> INSERT INTO shop VALUES
-> (1,'A',3.45),(1,'B',3.99),(2,'A',10.99),(3,'B',1.45),
-> (3,'C',1.69),(3,'D',1.25),(4,'D',19.95);
执行语句后,表应包含以下内容:
mysql> SELECT * FROM shop;
+---------+--------+-------+
| article | dealer | price |
+---------+--------+-------+
| 0001 |A | 3.45 |
| 0001 |B | 3.99 |
| 0002 |A | 10.99 |
| 0003 |B | 1.45 |
| 0003 |C | 1.69 |
| 0003 |D | 1.25 |
| 0004 |D | 19.95 |
+---------+--------+-------+
“最大的物品号是什么?”
SELECT MAX(article) AS article FROM shop;
+---------+
| article |
+---------+
| 4 |
+---------+
任务:找出最贵物品的编号、销售商和价格。
这很容易用一个子查询做到:
SELECT article, dealer, price
FROM shop
WHERE price=(SELECT MAX(price) FROMshop);
另一个解决方案是按价格降序排序所有行并用MySQL特定LIMIT子句只得到第一行:
SELECT article, dealer, price
FROM shop
ORDER BY price DESC
LIMIT 1;
注:如果有多项最贵的物品( 例如每个的价格为19.95),LIMIT解决方案仅仅显示其中一个!
任务:每项物品的的最高价格是多少?
SELECT article, MAX(price) AS price
FROM shop
GROUP BY article
+---------+-------+
| article | price |
+---------+-------+
| 0001| 3.99 |
| 0002 | 10.99 |
| 0003 | 1.69 |
| 0004 | 19.95 |
+---------+-------+
任务:对每项物品,找出最贵价格的物品的经销商。
可以用这样一个子查询解决该问题:
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
你可以清空MySQL用户变量以记录结果,不必将它们保存到客户端的临时变量中。(参见 9.3节,“用户变量”.)。
例如,要找出价格最高或最低的物品的,其方法是:
mysql> SELECT@min_price:=MIN(price),@max_price:=MAX(price) FROM shop;
mysql> SELECT *FROM shop WHERE price=@min_price OR price=@max_price;
+---------+--------+-------+
| article | dealer | price |
+---------+--------+-------+
| 0003 | D | 1.25 |
| 0004 | D | 19.95 |
+---------+--------+-------+
在MySQL中,InnoDB表支持对外部关键字约束条件的检查。参见15.2节,“InnoDB存储引擎”。还可以参见 1.8.5.5节,“外键”。
只是联接两个表时,不需要外部关键字。对于除InnoDB类型的表,当使用REFERENCES tbl_name(col_name)子句定义列时可以使用外部关键字,该子句没有实际的效果,只作为备忘录或注释来提醒,你目前正定义的列指向另一个表中的一个列。执行该语句时,实现下面很重要:
· MySQL不执行表tbl_name 中的动作,例如作为你正定义的表中的行的动作的响应而删除行;换句话说,该句法不会致使ON DELETE或ON UPDATE行为(如果你在REFERENCES子句中写入ON DELETE或ON UPDATE子句,将被忽略)。
· 该句法可以创建一个column;但不创建任何索引或关键字。
· 如果用该句法定义InnoDB表,将会导致错误。
你可以使用作为联接列创建的列,如下所示:
CREATE TABLE person (
id SMALLINT UNSIGNED NOTNULL AUTO_INCREMENT,
name CHAR(60) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE shirt (
id SMALLINT UNSIGNED NOTNULL AUTO_INCREMENT,
style ENUM('t-shirt','polo', 'dress') NOT NULL,
color ENUM('red', 'blue','orange', 'white', 'black') NOT NULL,
owner SMALLINT UNSIGNEDNOT NULL REFERENCES person(id),
PRIMARY KEY (id)
);
INSERT INTO person VALUES (NULL, 'AntonioPaz');
SELECT @last := LAST_INSERT_ID();
INSERT INTO shirt VALUES
(NULL, 'polo', 'blue', @last),
(NULL, 'dress', 'white', @last),
(NULL, 't-shirt', 'blue', @last);
INSERT INTO person VALUES (NULL, 'LillianaAngelovska');
SELECT @last := LAST_INSERT_ID();
INSERT INTO shirt VALUES
(NULL, 'dress', 'orange', @last),
(NULL, 'polo', 'red', @last),
(NULL, 'dress', 'blue', @last),
(NULL, 't-shirt', 'white', @last);
SELECT * FROM person;
+----+---------------------+
| id |name |
+----+---------------------+
| 1 | AntonioPaz |
| 2 | Lilliana Angelovska |
+----+---------------------+
SELECT * FROM shirt;
+----+---------+--------+-------+
| id | style | color |owner |
+----+---------+--------+-------+
| 1 | polo |blue | 1 |
| 2 | dress | white | 1 |
| 3 | t-shirt | blue | 1 |
| 4 | dress | orange| 2 |
| 5 | polo |red | 2 |
| 6 | dress |blue | 2 |
| 7 | t-shirt | white | 2 |
+----+---------+--------+-------+
SELECT s.* FROM person p, shirt s
WHEREp.name LIKE 'Lilliana%'
AND s.owner = p.id
AND s.color <> 'white';
+----+-------+--------+-------+
| id | style | color | owner |
+----+-------+--------+-------+
| 4 | dress | orange| 2 |
| 5 | polo |red | 2 |
| 6 | dress | blue | 2 |
+----+-------+--------+-------+
按照这种方式使用,REFERENCES子句不会显示在SHOW CREATE TABLE或DESCRIBE的输出中:
SHOW CREATE TABLE shirt\G
*************************** 1. row***************************
Table: shirt
Create Table: CREATE TABLE `shirt` (
`id` smallint(5) unsigned NOT NULLauto_increment,
`style` enum('t-shirt','polo','dress') NOTNULL,
`color`enum('red','blue','orange','white','black') NOT NULL,
`owner` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
在列定义中,按这种方式使用REFERENCES作为注释或“提示”适用于表MyISAM和BerkeleyDB。
一个比较灵活的例子是寻找两个通过OR组合到一起的关键字:
SELECT field1_index, field2_index FROMtest_table
WHERE field1_index = '1' OR field2_index = '1'
该情形是已经优化过的。参见7.2.6节,“索引合并优化”。
还可以使用UNION将两个单独的SELECT语句的输出合成到一起来更有效地解决该问题。参见13.2.7.2节,“UNION语法
”。
每个SELECT只搜索一个关键字,可以进行优化:
SELECT field1_index, field2_index
FROM test_table WHEREfield1_index = '1'
UNION
SELECT field1_index, field2_index
FROM test_table WHEREfield2_index = '1';
下面的例子显示了如何使用位组函数来计算每个月中用户访问网页的天数。
CREATE TABLE t1 (year YEAR(4), month INT(2)UNSIGNED ZEROFILL,
day INT(2) UNSIGNED ZEROFILL);
INSERT INTO t1VALUES(2000,1,1),(2000,1,20),(2000,1,30),(2000,2,2),
(2000,2,23),(2000,2,23);
示例表中含有代表用户访问网页的年-月-日值。可以使用以下查询来确定每个月的访问天数:
SELECTyear,month,BIT_COUNT(BIT_OR(1<<day)) AS days FROM t1
GROUP BYyear,month;
将返回:
+------+-------+------+
| year | month | days |
+------+-------+------+
| 2000 | 01| 3 |
| 2000 | 02| 2 |
+------+-------+------+
该查询计算了在表中按年/月组合的不同天数,可以自动去除重复的询问。
可以通过AUTO_INCREMENT属性为新的行产生唯一的标识:
CREATE TABLE animals (
id MEDIUMINT NOTNULL AUTO_INCREMENT,
name CHAR(30) NOTNULL,
PRIMARY KEY (id)
);
INSERT INTO animals (name) VALUES
('dog'),('cat'),('penguin'),
('lax'),('whale'),('ostrich');
SELECT * FROM animals;
将返回:
+----+---------+
| id | name |
+----+---------+
| 1 | dog |
| 2 | cat |
| 3 | penguin |
| 4 | lax |
| 5 | whale |
| 6 | ostrich |
+----+---------+
你可以使用LAST_INSERT_ID()SQL函数或mysql_insert_id() C API函数来查询最新的AUTO_INCREMENT值。这些函数与具体连接有关,因此其返回值不会被其它执行插入功能的连接影响。
注释:对于多行插入,LAST_INSERT_ID()和mysql_insert_id()从插入的第一行实际返回AUTO_INCREMENT关键字。在复制设置中,通过该函数可以在其它服务器上正确复制多行插入。
对于MyISAM和BDB表,你可以在第二栏指定AUTO_INCREMENT以及多列索引。此时,AUTO_INCREMENT列生成的值的计算方法为:MAX(auto_increment_column)+ 1 WHERE prefix=given-prefix。如果想要将数据放入到排序的组中可以使用该方法。
CREATE TABLE animals (
grpENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULLAUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
);
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
SELECT * FROM animals ORDER BY grp,id;
将返回:
+--------+----+---------+
| grp | id |name |
+--------+----+---------+
| fish | 1 |lax |
| mammal | 1 |dog |
| mammal | 2 |cat |
| mammal | 3 | whale |
| bird | 1 | penguin |
| bird | 2 | ostrich |
+--------+----+---------+
请注意在这种情况下(AUTO_INCREMENT列是多列索引的一部分),如果你在任何组中删除有最大AUTO_INCREMENT值的行,将会重新用到AUTO_INCREMENT值。对于MyISAM表也如此,对于该表一般不重复使用AUTO_INCREMENT值。
如果AUTO_INCREMENT列是多索引的一部分,MySQL将使用该索引生成以AUTO_INCREMENT列开始的序列值。。例如,如果animals表含有索引PRIMARYKEY (grp, id)和INDEX(id),MySQL生成序列值时将忽略PRIMARYKEY。结果是,该表包含一个单个的序列,而不是符合grp值的序列。
要想以AUTO_INCREMENT值开始而不是1,你可以通过CREATETABLE或ALTERTABLE来设置该值,如下所示:
mysql> ALTER TABLE tbl AUTO_INCREMENT =100;
关于AUTO_INCREMENT的详细信息:
· 如何为列指定AUTO_INCREMENT属性:13.1.5节,“CREATE TABLE语法”和 13.1.2节,“ALTER TABLE语法”。
· AUTO_INCREMENT的动作取决于SQL模式:5.3.2节,“SQL服务器模式”。
· 找出含有最新AUTO_INCREMENT值的行:12.1.3节,“比较函数和操作符”。
· 设置将用到的AUTO_INCREMENT值: 13.5.3节,“SET语法” 。
· AUTO_INCREMENT和复制:6.7节,“复制特性和已知问题”.
· AUTO_INCREMENT相关的可用于复制的Server-system变量(auto_increment_increment和auto_increment_offset):5.3.3节,“服务器系统变量”。