注册 登录
编程论坛 MySQL论坛

[翻译]MySQL5.0用户手册——第三章:快速入门

京山游侠 发布于 2006-02-23 23:13, 20543 次点击
前一段时间翻译了第一章的部分内容,可是人气不旺,可能是因为第一章讲概论性的东西提不起大家的兴趣吧。
第二章是讲怎样安装MySQL的,kai已经开始翻译这章,我就不重复劳动了。
所以我决定翻译第三章,来点实质性的东西。希望大家多多捧场。


[此贴子已经被作者于2006-2-23 23:33:39编辑过]

58 回复
#2
京山游侠2006-02-24 00:18

本章提供了一个教程,该教程演示了如何通过mysql 客户端程序来创建和使用一个简单的数据库。mysql (有时候称作 “终端监视器” 或简称为 “监视器”) 是一个允许你连接到一个MySQL Server、运行查询和查看看结果的交互程序。mysql 也可以以批处理方式运行: 你可以把查询语句预先写到一个文件中,然后让mysql 执行该文件的内容。 使用mysql 的两种方法这里都会讲到。

--help 选项启动mysql,可以查看它提供了哪些选项:

shell> mysql --help

本章假设mysql 已经安装在你的机器上并且有一个可用的MySQL服务器能够连接, 否则请联系你的 MySQL 管理员。 (如果你就是管理员,请阅读本手册的相关章节,如Chapter 5, Database Administration。)

这一章描述了建立和使用一个数据库的全过程, 如果你仅仅对存取一个已经存在数据库感兴趣, 可以跳过介绍创建数据库和创建表的章节。

因为本章本质上是一个教程,所以许多细节已被省略。如果需要获得更多信息,请查看本手册的相关章节。

3.1. 连接服务器和断开连接

在使用mysql连接服务器的时候,你通常需要提供一个 MySQL 用户名,很有可能还需要一个密码;如果服务器运行在远程主机上,你还需要指定主机名。联系你的管理员以找出你应该使用什么样的连接参数 ( 即,那个主机,用户名和密码 )。一旦你知道正确的参数,你应该这样连接:

shell> mysql -h host -u user -p
Enter password: ********

hostuser 代表MySQL 服务器运行的主机名 和你的 MySQL 帐户的用户名,请替换为适当的值。 ******** 代表你的密码;当 mysql 显示 Enter password: 提示时输入它。

如果连接成功,你将看到mysql> 提示符后面显示一些介绍信息:

shell> mysql -h host -u user -p
Enter password: ********
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 25338 to server version: 5.0.19-standard

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql>

mysql> 提示符说明mysql 已经准备好,你可以输入命令。

如果你是在运行 MySQL服务器的机器上登陆, 你可以省略主机名,如下:

shell< mysql -u user -p

如果你试图连接的时候得到如下错误信息ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2),这说明MySQL服务器守护进程(Unix) 或服务 (Windows) 没有运行,请 联系管理员或者查看Chapter 2, Installing MySQL 来解决问题。

获得登录时遇到的常见问题的帮助, 请查看 Section A.2, “Common Errors When Using MySQL Programs”.

有些MySQL安装后可以允许用户在本机上匿名登录, 如果你的机器时这种情况,你可以不带任何参数运行mysql来连接服务器:

shell> mysql

连接成功后,可以输入QUIT (或者\q) 命令断开与服务器的连接:

mysql> QUIT
Bye

在Unix系统, 也可以通过按下 Control-D来断开连接。

下面章节的大部分例子都假设你已经连接到服务器,mysql> 提示符指明这一点。

#3
京山游侠2006-02-25 16:51

3.2. 输入查询语句

确信你已经按照前面的方法连接到服务器。 连接服务器的时候没有选择要操作哪一个数据库,从这点上讲,学会如何发出查询比直接跳到创建表、载入数据和读出数据要重要些。这一章描述了输入命令的基本原则,输入几个查询语句,自己体会一下mysql 如何工作。

下面是一个简单的命令,要求服务器告诉你它的版本号和当前日期。在 mysql> 提示后输入命令并按回车键:

mysql> SELECT VERSION(), CURRENT_DATE;
+----------------+--------------+
| VERSION()      | CURRENT_DATE |
+----------------+--------------+
| 5.0.7-beta-Max | 2005-07-11   |
+----------------+--------------+
1 row in set (0.01 sec)
mysql>

这个查询说明如下几点

  • 一个命令通常由一个SQL语句和一个分号组成。(一些特殊的情况下,分号也可以省略,比如QUIT就是其中一个,其它的情况以后还会遇到。)

  • 当你发出一个命令,mysql 把它发送到服务器执行并显示结果, 然后显示mysql> 提示符指明可以输入下一条命令。

  • mysql 以表格形式显示查询的结果, 第一行是每一列的标志,后面的行是查询结果。 通常,列标志是你从数据库中取出的数据的列名, 如果你查询的值是一个表达式的运算结果(如下面的例子),mysql 会使用这个表达式作为这一列的标志。

  • mysql 显示有多少行数据返回和执行这个查询花了多少时间,让你对服务器的性能有一个初步的印象。这些值是不精确的,因为它代表的是时钟时间 (而不是CPU 时间或机器时间),它会受到服务器负载量和网络延时的影响。(为了简洁起见,下面的例子中有时将不再显示 “rows in set”这一行。)

关键字不区分大小写,下面的语句是等价的:
mysql> SELECT VERSION(), CURRENT_DATE;
mysql> select version(), current_date;
mysql> SeLeCt vErSiOn(), current_DATE;

下面的查询语句说明可以把mysql 当成一个简单的计算器:

mysql> SELECT SIN(PI()/4), (4+1)*5;
+------------------+---------+
| SIN(PI()/4)      | (4+1)*5 |
+------------------+---------+
| 0.70710678118655 |      25 |
+------------------+---------+
1 row in set (0.02 sec)

到目前,我们演示的都是简单的,单行的语句,其实也可以在同一行中输入多条语句,每个语句以分号隔开:

mysql> SELECT VERSION(); SELECT NOW();
+----------------+
| VERSION()      |
+----------------+
| 5.0.7-beta-Max |
+----------------+
1 row in set (0.00 sec)

+---------------------+
| NOW()               |
+---------------------+
| 2005-07-11 17:59:36 |
+---------------------+  
1 row in set (0.00 sec)

一个命令也不是非要在一行里面输完,把一个语句分成多行输入是没有问题的,mysql 使用分号代表一个语句的结束,而不是把一行当成一个语句。 (换句话说,mysql 接受非常自由的输入, 它收集所有的输入知道看到分号的时候才执行。)

这是一个简单的多行语句:

mysql> SELECT
    -> USER()
    -> ,
    -> CURRENT_DATE;
+---------------+--------------+
| USER()        | CURRENT_DATE |
+---------------+--------------+
| jon@localhost | 2005-07-11   |
+---------------+--------------+

在这个例子中,你可以注意到输入一行未完成的命令后, mysql 的提示符从mysql>变成了->,指明命令没有输完。 提示符是我们的朋友,它提供非常有价值的反馈信息,通过这些信息,我们可以知道mysql 期待什么输入。

如果一个命令输入了一部分,你却不想执行了的话,可以键入\c取消:

mysql> SELECT
    -> USER()
    -> \c
mysql>

在这里,也请注意提示符的变化, 在输入 \c之后,提示符又变回mysql>, 提供反馈说明mysql 可以接受新的命令。

下面的表格总结了所有mysql 的提示符的意义:

提示符 意义
mysql> 准备接受新命令。
-> 等待一个多行命令的下一行。
'> 等待一个以单引号 (‘'’)开始的字符串的下一行。
"> 等待一个以单引号 (‘"')开始的字符串的下一行。
`> 等待一个以backtick (‘`’)开始的标识符的下一行。
/*> 等待一个以/*开始的注释的下一行。

在 MySQL 5.0 系列中, /*> 提示符从 MySQL 5.0.6版开始提供。

我们经常会意外的遇到多行命令的情况,那就是当我们输入一行语句后却忘记了输入分号。在这种情况下,mysql等待下一行的输入:

mysql> SELECT USER()
    ->

如果你遇到这种情况 (你认为你已经输完一个语句却只得到一个-> 提示符),极有可能就是mysql 正在等待分号。 如果你没有注意到提示符的变化,你可能就会傻坐着等。 只要输入一个分号结束语句,mysql 就会执行它:

mysql> SELECT USER()
    -> ;
+---------------+
| USER()        |
+---------------+
| jon@localhost |
+---------------+

'>"> 在输入字符串的时候有时会出现 (换一句话说 MySQL 正在等待未完成的字符串)。 在MySQL中,你可以使用 ‘'’ 或 ‘"’字符来输入字符串 (比如,'hello'"goodbye"),而且mysql 能够让你输入多行的字符串。 当你看到一个 '>"> 提示符时,意味着你已经输入了字符串开头的引号,还没有输入结尾的引号,如果你确实正在输入一个多行字符串,很好,但是果真如此吗?不尽然。更常见的, '> 和 "> 提示符显示你粗心地漏掉了一个引号字符。例如:

mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;
    '>

如果你输入该 SELECT 语句,然后按回车键并等待结果,什么都没有出现。“为什么 该查询这么长呢?”,注意 "> 提示符提供的线索。它告诉你 mysql 期望见到一个未终止字符串的 余下部分。(你在语句中看见错误吗?字符串 "Smith 正好缺少第二个引号。)

在这种情况下,该怎么办?最简单的方法就是取消命令的执行, 但是,却不能简单地只输入\c ,因为MySQL会认为这是字符串未完成的部分, 相反,我们应该先输入一个引号 (这样就可以告诉mysql 字符串我们已经输完了)然后再输入\c

mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;
    '> '\c
mysql>

提示符又变成了mysql>,说明mysql 可以接受下一条命令。

`> 提示符和'> 以及">提示符相似, 只不过指明的是你没有完成acktick-quoted 标识符的输入。

知道'>, ">,和`> 的标识符的意义是非常重要的, 为如果你错误地输入一个未终止的字符串,你下一步输入的任何行好象都将要被mysql忽略 — 包括包含QUIT命令的行。 如果你不知道在输入其它命令之前好要先中止引号的话,这将搞得你一头雾水。

#4
京山游侠2006-02-25 23:59

3.3. 创建和使用数据库

现在已经知道了怎样输入命令,是操作数据库的时候了。

假设你家(动物园)里有许多宠物,并且你希望能够跟踪它们的信息,你可以创建一个表,把你需要的信息输入表中,通过从表中提取数据,你就可以回答关于你的宠物的各种各样的问题。这一节将演示怎样做到下面的这些事情:

  • 创建一个数据库

  • 创建一个表

  • 向表中输入数据

  • 用不同的方法从表中获得数据

  • 使用多个表

这里的动物园数据库是简单的 (故意的),但是不难想象在现实生活中有许多情况需要使用类似的数据库,比如,一个农场主可以使用一个类似的数据库来管理他的家畜, 或者一个兽医用来管理病畜记录。 这里使用到的动物园数据库中,包含其中的数据和查询语句,都可以在 MySQL 的网站上找到,同时提供 tar 压缩(http://www.mysql.com/Downloads/Contrib/Examples/menagerie.tar.gz) 和 Zip 压缩 (http://www.mysql.com/Downloads/Contrib/Examples/menagerie.zip) 两种格式的文件下载。

使用 SHOW 语句找出目前在服务器上存在哪些数据库:

mysql> SHOW DATABASES;
+----------+
| Database |
+----------+
| mysql    |
| test     |
| tmp      |
+----------+

这个数据库的列表在你的机器上可能是不同的,但基本都会包含mysqltest数据库。mysql 数据库是必须的,因为它保存了用户访问权限的数据, test 数据库经常作为一个工作区提供给用户试试身手。

注意,你有可能看不到所有的数据库,如果你没有SHOW DATABASES 权限。请查看Section 13.5.1.3, “GRANT Syntax”.

如果test 数据库存在,尝试访问它:

mysql> USE test
Database changed

这里请注意USE命令和QUIT命令一样,不需要分号结尾。 (你使用分号结尾也没有影响。) USE 语句还有一个特别的地方: 它只能作为单行输入。

你可以使用test 数据库 (只要你有访问它的权限) 来练习下面的例子,但是你在这个数据库中创建的任何东西都可以被其他访问这个数据库的人删掉,所以,你应该向你的 MySQL 管理员要求允许你使用一个自己的数据库,假设你想给你的数据库取名为menagerie, 管理员需要执行一个这样的命令:

mysql> GRANT ALL ON menagerie.* TO 'your_mysql_name'@'your_client_host';

这里your_mysql_name 是分配给你的 MySQL 用户名,your_client_host 是你连接服务器所使用的机器的机器名。

3.3.1. 创建和选择一个数据库

如果管理员在给你分配权限的时候创建了这个数据库,你就可以直接使用了, 否则,你需要自己创建它:

mysql> CREATE DATABASE menagerie;

在 Unix系统中,数据库名是区分大小写的 (不像 SQL 关键字),因此你必须总是以 menagerie 引用你的数据库,不是 Menagerie 、 MENAGERIE 或其他变种。 这条规则对表名也适用。 (在Windows中,这个限制不适用, 尽管你必须在同一个查询中使用同样的大小写来引用数据库和表。 由于这些原因,我们建议的最佳做法就是引用数据库和表的时候使用和创建它们时同样的大小写。)

Note: 如果你得到ERROR 1044 (42000): Access denied for user 'monty'@'localhost' to database 'menagerie' when attempting to create a database的错误信息, 说明你的用户帐户不具备进行这个操作的权限。告诉你的管理员或者查看Section 5.8, “The MySQL Access Privilege System”.

创建数据库后不会自动把它当成当前数据库使用, 你必须明确的选择它。 要使menagerie 成为当前数据库, 使用如下命令:

mysql> USE menagerie;
Database changed

你的数据库只需要创建一次, 但是每次启动mysql 会话的时候都需要选择它。你可以使用上面介绍的 USE 语句来选择数据库, 也可以在运行 mysql的时候通过命令行参数选择数据库,只需要在所有命令行参数之后指定数据库名就可以:

shell> mysql -h host -u user -p menagerie
Enter password: ********

请注意,如上命令行中的menagerie 可不是你的密码哟,如果你想在命令行中的-p 选项中提供密码,你必须不加任何空格, (比如,是-pmypassword,而不是-p mypassword)。然而,不建议把密码放在命令行中,因为会直接把明文暴露给使用你机器的其它用户。

#5
京山游侠2006-02-26 23:44

3.3.2. 创建一个表

创建数据库很容易是吧,但它现在还是空的,可以用SHOW TABLES 查看:

mysql> SHOW TABLES;
Empty set (0.00 sec)

较难的部分是设计数据库的结构:数据库中包含哪些表和每个表中包含哪些列。

我们现在需要的是一个保存宠物记录的表, 可以给这个表取名为pet , 而且这个表最少也要包含每个动物的名字,但是仅仅只包含名字却没有什么意思,我们还要一些其它的信息。 比如,如果你家有多个人养宠物的话,你就可以给每个动物指定一个主任,还可以记录一些象种类和性别这样的信息。

年龄呢? 你肯定很关心这个信息,但是把它储存到数据库中却并不是一个好主意,因为年龄是随着时间变化的,你需要不断的更新你的数据库。一个更好的办法就是储存动物的生日,当你需要年龄的时候,你可以根据当前时间和生日进行计算, MySQL 提供了进行时间计算的函数,所以要做到这一点并不难。 存储生日而不是年龄还有以下几个好处:

  • 你可以让数据库在宠物的生日即将来临的时候提醒你, (不要认为这个思路很无聊,在商业应用中,有很多时候需要在每个星期或每个月给要过生日的客户发出生日祝福,它们本质上是一样的。)

  • 你可以根据相对日期而不是当前日期来计算年龄, 比如,你在数据库中储存了动物的死亡日期, 你就可以非常容易的计算出动物是在多大的时候死的。

你还可以给 pet 表添加其他有用的信息,但是到目前为止这些已经足够了:名字、主人、种类,性别、出生和死亡日期。

使用CREATE TABLE 语句来指定你的表的结构:

mysql> CREATE TABLE pet (name VARCHAR(20), owner VARCHAR(20),
    -> species VARCHAR(20), sex CHAR(1), birth DATE, death DATE);

VARCHAR 类型用于储存name, owner, 和 species 列是比较合适的, 因为这些列中的数据是变长的。 这个定义不要求你这一列的数据长度豆一样,也不需要都是20。你可以选择165535之间的任何长度,只要你认为这个长度合适。 (Note: 在 MySQL 5.0.3以前,最大限制是 255。)如果你刚开始的选择不合理,以后需要较长的字段, 可以用MySQL 提供的ALTER TABLE 语句修改。

动物的性别可以用多种方法表示, 比如'm''f'或者'male' and 'female'。使用单个字符'm''f'是最简单的。

使用DATE 数据类型来保存birthdeath 列的数据是比较合适的。

创建表以后,在使用SHOW TABLES 将产生如下输出:

mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| pet                 |
+---------------------+

验证创建的表是否符合我们的设计,使用 DESCRIBE 语句:

mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name    | varchar(20) | YES  |     | NULL    |       |
| owner   | varchar(20) | YES  |     | NULL    |       |
| species | varchar(20) | YES  |     | NULL    |       |
| sex     | char(1)     | YES  |     | NULL    |       |
| birth   | date        | YES  |     | NULL    |       |
| death   | date        | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

我们可以在任何时候使用DESCRIBE , 比如,当我们忘记某以列的名字或者它的数据类型时。

要获得更多关于 MySQL 数据类型的信息,请查看Chapter 11, Data Types.

3.3.3. 向表中载入数据

创建表之后,就需要向表中填入数据,用 LOAD DATAINSERT 语句可以做到。

假设你的宠物信息如下表所示。 (请注意观察,MySQL 的日期数据应该用 'YYYY-MM-DD' 的格式输入, 有些人的习惯可能跟这不同。)

name owner species sex birth death
Fluffy Harold cat f 1993-02-04
Claws Gwen cat m 1994-03-17
Buffy Harold dog f 1989-05-13
Fang Benny dog m 1990-08-27
Bowser Diane dog m 1979-08-31 1995-07-29
Chirpy Gwen bird f 1998-09-11
Whistler Gwen bird 1997-12-09
Slim Benny snake m 1996-04-29

因为现在数据表表是空的,所以向表中载入数据的简单做法就是把这些数据保存到文本文件中,然后使用一个语句把这个文本文件的内容加载到数据表中。

我们可以创建一个pet.txt 文本文件,它的每一行包含一条记录,每个值按照CREATE TABLE 语句中给出的顺序排列,并且用跳格符隔开,对于遗失的数据 ( 例如未知的性别,或仍然活着的动物的死亡日期 ),你可以使用NULL 值,在文本文件中,使用\N (backslash, capital-N)表示。 例如, Whistler 鸟的数据看起来象这样 (这些值之间的空白是单个的跳格符):

name owner species sex birth death
Whistler Gwen bird \N 1997-12-09 \N

把文本文件pet.txt into 载入到pet 表,使用如下命令:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;

请注意,如果你是在Winows系统上创建的文本文件,并且文字编辑器使用\r\n 作为一行的结束,你应该使用如下语句:

mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
    -> LINES TERMINATED BY '\r\n';

(在运行 OS X操作系统的苹果机上,你很有可能需要使用LINES TERMINATED BY '\r'。)

你可以在LOAD DATA语句中明确指出每列数据和每行数据之间的分隔符, 默认是跳格符和换行符。

如果该语句运行失败,极有可能是你的 MySQL 默认的配置为不接受从本地文件载入数据。 请查看Section 5.7.4, “Security Issues with LOAD DATA LOCAL, 获得更改该默认配置的信息。

如果希望一次添加一条记录,可以INSERT语句,该语句最简单的用法中, 你只需要按照CREATE TABLE 语句中定义列的顺序为每一列指定数据即可。 假设 Diane新养了一只名叫 “Puffball.”的仓鼠, 你可以使用 INSERT 语句向表中添加一条记录:

mysql> INSERT INTO pet
    -> VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);

注意,这里的字符串和日期都是以用引号括起来的字符串的形式输入的, 而且在INSERT语句中,你可以直接插入NULL表示遗失的数据,而不是象LOAD DATA语句中使用 \N

从这个例子中可以看出, 一开始就使用多个INSERT 语句载入数据,比使用单个LOAD DATA语句载入数据,敲起键盘来要麻烦的多。

#6
hxboss2006-02-27 05:18

是不是关闭回复了

都没人顶帖

我觉得 楼主 真的是很辛苦啊

如果我英文够好的话

我也希望来帮你

#7
京山游侠2006-02-27 16:28
谢谢楼上的支持。

我相信只要我们坚持下去,来看的人是会越来越多的。
#8
京山游侠2006-02-27 23:58

3.3.4. 从表中提取数据

SELECT 语句用来从一个数据表中获取信息,该语句的一般格式为:

SELECT what_to_select
FROM which_table
WHERE conditions_to_satisfy;

what_to_select 指定你想看到哪些列, *代表所有列。 which_table 指定你想从哪个表中获取数据。WHERE 是可选的,如果存在该字句,则conditions_to_satisfy 用来指定行必须满足的条件。

3.3.4.1. 选择所有数据

SELECT 语句最简单的用法是提取所有的数据:

mysql> SELECT * FROM pet;
+----------+--------+---------+------+------------+------------+
| name     | owner  | species | sex  | birth      | death      |
+----------+--------+---------+------+------------+------------+
| Fluffy   | Harold | cat     | f    | 1993-02-04 | NULL       |
| Claws    | Gwen   | cat     | m    | 1994-03-17 | NULL       |
| Buffy    | Harold | dog     | f    | 1989-05-13 | NULL       |
| Fang     | Benny  | dog     | m    | 1990-08-27 | NULL       |
| Bowser   | Diane  | dog     | m    | 1979-08-31 | 1995-07-29 |
| Chirpy   | Gwen   | bird    | f    | 1998-09-11 | NULL       |
| Whistler | Gwen   | bird    | NULL | 1997-12-09 | NULL       |
| Slim     | Benny  | snake   | m    | 1996-04-29 | NULL       |
| Puffball | Diane  | hamster | f    | 1999-03-30 | NULL       |
+----------+--------+---------+------+------------+------------+

当你想浏览整个表的时候, SELECT的这种格式很有用, 例如在我们载入数据之后,我们就可以这样浏览一下意外的发现 Bowser 的生日有点不对, 查看家谱后发现,它的出生日期应该是1989,而不是 1979。

有两个办法可以更正这个问题:

  • 编辑pet.txt 文件修改错误,然后使用DELETE语句清空数据表,重新使用LOAD DATA载入数据::

    mysql> DELETE FROM pet;
    mysql> LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
    

    但是,使用这种方法,你就必须重新输入 Puffball的数据。

  • 使用UPDATE 语句只更正错误记录:

    mysql> UPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
    

    UPDATE 可以只更改特定的记录而不需要重新载入整个数据表。

3.3.4.2. 选择特定的行

如前所述,提取整个表中的数据是简单的,只需要忽略SELECT 语句的WHERE子句就可以了。但是最常见的情况是你并不想看到整个表的数据,特别是当表非常大的时候,相反,你只对解决某些特定的问题感兴趣,这时可以给你需要的信息加上限定条件,下面让我们来看一下关于pets的一些特定查询的例子。

例如,如果你想验证对 Bowser 的出生日期所作的修改,可以象这样选择Bowser的记录:

mysql> SELECT * FROM pet WHERE name = 'Bowser';
+--------+-------+---------+------+------------+------------+
| name   | owner | species | sex  | birth      | death      |
+--------+-------+---------+------+------------+------------+
| Bowser | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+-------+---------+------+------------+------------+

输出的结果确认出生的年份已经正确的记录为 1989,而不是 1979。

字符串比较通常是不区分大小写的, 所以你可以指定name为'bowser''BOWSER', 或其它的大小写组合, 查询的结果是一样的。

你可以为任何一列限定条件,而不仅仅只是name列。例如,如果你想知道哪个动物是在1998年或以后出生的,可以 给 birth 列添加测试条件:

mysql> SELECT * FROM pet WHERE birth >= '1998-1-1';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Puffball | Diane | hamster | f    | 1999-03-30 | NULL  |
+----------+-------+---------+------+------------+-------+

你还可以几个条件联合起来查询,例如,查找所有的母性小狗:

mysql> SELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

上面的查询使用了AND 逻辑操作符, 下面是一个使用OR 操作符的例子:

mysql> SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
+----------+-------+---------+------+------------+-------+
| name     | owner | species | sex  | birth      | death |
+----------+-------+---------+------+------------+-------+
| Chirpy   | Gwen  | bird    | f    | 1998-09-11 | NULL  |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL  |
| Slim     | Benny | snake   | m    | 1996-04-29 | NULL  |
+----------+-------+---------+------+------------+-------+

ANDOR 操作符可以同时使用, AND 操作符的优先级高于OR操作符, 但是最好还是使用圆括号明确的指出查询条件怎样分组:

mysql> SELECT * FROM pet WHERE (species = 'cat' AND sex = 'm')
    -> OR (species = 'dog' AND sex = 'f');
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

3.3.4.3. 选择特定的列

如果你不想看到一整行中所有的列, 你可以在查询语句中指定你感兴趣的列,列名用逗号隔开。 例如, 如果你想知道你的动物分别是什么时候出生的, 则只选择namebirth 列:

mysql> SELECT name, birth FROM pet;
+----------+------------+
| name     | birth      |
+----------+------------+
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Buffy    | 1989-05-13 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Puffball | 1999-03-30 |
+----------+------------+

要找出这些宠物的主人,使用下面的查询:

mysql> SELECT owner FROM pet;
+--------+
| owner  |
+--------+
| Harold |
| Gwen   |
| Harold |
| Benny  |
| Diane  |
| Gwen   |
| Gwen   |
| Benny  |
| Diane  |
+--------+

请注意,这个查询只是简单的从每条记录中提取owner列的数据,而不管他们有很多重复。 要让结果更简洁,可以添加 DISTINCT关键字消除重复的记录:

mysql> SELECT DISTINCT owner FROM pet;
+--------+
| owner  |
+--------+
| Benny  |
| Diane  |
| Gwen   |
| Harold |
+--------+

可以使用WHERE 把选择特定的行和选择特定的列相结合。例如, 选择猫和狗的生日,可以使用如下查询:

mysql> SELECT name, species, birth FROM pet
    -> WHERE species = 'dog' OR species = 'cat';
+--------+---------+------------+
| name   | species | birth      |
+--------+---------+------------+
| Fluffy | cat     | 1993-02-04 |
| Claws  | cat     | 1994-03-17 |
| Buffy  | dog     | 1989-05-13 |
| Fang   | dog     | 1990-08-27 |
| Bowser | dog     | 1989-08-31 |
+--------+---------+------------+

3.3.4.4. 排序

你可能已经注意到了前面的例子中,输出的结果没有按照特定的顺序排列, 然而,当每一行都以有意义的方式排序时,我们检验输出结果往往更加方便。要把结果排序,使用ORDER BY 子句。

这里是动物的生日,按日期排列:

mysql> SELECT name, birth FROM pet ORDER BY birth;
+----------+------------+
| name     | birth      |
+----------+------------+
| Buffy    | 1989-05-13 |
| Bowser   | 1989-08-31 |
| Fang     | 1990-08-27 |
| Fluffy   | 1993-02-04 |
| Claws    | 1994-03-17 |
| Slim     | 1996-04-29 |
| Whistler | 1997-12-09 |
| Chirpy   | 1998-09-11 |
| Puffball | 1999-03-30 |
+----------+------------+

对于字符类型的列, 排序时通常不区分大小写,也就是说,如果某一列中包含有意义相同但大小写不一致的数据,那它们的排序的结果是不确定的。 你可以在列名前添加BINARY 关键字来指定区分大小写的排序:

ORDER BY BINARY col_name.

默认的排序方式是递增的,把最小的值放在最前面。要按照降序排列, 把 DESC关键字添加到你要排序的列名之后:

mysql> SELECT name, birth FROM pet ORDER BY birth DESC;
+----------+------------+
| name     | birth      |
+----------+------------+
| Puffball | 1999-03-30 |
| Chirpy   | 1998-09-11 |
| Whistler | 1997-12-09 |
| Slim     | 1996-04-29 |
| Claws    | 1994-03-17 |
| Fluffy   | 1993-02-04 |
| Fang     | 1990-08-27 |
| Bowser   | 1989-08-31 |
| Buffy    | 1989-05-13 |
+----------+------------+

你可以在多个列上进行排序,也可以在不同的列上使用不同的排序方式。 例如,把动物类别按增序排列,把动物的出生日期按照降序排列,可以使用如下查询:

mysql> SELECT name, species, birth FROM pet
    -> ORDER BY species, birth DESC;
+----------+---------+------------+
| name     | species | birth      |
+----------+---------+------------+
| Chirpy   | bird    | 1998-09-11 |
| Whistler | bird    | 1997-12-09 |
| Claws    | cat     | 1994-03-17 |
| Fluffy   | cat     | 1993-02-04 |
| Fang     | dog     | 1990-08-27 |
| Bowser   | dog     | 1989-08-31 |
| Buffy    | dog     | 1989-05-13 |
| Puffball | hamster | 1999-03-30 |
| Slim     | snake   | 1996-04-29 |
+----------+---------+------------+

请注意,DESC关键字只对紧挨在它前面的列名 (birth)起作用, 对 species 列的排序方式没有影响。

#9
京山游侠2006-02-27 23:58

3.3.4.5. 日期计算

MySQL 提供了一系列的函数允许我们对日期进行计算,例如,计算年龄或提取日期的部分信息。

要计算你的宠物有几岁了,可以计算当前时间和宠物的出生日期的年的差值, 如果当前日期(指月和日)比动物出生的日期要早,就再减去1。 下面的查询显示每一个动物的生日、 当前时间和年龄 。

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
+----------+------------+------------+------+

这里, YEAR() 可以提取日期中年的部分 , RIGHT() 提取代表MM-DD (calendar year) 部分的最右边的五个字符, 这个表达式的剩下部分就是比较MM-DD 的值,然后把结果转化成 1 或 0,用来调整CURDATE()birth中的日期的差值。 这个表达式比较臃肿, 所以给它取一个别名一个别名 (age) ,以便输出的结果的列标签更加有意义。

为了让查询执行后的结果更加便于检查,可以按照一定的方法进行排序,只要添加ORDER BY name 子句就可以按照姓名排序:

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet ORDER BY name;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
+----------+------------+------------+------+

如果要按照age 而不是 name排序, 只需调整 ORDER BY 子句:

mysql> SELECT name, birth, CURDATE(),
    -> (YEAR(CURDATE())-YEAR(birth))
    -> - (RIGHT(CURDATE(),5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet ORDER BY age;
+----------+------------+------------+------+
| name     | birth      | CURDATE()  | age  |
+----------+------------+------------+------+
| Chirpy   | 1998-09-11 | 2003-08-19 |    4 |
| Puffball | 1999-03-30 | 2003-08-19 |    4 |
| Whistler | 1997-12-09 | 2003-08-19 |    5 |
| Slim     | 1996-04-29 | 2003-08-19 |    7 |
| Claws    | 1994-03-17 | 2003-08-19 |    9 |
| Fluffy   | 1993-02-04 | 2003-08-19 |   10 |
| Fang     | 1990-08-27 | 2003-08-19 |   12 |
| Bowser   | 1989-08-31 | 2003-08-19 |   13 |
| Buffy    | 1989-05-13 | 2003-08-19 |   14 |
+----------+------------+------------+------+

相同的查询也可以用来计算一个动物是几岁时死亡的, 你可以通过检查death列的值是否为NULL来决定这个动物是否死亡, 然后,对于所有不为NULL的值,计算deathbirth 的差值:

mysql> SELECT name, birth, death,
    -> (YEAR(death)-YEAR(birth)) - (RIGHT(death,5)<RIGHT(birth,5))
    -> AS age
    -> FROM pet WHERE death IS NOT NULL ORDER BY age;
+--------+------------+------------+------+
| name   | birth      | death      | age  |
+--------+------------+------------+------+
| Bowser | 1989-08-31 | 1995-07-29 |    5 |
+--------+------------+------------+------+

这个语句中使用death IS NOT NULL而不是death <> NULL 是因为 NULL 是一个特殊值,它不能用常规的操作符进行比较, 这点稍后即将讨论。 请查看 Section 3.3.4.6, “Working with NULL Values”.

你想知道哪些动物下个月过生日吗? 这种计算,年和天是不用考虑的,你只需要简单的提取birth列中的月份部分。MySQL提供了一些提取日期的部分信息的函数, 比如YEAR()MONTH()、 和 DAYOFMONTH()MONTH() 函数用在这里正合适。 看看它是怎样的效果如何, 运行一个简单的查询来显示birthMONTH(birth)的值:

mysql> SELECT name, birth, MONTH(birth) FROM pet;
+----------+------------+--------------+
| name     | birth      | MONTH(birth) |
+----------+------------+--------------+
| Fluffy   | 1993-02-04 |            2 |
| Claws    | 1994-03-17 |            3 |
| Buffy    | 1989-05-13 |            5 |
| Fang     | 1990-08-27 |            8 |
| Bowser   | 1989-08-31 |            8 |
| Chirpy   | 1998-09-11 |            9 |
| Whistler | 1997-12-09 |           12 |
| Slim     | 1996-04-29 |            4 |
| Puffball | 1999-03-30 |            3 |
+----------+------------+--------------+

找出哪个动物下个月过生日也是很简单的, 假设现在是四月, 你可以这样找出出生月份为五月的动物:

mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name  | birth      |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+

如果当前月份是十二月的话,你就会遇到一个小问题,你不能简单的把月份加一然后查找在十三月份出生的动物,因为不存在十三月份,相反,你应该查找在一月份出生的动物。

你可以写一个不管当前月份是什么都可以使用的查询语句,也不需要明确的指定月份值。DATE_ADD() 允许你为一个日期加上一个时间段, 如果你向CURDATE()加上一个月, 然后用MONTH()提取月份值, 结果就是下个月的月份,刚好可以用来查找哪个动物下个月过生日。

mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

另外一种方法就是使用取模函数 (MOD) ,当当前月份是12的时候就把它转化为0,然后再加1:

mysql> SELECT name, birth FROM pet
    -> WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;

请注意MONTH 函数返回112的值,而MOD(something,12) 返回011的值, 所以必须先MOD()再加1, 否则我们得到的结果就是从 November (11) 到 January (1)。

#10
京山游侠2006-03-01 00:10

3.3.4.6. 正确处理NULL

在你习惯使用 NULL 之前,肯定会对它的奇怪表现感到惊奇, 从概念上讲,NULL 意味着一个“遗失的值”或“未知的值”,而且它的处理方式和其它的值有点不同。要测试一个NULL值, 你不能使用象=, <, 或 <>这样的算术比较运算符, 不信?自己试试下面的查询语句:

mysql> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
+----------+-----------+----------+----------+
| 1 = NULL | 1 <> NULL | 1 < NULL | 1 > NULL |
+----------+-----------+----------+----------+
|     NULL |      NULL |     NULL |     NULL |
+----------+-----------+----------+----------+

很显然,从这些比较中你得不到有意义的结果,应该使用IS NULLIS NOT NULL 操作符代替:

mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
|         0 |             1 |
+-----------+---------------+

请注意,在 MySQL中,0NULL 意味着 false ,其它的值意味着 true。在一个布尔操作中默认的 truth 值是 1

这种对NULL值的特殊对待,就是为什么在前面的例子中 测试一个动物是否依然或者必须用death IS NOT NULL 而不是 death <> NULL语句的原因了。

所有的NULL值在GROUP BY语句中都认为是相等的。

当使用ORDER BY时,如果是ORDER BY ... ASCNULL 被放在最前面,如果是ORDER BY ... DESC就会被放在最后面。

使用NULL 的一个常见错误就是认为不能把0和空字符串存入定义为 NOT NULL的列中, 事实上不是这样, 0和空字符串都有实际的值,而NULL 意味着 “没有值”。 不信,你可以使用IS [NOT] NULL 测试:

mysql> SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
+-----------+---------------+------------+----------------+
| 0 IS NULL | 0 IS NOT NULL | '' IS NULL | '' IS NOT NULL |
+-----------+---------------+------------+----------------+
|         0 |             1 |          0 |              1 |
+-----------+---------------+------------+----------------+

因此,完全可以插入一个0或空字符串到一个定义为NOT NULL 的列中,这些值本来就不是空值。请查看Section A.5.3, “Problems with NULL Values”.

#11
京山游侠2006-03-01 00:48

3.3.4.7. 模式匹配

MySQL 提供标准的 SQL 模式匹配语法,同时也提供一种扩展的模式匹配语法,这种语法类似于在 Unix 中的 vi, grep, 和 sed工具中使用的正则表达式语法。

SQL 模式匹配允许你使用 ‘_’ 来匹配一个单个的字符和 ‘%’ 来匹配多个字符(包括0个字符)。在MySQL中,SQL 模式默认是不区分大小写的,下面是一些实例。请注意,你不能使用=<> 来进行 SQL 模式的比较,应该使用LIKENOT LIKE 比较符代替。

查找以 ‘b’开头的名字:

mysql> SELECT * FROM pet WHERE name LIKE 'b%';
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

查找以 ‘fy’结尾的名字:

mysql> SELECT * FROM pet WHERE name LIKE '%fy';
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

查找包含 ‘w’的名字:

mysql> SELECT * FROM pet WHERE name LIKE '%w%';
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

查找刚好是5个字符的名字, 可以使用五个 ‘_’ 模式字符:

mysql> SELECT * FROM pet WHERE name LIKE '_____';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

MySQL提供的另外一种模式匹配 使用扩展的正则表达式, 当测试是否和这种模式相匹配时, 使用REGEXPNOT REGEXP 操作符 (或者RLIKENOT RLIKE)。

扩展正则表达式的一些通配符号如下:

  • .’ 匹配所有单个字符。

  • 一个字符集合 ‘[...]’ 匹配任何在这个集合中的字符。 例如, ‘[abc]’ 匹配 ‘a’, ‘b’, 或 ‘c’。 要指定一个范围的字符,可以使用一个破折号,如 ‘[a-z]’匹配所有字母, 而 ‘[0-9]’ 匹配所有数字。

  • *’ 匹配它前面的字符0到多遍, 例如,‘x*’匹配任意个 ‘x’ 字符, ‘[0-9]*’ 匹配任意位数的数字, ‘.*’ 匹配任意数量的任何字符。

  • 一个REGEXP 模式匹配操作只要字符串的匹配任何一处都算成功 。(这和LIKE 模式匹配不同,它只有当整个字符串都匹配的时候才算成功。)
  • 要指定一个模式必须和一个值的开头或结尾匹配,可在开头使用 ‘^’ 在结尾使用 ‘$’ 。

下面的例子把前面例子中语句重写,不使用LIKE而使用REGEXP,来展示正则表达式的功能:

查找以 ‘b’开头的名字, 使用 ‘^’ 来匹配名字的开头:

mysql> SELECT * FROM pet WHERE name REGEXP '^b';
+--------+--------+---------+------+------------+------------+
| name   | owner  | species | sex  | birth      | death      |
+--------+--------+---------+------+------------+------------+
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL       |
| Bowser | Diane  | dog     | m    | 1989-08-31 | 1995-07-29 |
+--------+--------+---------+------+------------+------------+

如果你确实想在进行REGEXP 比较时区分大小写, 可以使用BINARY 关键字让一个字符串成为二进制的字符串,这个查询配配以小写的 ‘b’ 开头的名字:

mysql> SELECT * FROM pet WHERE name REGEXP BINARY '^b';

查找以‘fy’结尾的名字,以 ‘$’ 来匹配名字的结尾:

mysql> SELECT * FROM pet WHERE name REGEXP 'fy$';
+--------+--------+---------+------+------------+-------+
| name   | owner  | species | sex  | birth      | death |
+--------+--------+---------+------+------------+-------+
| Fluffy | Harold | cat     | f    | 1993-02-04 | NULL  |
| Buffy  | Harold | dog     | f    | 1989-05-13 | NULL  |
+--------+--------+---------+------+------------+-------+

查找包含 ‘w’的名字,使用如下查询:

mysql> SELECT * FROM pet WHERE name REGEXP 'w';
+----------+-------+---------+------+------------+------------+
| name     | owner | species | sex  | birth      | death      |
+----------+-------+---------+------+------------+------------+
| Claws    | Gwen  | cat     | m    | 1994-03-17 | NULL       |
| Bowser   | Diane | dog     | m    | 1989-08-31 | 1995-07-29 |
| Whistler | Gwen  | bird    | NULL | 1997-12-09 | NULL       |
+----------+-------+---------+------+------------+------------+

上面使用这则表达式匹配的例子和前面使用SQL模式匹配的例子不一样,因为正则表达式只要是在字符串中的任何地方匹配都算是跟整个字符串匹配,所以上面的例子不需要在'w'字符的两边加上通配符。

查找刚好只有五个字符的名字,使用 ‘^’ 和 ‘$’ 来匹配名字的开头和结尾,使用五个 ‘.’ 来匹配中间的值:

mysql> SELECT * FROM pet WHERE name REGEXP '^.....$';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

你还可以使用{n} (“repeat-n-times”) 操作符来写上面的查询:

mysql> SELECT * FROM pet WHERE name REGEXP '^.{5}$';
+-------+--------+---------+------+------------+-------+
| name  | owner  | species | sex  | birth      | death |
+-------+--------+---------+------+------------+-------+
| Claws | Gwen   | cat     | m    | 1994-03-17 | NULL  |
| Buffy | Harold | dog     | f    | 1989-05-13 | NULL  |
+-------+--------+---------+------+------------+-------+

Appendix G, MySQL Regular Expressions,提供了关于正则表达式符号的更多信息。

#12
京山游侠2006-03-02 16:10

3.3.4.8. 行统计

数据库经常被用来回答这样的问题, “ 一个特定类型的数据在表中出现的频率如何?” 例如,你可能想知道你总共有多少只宠物, 或每一个主人拥有多少只宠物, 或者你想对你的宠物进行各种各样的统计。

你总共拥有多少只宠物等同于 “pet数据表里有多少行记录”, 因为每一只宠物的信息作为一行记录,COUNT(*) 统计行的总数,所以查找总共有多少宠物的语句如下:

mysql> SELECT COUNT(*) FROM pet;
+----------+
| COUNT(*) |
+----------+
|        9 |
+----------+

以前,我们找出过宠物的主人有哪些,现在,你可以使用COUNT() 来找出每一个主人分别拥有几只宠物:

mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
+--------+----------+
| owner  | COUNT(*) |
+--------+----------+
| Benny  |        2 |
| Diane  |        2 |
| Gwen   |        3 |
| Harold |        2 |
+--------+----------+

请注意GROUP BY 是用来把记录按照宠物的主人分组, 否则,你将得到一条错误信息:

mysql> SELECT owner, COUNT(*) FROM pet;
ERROR 1140 (42000): Mixing of GROUP columns (MIN(),MAX(),COUNT(),...) 
with no GROUP columns is illegal if there is no GROUP BY clause

COUNT()GROUP BY 可以让你以各种各样的方式展示数据表中的数据,下面的例子显示了对pet表的各种统计操作:

按类别统计动物的数量:

mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
+---------+----------+
| species | COUNT(*) |
+---------+----------+
| bird    |        2 |
| cat     |        2 |
| dog     |        3 |
| hamster |        1 |
| snake   |        1 |
+---------+----------+

按性别统计动物的数量:

mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+------+----------+
| sex  | COUNT(*) |
+------+----------+
| NULL |        1 |
| f    |        4 |
| m    |        4 |
+------+----------+

(在这里,NULL表示不清楚动物的性别。)

按类别和性别统计动物的数量:

mysql> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | NULL |        1 |
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

使用COUNT()时,你也不是一定要提取整个表的数据,例如,在前面的查询中,如果你只对猫和狗感兴趣,可以使用如下语句:

mysql> SELECT species, sex, COUNT(*) FROM pet
    -> WHERE species = 'dog' OR species = 'cat'
    -> GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
+---------+------+----------+

如果你只想知道已知性别的动物的统计资料,如下:

mysql> SELECT species, sex, COUNT(*) FROM pet
    -> WHERE sex IS NOT NULL
    -> GROUP BY species, sex;
+---------+------+----------+
| species | sex  | COUNT(*) |
+---------+------+----------+
| bird    | f    |        1 |
| cat     | f    |        1 |
| cat     | m    |        1 |
| dog     | f    |        1 |
| dog     | m    |        2 |
| hamster | f    |        1 |
| snake   | m    |        1 |
+---------+------+----------+

3.3.4.9. 使用多个表

pet表只是保存了你所拥有的宠物的基本信息,或许你还需要保存其它的信息,诸如象它们生活中发生的一些事件,比如什么时候看兽医了,什么时候生小动物了等等。这个表应该是个什么样子呢?它需要:

  • 需要包含宠物的名字,以便你能知道是发生在哪知宠物身上的事件。

  • 一个时间,以便你能知道事件是什么时候发生的。

  • 一个描述事件的字段。

  • 一个事件类型字段,以便你能将事件进行分类。

出于这些考虑,event表的CREATE TABLE 语句应该看起来象这样:

mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
    -> type VARCHAR(15), remark VARCHAR(255));

pet 表一样,可以创建一个以tab符分割的文本文件来载入初始化数据:

name date type remark
Fluffy 1995-05-15 litter 4 kittens, 3 female, 1 male
Buffy 1993-06-23 litter 5 puppies, 2 female, 3 male
Buffy 1994-06-19 litter 3 puppies, 3 female
Chirpy 1999-03-21 vet needed beak straightened
Slim 1997-08-03 vet broken rib
Bowser 1991-10-12 kennel
Fang 1991-10-12 kennel
Fang 1998-08-28 birthday Gave him a new chew toy
Claws 1998-03-17 birthday Gave him a new flea collar
Whistler 1998-12-09 birthday First birthday

象这样载入记录:

mysql> LOAD DATA LOCAL INFILE 'event.txt' INTO TABLE event;

以我们在pet 表上学到的查询基础,要在event表上执行查询是很简单的,因为它们的规则是一样的。 但是,如果event表自身的信息不足以回答你的问题,怎么办?

假设你想知道你的宠物是在多大年龄生小动物的,我们知道需要计算两个时间之间的差值,但是, 生小动物的时间在event 表种,但是要用来计算年龄的出生日期却在pet 表中,说明这个查询需要涉及到两个表:

mysql> SELECT pet.name,
    -> (YEAR(date)-YEAR(birth)) - (RIGHT(date,5)<RIGHT(birth,5)) AS age,
    -> remark
    -> FROM pet, event
    -> WHERE pet.name = event.name AND event.type = 'litter';
+--------+------+-----------------------------+
| name   | age  | remark                      |
+--------+------+-----------------------------+
| Fluffy |    2 | 4 kittens, 3 female, 1 male |
| Buffy  |    4 | 5 puppies, 2 female, 3 male |
| Buffy  |    5 | 3 puppies, 3 female         |
+--------+------+-----------------------------+

下面是这个查询中需要注意的事情:

  • FROM子句列出了两个表名,因为这个查询需要从两个表中提取数据。

  • 当关联 (joining) 多个表中的信息时, 你需要指定一个表中的信息是怎样和另外一个表中的信息进行匹配的,在这个例子中很简单,因为它们都有name字段, WHERE 子句使用name字段匹配两个表中的信息。

  • 由于name 列同时出现在两个表中, 所以你必须指定你引用的name列属于哪个表, 这是通过在列名前面指定表名来做到的。

你并不是必须在两个不同的表之间进行关联, 有些时候一个表也可以和它自己关联,如果你想在同一个表中把一条记录和另外一条记录进行比较,例如,要为两只动物配对,你可以让pet 表和自己关联来找出哪两只动物种类相同而性别不同:

mysql> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
    -> FROM pet AS p1, pet AS p2
    -> WHERE p1.species = p2.species AND p1.sex = 'f' AND p2.sex = 'm';
+--------+------+--------+------+---------+
| name   | sex  | name   | sex  | species |
+--------+------+--------+------+---------+
| Fluffy | f    | Claws  | m    | cat     |
| Buffy  | f    | Fang   | m    | dog     |
| Buffy  | f    | Bowser | m    | dog     |
+--------+------+--------+------+---------+

在这个查询中,我们为这个表的两次引用分别指定了别名,这样可以明确指出哪个列名属于哪个引用。

#13
京山游侠2006-03-02 20:43

3.4. 获得关于数据库和表格的信息

如果你忘记数据库或表的名字,或忘记一个给定表的结构 (例如,它的列叫什么名字),怎么办? MySQL 通过提供一些可以获取数据库和表的信息的语句来解决问题。

在前面我们看到过SHOW DATABASES语句, 该语句可以显示服务器上管理的所有数据库,如果要找出当前选择数据库,使用 DATABASE()函数:

mysql> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| menagerie  |
+------------+

如果你没有选择任何数据库,则其结果为 NULL

要找出当前数据库中包含哪些表 (例如,你对某个表的名字不确定),使用如下命令:

mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| event               |
| pet                 |
+---------------------+

如果你想知道某一个表的结构,可以使用DESCRIBE 命令,它显示表格中每一列的信息。

mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name    | varchar(20) | YES  |     | NULL    |       |
| owner   | varchar(20) | YES  |     | NULL    |       |
| species | varchar(20) | YES  |     | NULL    |       |
| sex     | char(1)     | YES  |     | NULL    |       |
| birth   | date        | YES  |     | NULL    |       |
| death   | date        | YES  |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+

Field 是列的名称,Type 是列的数据类型, NULL 指出该列能否包含NULL 值,Key 指出该列是否作为索引, Default 指出该列的默认值。

如果你的表格有索引,SHOW INDEX FROM tbl_name 可以得到这些索引的信息。

3.5. 在批处理模式下使用mysql

在前面的章节中, 你以输入命令和显示结果的交互模式使用 mysql,你也可以让mysql 按批处理模式工作。 要以批处理的方式运行,你必须把你要执行的命令放入一个文件,然后告诉mysql 从这个文件读取命令。

shell> mysql < batch-file

如果是在Windows下运行mysql ,并且你的文件中包含一些可能引发问题的特殊字符, 你也可以这样使用:

C:\> mysql -e "source batch-file"

如果你必须在命令行指定连接参数,命令看起来应该是这样:

shell> mysql -h host -u user -p < batch-file
Enter password: ********

如果你以这种方式使用mysql ,你还可以创建一个脚本文件,然后运行这个脚本。

如果你希望你的脚本在执行的时候即使有些命令产生错误也不中断整个脚本的执行, 应该使用--force 命令行选项。

为什么要使用脚本呢?可能有以下理由:

  • 如果你经常需要重复执行一些查询 (比如说,每天或每星期),把它写成脚本可以避免每次执行时都要输入大量命令。

  • 你可以通过复制粘贴脚本文件中的部分内容来加速新的脚本的创建。

  • 批处理模式对于不断改进你的查询语句也很有帮助,特别是对于多行的命令或者多个语句的命令序列,如果语句中出现错误,你不用全部重新输入,只需要对脚本中的错误进行修改,然后让mysql 再执行它。

  • 如果你的查询有大量的输出, 你可以使用分页的方式来查看它,这比滚动屏幕的方式要好得多。

    shell> mysql < batch-file | more
    
  • 你可以把输出结果重定向到一个文件,以便进一步处理。

    shell> mysql < batch-file > mysql.out
    
  • 你可以将脚本文件发送给别人,让别人也能执行这些命令。

  • 有些情况下不允许交互式操作,例如,以cron job(类似计划任务)的方式执行查询,这种情况下,你就必须使用批处理模式。

批处理模式下默认的输出格式和交互模式有些不同 (更加简洁) 。例如,SELECT DISTINCT species FROM pet 在交互模式下的输出看起来如下:

+---------+
| species |
+---------+
| bird    |
| cat     |
| dog     |
| hamster |
| snake   |
+---------+

而在批处理模式下,看起来却是这样:

species
bird
cat
dog
hamster
snake

如果你希望在批处理模式下得到和交互模式相同的输出格式,请使用mysql -t。 如果要回显命令本身,请使用mysql -vvv

你还可以在mysql 提示符下面使用source 或者\. 命令来执行脚本文件:

mysql> source filename;
mysql> \. filename
#14
京山游侠2006-03-03 23:57

3.6. 常用示例

这里是一些例子,可以演示怎样解决MySQL中的一些常见问题。

有些示例要使用shop 表,该表中保存有每一个商人的每种货物(使用编号保存)的价格,假设每一个商人的每一种物品具有单独的价格,则 (article, dealer) 是这个记录的主键。

启动命令行工具mysql 并且选择一个数据库:

shell> mysql your-database-name

(大多数情况下,你可以使用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,
    -> PRIMARY KEY(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 |
+---------+--------+-------+

3.6.1. 查询一列的最大值

“最大的商品编号是多少?”

SELECT MAX(article) AS article FROM shop;

+---------+
| article |
+---------+
|       4 |
+---------+

3.6.2. 查询一列的最大值所在的行的数据

任务:找出最贵的商品的编号、商人和价格。

利用一个嵌套查询,这很容易做到:

SELECT article, dealer, price
FROM   shop
WHERE  price=(SELECT MAX(price) FROM shop);

另一种做法是把表中的数据按 price进行降序排列, 然后使用 MySQL-特别提供的LIMIT 子句只提取第一行数据:

SELECT article, dealer, price
FROM shop
ORDER BY price DESC
LIMIT 1;

Note: 如果表中具有多个最贵的商品, 每一个的价格都是 19.95,那么使用LIMIT 的方法就只能显示其中的一条信息。

3.6.3. 查询一列中每一组的最大值

任务:查找每一种商品的最高价格。

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 |
+---------+-------+

3.6.4. 查询一列中每一组的最大值所在的行的数据

任务:对于每一种商品,查询最贵的那一个的编号、商人和价格信息。

这个问题可以用这样的嵌套查询解决:

SELECT article, dealer, price
FROM   shop s1
WHERE  price=(SELECT MAX(s2.price)
              FROM shop s2
              WHERE s1.article = s2.article);

3.6.5. 使用自定义变量

你可以使用 MySQL 的自定义变量来储存查询结果,而不是一定要使用客户端的临时变量来存储查询结果。(请查看Section 9.3, “User-Defined Variables”.)

例如,要查找最贵和最便宜的商品,如下:

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 |
+---------+--------+-------+
#15
xqiang2006-03-04 09:33
好,顶!
不好意思啊,现在才看到,呵呵!!!
#16
xqiang2006-03-04 09:41
对了,京山兄,除了这个第三章,还有没有其他的学习MYSQL的文章啊?呵呵!!辛苦啦!
#17
xqiang2006-03-04 10:22
大哥,3.8节怎么没有啊?
#18
京山游侠2006-03-04 13:04
呵呵,还没有翻译完呢。请耐心等待哦。

按这样的速度,我想要把这个参考手册完全翻译完,大概需要一年的时间吧。
#19
京山游侠2006-03-06 00:42

3.6.6. 使用外键

在MySQL中,InnoDB 表提供对外键约束的检查。请查看Section 14.2, “The InnoDB Storage Engine”, 和 Section 1.9.5.5, “Foreign Keys”.

如果只是连接两个表,外键不是必须的。对于InnoDB以外的其它数据库引擎,也可以使用REFERENCES tbl_name(col_name) 子句来定义一个列,但是不产生任何效果, 只是当成一个备注来提示你这个字段和另外一个表中的字段有联系, 在使用这个子句的时候,认识到以下几条是非常重要的:

  • MySQL 不进行任何类型的 CHECK 以确认col_name 列一定存在于tbl_name表中 (甚至是tbl_name 表自己是否存在)。

  • 当在一个外键所在的表中进行删除行之类的操作时,MySQL 不会在引用外键的表中进行任何动作, 换句话说,这个子句无论如何没有ON DELETEON UPDATE 动作的含义。 (即使你能在REFERENCES子句中写上ON DELETEON UPDATE 子句,它们也会被忽略。)

  • 该语句创建一个 ; 但是 创建任何类型的索引或键值。

  • 如果试图定义一个InnoDB 表,该语句将产生一个错误。

你可以创建一个和另外一个列有联系的列,如下:
CREATE TABLE person (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR(60) NOT NULL,
    PRIMARY KEY (id)
);

CREATE TABLE shirt (
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    style ENUM('t-shirt', 'polo', 'dress') NOT NULL,
    color ENUM('red', 'blue', 'orange', 'white', 'black') NOT NULL,
    owner SMALLINT UNSIGNED NOT NULL REFERENCES person(id),
    PRIMARY KEY (id)
);

INSERT INTO person VALUES (NULL, 'Antonio Paz');

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, 'Lilliana Angelovska');

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 | Antonio Paz         |
|  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
 WHERE p.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 NULL auto_increment,
`style` enum('t-shirt','polo','dress') NOT NULL,
`color` enum('red','blue','orange','white','black') NOT NULL,
`owner` smallint(5) unsigned NOT NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1

MyISAMBerkeleyDB 表中,这种方式使用的REFERENCES被当成一个注释或 “暗示” 。

3.6.7. 根据两个键查询

OR操作符对于使用一个键查询是最优化的, AND操作符也是如此。

一种特殊的情况是使用OR操作符来组合在两个键上的查询:

SELECT field1_index, field2_index FROM test_table
WHERE field1_index = '1' OR  field2_index = '1'

从MySQL 5.0.0版以后提供对这种情况的优化,请查看Section 7.2.6, “Index Merge Optimization”.

解决这个问题,还有一种有效率的办法,就是使用UNION 来联合两个 SELECT 语句的输出。请查看Section 13.2.7.2, “UNION Syntax”.

每一个SELECT 语句只从一个键查询,所有能够被优化:

SELECT field1_index, field2_index
    FROM test_table WHERE field1_index = '1'
UNION
SELECT field1_index, field2_index
    FROM test_table WHERE field2_index = '1';

3.6.8. 计算每天的访问量

下面的例子演示了怎样通过一组以bit开头的函数来计算一个月中用户有多少天访问了Web页:

CREATE TABLE t1 (year YEAR(4), month INT(2) UNSIGNED ZEROFILL,
             day INT(2) UNSIGNED ZEROFILL);
INSERT INTO t1 VALUES(2000,1,1),(2000,1,20),(2000,1,30),(2000,2,2),
            (2000,2,23),(2000,2,23);

示例表中包含了 year-month-day 数据以指示用户访问该Web页的时间, 要知道每个月有多少天有用户访问该Web页,使用如下语句:

SELECT year,month,BIT_COUNT(BIT_OR(1<<day)) AS days FROM t1
       GROUP BY year,month;

返回结果如下:

+------+-------+------+
| year | month | days |
+------+-------+------+
| 2000 |    01 |    3 |
| 2000 |    02 |    2 |
+------+-------+------+

该查询计算每一个 year/month 组合中有多少不同的day, 重复的项将自动忽略。

3.6.9. 使用自动增量

AUTO_INCREMENT 属性可以用来自动为新增加的行产生唯一的标识。

CREATE TABLE animals (
     id MEDIUMINT NOT NULL AUTO_INCREMENT,
     name CHAR(30) NOT NULL,
     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 值。这些函数是独立于连接的,所以它们的返回值不会收到其它连接上插入的数据的影响。

注意: 对于多行的 insert语句,LAST_INSERT_ID()mysql_insert_id() 实际上返回这一次插入的第一行的AUTO_INCREMENT 键的值, 这允许多行插入能够在其它的replication服务器上正确的重现。

对于MyISAMBDB 表, 你可以指定多列索引中的第二列为AUTO_INCREMENT ,在这种情况下,AUTO_INCREMENT 列的值是通过 MAX(auto_increment_column) + 1 WHERE prefix=given-prefix计算的,当你想把数据放入有序群组的时候这是非常有用的。

CREATE TABLE animals (
    grp ENUM('fish','mammal','bird') NOT NULL,
    id MEDIUMINT NOT NULL AUTO_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 将使用该列来产生自动增量。 例如,如果animals 表中包含PRIMARY KEY (grp, id)INDEX (id),MySQL 将会忽略PRIMARY KEY 以产生自动增量序列,结果就是这个表中将只包含一个序列, 而不是每一个 grp 值都有一个单独的序列。

如果要让AUTO_INCREMENT 值不是从 1开始, 你可以使用 CREATE TABLEALTER TABLE来指定该值,例如:

mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;

关于AUTO_INCREMENT 的更多信息:

#20
Knocker2006-03-06 13:15
#21
京山游侠2006-03-07 21:14

3.7. 在 Twin Project项目中查找数据

在 Analytikerna 和 Lentus,我们曾经为一个大的研究项目做过系统和现场工作。这个项目是 Karolinska Institutet Stockholm的环境医学协会 和南加里佛利亚大学的关于年龄和心理的临床研究所合作的。

这个项目先对在瑞典的所有大于65 岁的双胞胎做电话访问, 如果某对双胞胎符合某些条件,则进行下一个步骤,如果这对双胞胎愿意参与这个项目,则派出一个由医生和护士组成的小队对其进行拜访,进行一些身体或精神上的检查、实验室检查、脑电图和心理状态评估, 并且收集家族史。此外,还对医学和环境的危险度信息进行收集。

关于双胞胎研究项目的更多信息,请访问:http://www.mep.ki.se/twinreg/index_en.html

这个项目的后续部分就是使用一个使用Perl 和MySQL开发的Web接口来管理数据。

每天晚上,所有新的访问数据将被添加到 MySQL 数据库中。

3.7.1. 寻找所有未分开的双胞胎

下面的查询可以确定 哪些人可以进行这个项目的第二部分。

SELECT
    CONCAT(p1.id, p1.tvab) + 0 AS tvid,
    CONCAT(p1.christian_name, ' ', p1.surname) AS Name,
    p1.postal_code AS Code,
    p1.city AS City,
    pg.abrev AS Area,
    IF(td.participation = 'Aborted', 'A', ' ') AS A,
    p1.dead AS dead1,
    l.event AS event1,
    td.suspect AS tsuspect1,
    id.suspect AS isuspect1,
    td.severe AS tsevere1,
    id.severe AS isevere1,
    p2.dead AS dead2,
    l2.event AS event2,
    h2.nurse AS nurse2,
    h2.doctor AS doctor2,
    td2.suspect AS tsuspect2,
    id2.suspect AS isuspect2,
    td2.severe AS tsevere2,
    id2.severe AS isevere2,
    l.finish_date
FROM
    twin_project AS tp
    /* For Twin 1 */
    LEFT JOIN twin_data AS td ON tp.id = td.id
              AND tp.tvab = td.tvab
    LEFT JOIN informant_data AS id ON tp.id = id.id
              AND tp.tvab = id.tvab
    LEFT JOIN harmony AS h ON tp.id = h.id
              AND tp.tvab = h.tvab
    LEFT JOIN lentus AS l ON tp.id = l.id
              AND tp.tvab = l.tvab
    /* For Twin 2 */
    LEFT JOIN twin_data AS td2 ON p2.id = td2.id
              AND p2.tvab = td2.tvab
    LEFT JOIN informant_data AS id2 ON p2.id = id2.id
              AND p2.tvab = id2.tvab
    LEFT JOIN harmony AS h2 ON p2.id = h2.id
              AND p2.tvab = h2.tvab
    LEFT JOIN lentus AS l2 ON p2.id = l2.id
              AND p2.tvab = l2.tvab,
    person_data AS p1,
    person_data AS p2,
    postal_groups AS pg
WHERE
    /* p1 gets main twin and p2 gets his/her twin. */
    /* ptvab is a field inverted from tvab */
    p1.id = tp.id AND p1.tvab = tp.tvab AND
    p2.id = p1.id AND p2.ptvab = p1.tvab AND
    /* Just the screening survey */
    tp.survey_no = 5 AND
    /* Skip if partner died before 65 but allow emigration (dead=9) */
    (p2.dead = 0 OR p2.dead = 9 OR
     (p2.dead = 1 AND
      (p2.death_date = 0 OR
       (((TO_DAYS(p2.death_date) - TO_DAYS(p2.birthday)) / 365)
        >= 65))))
    AND
    (
    /* Twin is suspect */
    (td.future_contact = 'Yes' AND td.suspect = 2) OR
    /* Twin is suspect - Informant is Blessed */
    (td.future_contact = 'Yes' AND td.suspect = 1
                               AND id.suspect = 1) OR
    /* No twin - Informant is Blessed */
    (ISNULL(td.suspect) AND id.suspect = 1
                        AND id.future_contact = 'Yes') OR
    /* Twin broken off - Informant is Blessed */
    (td.participation = 'Aborted'
     AND id.suspect = 1 AND id.future_contact = 'Yes') OR
    /* Twin broken off - No inform - Have partner */
    (td.participation = 'Aborted' AND ISNULL(id.suspect)
                                  AND p2.dead = 0))
    AND
    l.event = 'Finished'
    /* Get at area code */
    AND SUBSTRING(p1.postal_code, 1, 2) = pg.code
    /* Not already distributed */
    AND (h.nurse IS NULL OR h.nurse=00 OR h.doctor=00)
    /* Has not refused or been aborted */
    AND NOT (h.status = 'Refused' OR h.status = 'Aborted'
    OR h.status = 'Died' OR h.status = 'Other')
ORDER BY
    tvid;

说明如下:

  • CONCAT(p1.id, p1.tvab) + 0 AS tvid

    我们希望对idtvab 连接后的结果按数值排序, 给结果加上0是为了使 MySQL 把这个结果当成数值。

  • id

    这是每一对双胞胎的标识, 在所有的表中这都是一个键。

  • tvab

    表示每一对双胞胎中的一个,它的值可以是12

  • ptvab

    这个列的值和tvab列的值相反,当tvab列的值为1时这列的值就为2, 反之亦然。这一列之所以存在是因为可以减少敲键盘的次数,还可以使 MySQL 优化查询。

在这个查询实例中,演示了怎样从到一个同样的表的连接中查找数据 (p1p2),在这里,这种技巧用来确定一对双胞胎是否在 65岁以前就死了,如果是,这一行就不返回。

上述所有信息都存在于所有表中, 我们同时使用id,tvab键 (所有表),和id,ptvab 键(person_data表) 来使查询更快。

在我们专用的机器上 (A 200MHz UltraSPARC),这个查询返回 150-200行数据,只需要不到一秒钟。

上面查询的表的当前记录是:

Table Rows
person_data 71074
lentus 5291
twin_project 5286
twin_data 2012
informant_data 663
harmony 381
postal_groups 100

3.7.2. 显示一个表示每一对双胞胎状态的表

每一次访问以一个称为event的状态码结束,这个查询用来显示一个按 event组合的所有双胞胎的表,这可以指明有多少对双胞胎的调查全部完成,有多少对双胞胎中一个完成一个没有完成,等等。

SELECT
        t1.event,
        t2.event,
        COUNT(*)
FROM
        lentus AS t1,
        lentus AS t2,
        twin_project AS tp
WHERE
        /* We are looking at one pair at a time */
        t1.id = tp.id
        AND t1.tvab=tp.tvab
        AND t1.id = t2.id
        /* Just the screening survey */
        AND tp.survey_no = 5
        /* This makes each pair only appear once */
        AND t1.tvab='1' AND t2.tvab='2'
GROUP BY
        t1.event, t2.event;

3.8. 在Apache中使用 MySQL

你可以从 MySQL 数据库中验证用户信息 ,也可以把日志写入 MySQL 表中。

你可以改变Apache的日志格式 以便于MySQL阅读,通过把下面的语句写入Apache的配置文件中:

LogFormat         "\"%h\",%{%Y%m%d%H%M%S}t,%>s,\"%b\",\"%{Content-Type}o\",          \"%U\",\"%{Referer}i\",\"%{User-Agent}i\""

要把一个日志文件按上面的格式载入到 MySQL中,你可以使用如下语句:

LOAD DATA INFILE '/local/access_log' INTO TABLE tbl_name
FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY '\\'

这个表格必须先被创建,还要包含对应的列来能够容纳LogFormat 格式的数据。

#22
京山游侠2006-03-07 21:20
完整的第三章下载
只有本站会员才能查看附件,请 登录

#23
xqiang2006-03-10 09:44

谢了哈京山兄!

#24
szmark2006-03-10 17:37
好东西,看一次顶一次
#25
wenlilir2006-03-10 17:48
支持

真的很佩服楼主的毅力啊,这个翻译应该算是MySQL5最好的中文资料之一了,看后深表感谢,并以支持:)

#26
jhchinese2006-03-10 18:59
回楼主的话
楼主只是想把它翻译出来,可是那样很费时间的,有没有想过自己整理出它的精髓,概念的性的东西可以自己找书的,可是如果有了框架再去看概念就会容易多了
这样也不会浪费太多的时间
期待.....
#27
京山游侠2006-03-10 19:35
我知道这样很要时间

不过我的思路和你有点不同,我不是想靠省略不重要的东西来节省时间,我是想让大家都参与进来,人多了,翻译起来就快了。

我希望我坚持下去,能够吸引足够多的参与者。我们可以让管理员创建一个门派,就叫做MySQL RMTT (即MySQL Reference Manual Traslate Team)。
#28
3037709572006-03-12 22:43

我都爱上你了,你真棒!呵呵!

#29
原味的夏天2006-03-15 19:34
楼主有没有做过这些,你有没有发现有的其实是存在问题的?
#30
原味的夏天2006-03-15 19:36
不管怎么样,感谢楼主的辛勤翻译,让我有这么好的资料!
#31
京山游侠2006-03-15 21:41

呵呵,文中的例子我都经过亲自测试,没有问题的。

#32
原味的夏天2006-03-16 14:08
哦,那就好,偶现在可以继续学下去了,遇到问题,请多多指教了,原来这里面有的东西版本低就不能实现的,因为我的版本不够高,所以有的就运行不了,才问楼主有没有错误的,不好意思啊!
#33
wind07172006-03-21 09:31

#34
miss_friend2006-03-29 13:03
不顶太对不起了,不过我还是有不明白的,
比如我建了个数据库aaa
我想在它里面建好几个表,我把这些create table语句写在了d.sql文件中,放在E盘下
我怎么用这个d.sql文件呀
#35
zengstar2006-04-05 15:13

这些和SQL没什么区别,有没有更简便的方法建库呀!不要命令行的模式!
教我下哈!谢谢!

#36
pen2006-04-06 11:24
顶,EN手册看懂就不错了,再翻译真的很不容易,我继续看
#37
龙之吻2006-04-08 23:26

好厉害,顶一下了,刚刚发现啊,

#38
cx091809182006-04-16 15:11

保存下来看了,LZ加油。

#39
dreame2006-04-23 22:59
京山游侠,kai对你们我真是佩服,支持的话也不多说了,希望你们早点完工!!
#40
happy0082006-04-24 22:51

顶啊, 我虽然不会编程, 看了楼主的文章,还是要顶一下的。 嘻嘻

#41
happy0082006-04-24 22:53
能不能请教一下, 挂机外挂的本地验证器怎么编写啊!! 谢谢了 可以帮忙的话
加一下 QQ 315355592
#42
bagger2006-04-29 16:18

为什么不做一个.CHM的文件的呢

#43
janghiz2006-05-12 16:00

支持一下

#44
chenzhao2006-06-13 16:32

#45
呀呀2006-06-15 16:51
不是,你太棒了 我看入迷了,忘了回复了,谢谢你!太好了,正是我需要的.
#46
flyingtp2006-08-28 14:12

好,谢谢了

#47
zhoche20082006-09-08 03:25
写的好啊,在上刚学习MYSQL,雪中送炭啊,
#48
ヤ順祺冄繎ヤ2006-10-11 10:31
太好了。。

谢谢
#49
lxy02112006-10-13 16:35

好!这样学一定会很快的学会的啊`1

#50
lxy02112006-10-13 16:37
我们也是老师说要自己看的啊!这下轻松多了啊!看到上面的写的那详细我一定会好好学的~谢谢哦!
#51
lxy02112006-10-13 16:42
哪位大哥大姐有电子书吗?发个过来行不啊?谢谢哦~
12