第二章是讲怎样安装MySQL的,kai已经开始翻译这章,我就不重复劳动了。
所以我决定翻译第三章,来点实质性的东西。希望大家多多捧场。
目录
[此贴子已经被作者于2006-2-23 23:33:39编辑过]
目录
[此贴子已经被作者于2006-2-23 23:33:39编辑过]
本章提供了一个教程,该教程演示了如何通过mysql 客户端程序来创建和使用一个简单的数据库。mysql (有时候称作 “终端监视器” 或简称为 “监视器”) 是一个允许你连接到一个MySQL Server、运行查询和查看看结果的交互程序。mysql 也可以以批处理方式运行: 你可以把查询语句预先写到一个文件中,然后让mysql 执行该文件的内容。 使用mysql 的两种方法这里都会讲到。
以--help
选项启动mysql,可以查看它提供了哪些选项:
shell> mysql --help
本章假设mysql 已经安装在你的机器上并且有一个可用的MySQL服务器能够连接, 否则请联系你的 MySQL 管理员。 (如果你就是管理员,请阅读本手册的相关章节,如Chapter 5, Database Administration。)
这一章描述了建立和使用一个数据库的全过程, 如果你仅仅对存取一个已经存在数据库感兴趣, 可以跳过介绍创建数据库和创建表的章节。
因为本章本质上是一个教程,所以许多细节已被省略。如果需要获得更多信息,请查看本手册的相关章节。
在使用mysql连接服务器的时候,你通常需要提供一个 MySQL 用户名,很有可能还需要一个密码;如果服务器运行在远程主机上,你还需要指定主机名。联系你的管理员以找出你应该使用什么样的连接参数 ( 即,那个主机,用户名和密码 )。一旦你知道正确的参数,你应该这样连接:
shell>mysql -h
Enter password:host
-uuser
-p********
host
和 user
代表MySQL 服务器运行的主机名 和你的 MySQL 帐户的用户名,请替换为适当的值。 ******** 代表你的密码;当 mysql 显示 Enter password: 提示时输入它。
如果连接成功,你将看到mysql>
提示符后面显示一些介绍信息:
shell>mysql -h
Enter password:host
-uuser
-p********
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>
提示符指明这一点。
确信你已经按照前面的方法连接到服务器。 连接服务器的时候没有选择要操作哪一个数据库,从这点上讲,学会如何发出查询比直接跳到创建表、载入数据和读出数据要重要些。这一章描述了输入命令的基本原则,输入几个查询语句,自己体会一下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
命令的行。 如果你不知道在输入其它命令之前好要先中止引号的话,这将搞得你一头雾水。
现在已经知道了怎样输入命令,是操作数据库的时候了。
假设你家(动物园)里有许多宠物,并且你希望能够跟踪它们的信息,你可以创建一个表,把你需要的信息输入表中,通过从表中提取数据,你就可以回答关于你的宠物的各种各样的问题。这一节将演示怎样做到下面的这些事情:
创建一个数据库
创建一个表
向表中输入数据
用不同的方法从表中获得数据
使用多个表
这里的动物园数据库是简单的 (故意的),但是不难想象在现实生活中有许多情况需要使用类似的数据库,比如,一个农场主可以使用一个类似的数据库来管理他的家畜, 或者一个兽医用来管理病畜记录。 这里使用到的动物园数据库中,包含其中的数据和查询语句,都可以在 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 |
+----------+
这个数据库的列表在你的机器上可能是不同的,但基本都会包含mysql
和test
数据库。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
是你连接服务器所使用的机器的机器名。
如果管理员在给你分配权限的时候创建了这个数据库,你就可以直接使用了, 否则,你需要自己创建它:
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
Enter password:host
-uuser
-p menagerie********
请注意,如上命令行中的menagerie
可不是你的密码哟,如果你想在命令行中的-p
选项中提供密码,你必须不加任何空格, (比如,是-pmypassword
,而不是-p mypassword
)。然而,不建议把密码放在命令行中,因为会直接把明文暴露给使用你机器的其它用户。
创建数据库很容易是吧,但它现在还是空的,可以用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。你可以选择1
到 65535
之间的任何长度,只要你认为这个长度合适。 (Note: 在 MySQL 5.0.3以前,最大限制是 255。)如果你刚开始的选择不合理,以后需要较长的字段, 可以用MySQL 提供的ALTER TABLE
语句修改。
动物的性别可以用多种方法表示, 比如'm'
和 'f'
或者'male'
and 'female'
。使用单个字符'm'
和 'f'
是最简单的。
使用DATE
数据类型来保存birth
和death
列的数据是比较合适的。
创建表以后,在使用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.
创建表之后,就需要向表中填入数据,用 LOAD DATA
和 INSERT
语句可以做到。
假设你的宠物信息如下表所示。 (请注意观察,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
语句载入数据,敲起键盘来要麻烦的多。
SELECT
语句用来从一个数据表中获取信息,该语句的一般格式为:
SELECTwhat_to_select
FROMwhich_table
WHEREconditions_to_satisfy
;
what_to_select
指定你想看到哪些列, *代表所有列。 which_table
指定你想从哪个表中获取数据。WHERE
是可选的,如果存在该字句,则conditions_to_satisfy
用来指定行必须满足的条件。
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
可以只更改特定的记录而不需要重新载入整个数据表。
如前所述,提取整个表中的数据是简单的,只需要忽略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 |
+----------+-------+---------+------+------------+-------+
AND
和 OR
操作符可以同时使用, 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 | +-------+--------+---------+------+------------+-------+
如果你不想看到一整行中所有的列, 你可以在查询语句中指定你感兴趣的列,列名用逗号隔开。 例如, 如果你想知道你的动物分别是什么时候出生的, 则只选择name
和 birth
列:
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 | +--------+---------+------------+
你可能已经注意到了前面的例子中,输出的结果没有按照特定的顺序排列, 然而,当每一行都以有意义的方式排序时,我们检验输出结果往往更加方便。要把结果排序,使用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
列的排序方式没有影响。
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
的值,计算death
和 birth
的差值:
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()
函数用在这里正合适。 看看它是怎样的效果如何, 运行一个简单的查询来显示birth
和 MONTH(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
函数返回1
到 12
的值,而MOD(something,12)
返回0
到 11
的值, 所以必须先MOD()再加1,
否则我们得到的结果就是从 November (11
) 到 January (1
)。
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 NULL
和 IS NOT NULL
操作符代替:
mysql> SELECT 1 IS NULL, 1 IS NOT NULL;
+-----------+---------------+
| 1 IS NULL | 1 IS NOT NULL |
+-----------+---------------+
| 0 | 1 |
+-----------+---------------+
请注意,在 MySQL中,0
或 NULL
意味着 false ,其它的值意味着 true。在一个布尔操作中默认的 truth 值是 1
。
这种对NULL
值的特殊对待,就是为什么在前面的例子中 测试一个动物是否依然或者必须用death IS NOT NULL
而不是 death <> NULL
语句的原因了。
所有的NULL
值在GROUP BY
语句中都认为是相等的。
当使用ORDER BY
时,如果是ORDER BY ... ASC
则NULL
被放在最前面,如果是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”.
MySQL 提供标准的 SQL 模式匹配语法,同时也提供一种扩展的模式匹配语法,这种语法类似于在 Unix 中的 vi, grep, 和 sed工具中使用的正则表达式语法。
SQL 模式匹配允许你使用 ‘_
’ 来匹配一个单个的字符和 ‘%
’ 来匹配多个字符(包括0个字符)。在MySQL中,SQL 模式默认是不区分大小写的,下面是一些实例。请注意,你不能使用=
或 <>
来进行 SQL 模式的比较,应该使用LIKE
或 NOT 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提供的另外一种模式匹配 使用扩展的正则表达式, 当测试是否和这种模式相匹配时, 使用REGEXP
和 NOT REGEXP
操作符 (或者RLIKE
和 NOT 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 |
+-------+--------+---------+------+------------+-------+
你还可以使用{
(“repeat-n
}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,提供了关于正则表达式符号的更多信息。