此文档通过演示如何使用 mysql 客户程序创造和使用一个简单的数据库,提供一个 MySQL 的入门教程。mysql (有时称为“终端监视器”或只是“监视”)是一个交互式程序,允许你连 接一个 MySQL 服务器,运行查询并察看结果。mysql 可以用于此模式:你预先把查询放在 一个文件中,然后告诉 mysql 执行文件的内容。本章将介绍使用 mysql 的两个方法。
要想查看由 mysql 提供的选择项目表,可以用--help 选项来调用:
shell> mysql --help
1. 连接与断开服务器
为了连接服务器, 当调用 mysql 时,通常需要提供一个 MySQL 用户名并且很可能 需要一个 密码。如果服务器运行在登录服务器之外的其它机器上,还需要指定 主机名。联系管理员以找出进行连接所使用的参数 (即, 连接的主机、用户名和 使用的密码)。知道正确的参数后,可以按照以下方式进行连接:
shell> mysql -h host -u user -p
Enter password: ********
host 和user 分别代表 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.1.2-alpha-standard
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
mysql>
mysql> 提示符告诉你 mysql 准备为你输入命令。
一些 MySQL 安装允许用户以匿名(未命名) 用户连接到本地主机上运行的服务器。如果你的 机器是这种情况,你应该能不带任何选项地调用 mysql 与该服务器连接:
shell> mysql
成功地连接后, 可以在 mysql>提示下输入 QUIT (或\q)随时退出:
mysql> QUIT
Bye
在 Unix 中,也可以按 control-D 键断开服务器。
在下列章节的大多数例子都假设你连接到了服务器。由 mysql>提示指明。
2. 输入查询
确保你连接上了服务器,如在先前的章节讨论的。连接上服务器并布代表选择了任何数据库, 但这样就可以了。知道关于如何查询的基本知识, 比马上跳至创建表、给他们装载数据并且 从他们检索数据更重要。本节描述输入命令的基本原则,使用几个查询,你能尝试了解 mysql 是如何工作的。
这是一个简单的命令,要求服务器告诉它的版本号和当前日期。在 mysql>提示输入如下命 令并按回车键:
mysql> SELECT VERSION(), CURRENT_DATE;
+-----------------+--------------+
| VERSION() | CURRENT_DATE |
+-----------------+--------------+
| 5.1.2-alpha-log | 2005-10-11 |
+-----------------+--------------+
1 row in set (0.01 sec)
mysql>
这询问说明 mysql 的几个方面:
· 一个命令通常由 SQL 语句组成,随后跟着一个分号。(有一些例外不需要分号。早
先提到的 QUIT 是一个例子。后面我们将看到其它的例子。)
· 当发出一个命令时,mysql 将它发送给服务器并显示执行结果, 然后显示另一个
mysql>显示它准备好接受其它命令。
· mysql 用表格(行和列)方式显示查询输出。第一行包含列的标签, 随后的行是查询结
果。通常,列标签是你取自数据库表的列的名字。如果你正在检索一个表达式而非表列的值 (如刚才的例子),mysql 用表达式本身标记列。
· mysql 显示返回了多少行,以及查询花了多长时间,它给你提供服务器性能的一个
大致概念。因为他们表示时钟时间(不是 CPU 或机器时间),并且因为他们受到诸如服务器 负载和网络延时的影响, 因此这些值是不精确的。(为了简洁,在本章其它例子中不再显示 “集合中的行”。)
能够以大小写输入关键词。下列查询是等价的:
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.1.2-alpha-log |
+-----------------+
1 row in set (0.00 sec)
+---------------------+
| NOW() |
+---------------------+
| 2005-10-11 15:15:00 |
+---------------------+
1 row in set (0.00 sec)
不必全在一个行内给出一个命令,较长命令可以输入到多个行中。 mysql 通过寻找终止分 号而不是输入行的结束来决定语句在哪儿结束。(换句话说, mysql 接受自由格式的输入:
它收集输入行但直到看见分号才执行。)
这里是一个简单的多行语句的例子:
mysql> SELECT
-> USER()
-> ,
-> CURRENT_DATE;
+---------------+--------------+
| USER() | CURRENT_DATE |
+---------------+--------------+
| jon@localhost | 2005-10-11 |
+---------------+--------------+
在这个例子中, 在输入多行查询的第一行后, 要注意提示符如何从 mysql>变为->,这正是 mysql 如何指出它没见到完整的语句并且正在等待剩余的部分。提示符是你的朋友,因为
它提供有价值的反馈,如果使用该反馈, 将总是知道 mysql 正在等待什么。 如果你决定不想执行正在输入过程中的一个命令, 输入\c 取消它:
mysql> SELECT
-> USER()
-> \c
mysql>
这里也要注意提示符,在你输入\c 以后, 它切换回到 mysql>,提供反馈以表明 mysql 准备 接受一个新命令。
下表显示出可以看见的各个提示符并简述它们所表示的 mysql 的状态:
当你打算在一个单行上发出一个命令时, 通常会“偶然”出现多行语句,但是没有终止分号。 在这种情况中, 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 语句,然后按 Enter(回车) 键并等待结果, 什么都没有出现。不要 惊讶, “为什么该查询这么长呢? ”,注意">提示符提供的线索。它告诉你 mysql期望见到一 个未终止字符串的余下部分。(你看见语句中的错误吗?字符串"Smith 丢掉了第二个引号。)
走到这一步,你该做什么?最简单的是取消命令。然而, 在这种情况下,你不能只是输入\c, 因为 mysql 作为它正在收集的字符串的一部分来解释它! 相反, 应输入关闭的引号字符(这 样 mysql 知道你完成了字符串),然后输入\c:
mysql> SELECT * FROM my_table WHERE name = 'Smith AND age < 30;
'> '\c
mysql>
提示符回到 mysql>,显示 mysql 准备好接受一个新命令了。
`> 提示符类似于 '> 和"> 提示符,但表示你已经开始但没有结束以`> 开始的识别符。
知道'>和">提示符的含义很重要,因为如果你错误地输入一个未终止的字符串, 任何后面输 入的行将要被 mysql 忽略--包括包含 QUIT 的行!这可能令人相当困惑,特别是如果取消当 前命令前还不知道你需要提供终止引号。
3. 创建并使用数据库
3.3.1. 创建并选择数据库
3.3.2. 创建表
3.3.3. 将数据装入表中
3.3.4. 从表检索信息
知道怎样输入命令, 便可以访问数据库了。
假定在你的家(你的“动物园”)中有很多宠物, 并且你想跟踪关于它们各种类型的信息。你可 以通过创建表来保存你的数据并根据所需要的信息装载他们,然后你可以从表中检索数据来 回答关于动物不同种类的问题。本节显示如何做到所有这些事情:
· 创建数据库
· 创建数据库表
· 装载数据到数据库表
· 以各种方法从表中检索数据
· 使用多个表
动物园数据库很简单(特意的),但是不难把它想象成可能用到类似数据库的真实世界情况。 例如, 农夫可以使用这样的一个数据库来追踪家畜,或者兽医可以用它跟踪病畜记录。从 MySQL 网址上可以获得后面章节中将用到的含有部分查询和样例数据的动物园分发。有 tar 压缩格式 (http://downloads.mysql.com/docs/menagerie-db.tar.gz)和 Zip 压缩格式
(http://downloads.mysql.com/docs/menagerie-db.zip)。
使用 SHOW 语句找出服务器上当前存在什么数据库:
mysql> SHOW DATABASES;
可能你的机器上的数据库列表是不同的, 但是很可能有 mysql 和 test 数据库。mysql 是必 需的, 因为它描述用户访问权限,test 数据库经常作为用户试身手的工作区。
请注意如果没有 SHOW DATABASES 权限,则不能看见所有数据库。
如果 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.1. 创建并选择数据库
如果管理员在设置权限时为你创建了数据库, 你可以开始使用它。否则, 你需要 自己创建数据库:
mysql> CREATE DATABASE menagerie;
在 Unix 下,数据库名称是区分大小写的(不像 SQL 关键字),因此你必须总是以 menagerie 访问数据库,而不能用 Menagerie、MENAGERIE 或其它一些变量。对表名也是这样的。 (在 Windows 下, 该限制不适用,尽管你必须在一个给定的查询中使用同样的大小写来引 用数据库和表。但是,由于多种原因,作为最好的惯例, 一定要使用与数据库创建时的同样 的大小写。)
创建数据库并不表示选定并使用它, 你必须明确地操作。为了使 menagerie 成为当前的数 据库, 使用这个命令:
mysql> USE menagerie
Database changed
数据库只需要创建一次, 但是必须在每次启动 mysql 会话时在使用前先选择它。你可以根 据上面的例子执行一个 USE 语句来实现。还可以在调用 mysql 时,通过命令行选择数据库, 只需要在提供连接参数之后指定数据库名称。例如:
shell> mysql -h host -u user
Enter password: ********
-p menagerie
注意, 刚才显示的命令行中的 menagerie 不是你的 密码。如果你想要在命令行上在-p 选项 后提供 密码, 则不能插入空格(例如, 如-pmypassword,不是-p mypassword)。但是,不 建议在命令行输入密码, 因为这样会暴露 密码, 能被在机器上登录的其它用户窥探到。
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),
-> speciesVARCHAR(20), sex CHAR(1),birth DATE, death DATE);
VARCHAR 适合于 name、owner 和 species 列, 因为列值是变长的。这些列的长度不必都 相同, 而且不必是 20。你可以挑选从 1 到 65535 的任何长度, 从中选择一个最合理的值。 (如果选择得不合适, 后来证明你需要一个更长的字段, MySQL 提供一个 ALTER TABLE 语句。)
可以用多种类型的值来表示动物记录中的性别,例如, "m"和"f",或"male"和"female"。使 用单字符"m"和"f"是最简单的方法。
很显然,birth 和 death 列应选用 DATE 数据类。
创建了数据库表后, SHOW TABLES 应该产生一些输出:
mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| pet |
+---------------------+
为了验证你的表是按你期望的方式创建, 使用一个 DESCRIBE 语句:
mysql> DESCRIBE pet;
你可以随时使用 DESCRIBE ,例如,如果你忘记表中的列的名称或类型时。
3.3. 将数据装入表中
创建表后, 需要填入内容。通过 LOAD DATA 和 INSERT 语句可以完成该任务。
假定你的宠物纪录描述如下。(假定在 MySQL 中期望的日期格式是 YYYY-MM-DD;这可 能与你习惯的不同。)
因为你是从一个空表开始的,填充它的一个简易方法是创建一个文本文件,每个动物各一行, 然后用一个语句将文件的内容装载到表中。
你可以创建一个文本文件 “pet.txt”,每行包含一个记录, 用定位符(tab)把值分开, 并 且以 CREATE TABLE 语句中列出的列次序给出。对于丢失的值(例如未知的性别, 或仍然 活着的动物的死亡日期),你可以使用 NULL 值。为了在你的文本文件中表示这些内容, 使 用\N(反斜线, 字母 N)。例如, Whistler 鸟的记录应为(这里值之间的空白是一个定位符):因为你是从一个空表开始的,填充它的一个简易方法是创建一个文本文件,每个动物各一行, 然后用一个语句将文件的内容装载到表中。
要想将文本文件 “pet.txt”装载到 pet 表中,使用这个命令:
mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;
请注意如果用 Windows 中的编辑器(使用\r\n 做为行的结束符) 创建文件,应使用:
mysql> LOAD DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet
-> LINES TERMINATED BY '\r\n';
(在运行 OS X 的 Apple 机上, 应使用行结束符'\r'。)
如果你愿意,你能明确地在 LOAD DATA 语句中指出列值的分隔符和行尾标记,但是默认 标记是定位符和换行符。这对读入文件 “pet.txt”的语句已经足够。
如果想要一次增加一个新记录, 可以使用 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 语 句装载你的初始记录。
3.4. 从表检索信息
3.3.4.1. 选择所有数据
3.3.4.2. 选择特殊行
3.3.4.3. 选择特殊列
3.3.4.4. 分类行
3.3.4.5. 日期计算
3.3.4.6. NULL 值操作
3.3.4.7. 模式匹配
3.3.4.8. 计数行
3.3.4.9. 使用 1 个以上的表
SELECT 语句用来从数据表中检索信息。语句的一般格式是:
SELECT what_to_select
FROM which_table
WHERE conditions_to_satisfy;
what_to_select 指出你想要看到的内容, 可以是列的一个表, 或*表示“所有的列” 。 which_table 指出你想要从其检索数据的表。WHERE 子句是可选项, 如果选择该项, conditions_to_satisfy 指定行必须满足的检索条件。
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.4.2. 选择特殊行
如上所示,检索整个表是容易的。只需要从 SELECT 语句中删掉 WHERE 子句。但 是一般你不想看到整个表, 特别地当表变得很大时。相反, 你通常对回答一个具 体的问题更感兴趣, 在这种情况下在你想要的信息上进行一些限制。让我们看一 些他们回答的有关你宠物的问题的选择查询。
可以从表中只选择特定的行。例如,如果你想要验证你对 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。
字符串比较时通常对大小些不敏感, 因此你可以将名字指定为"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');
3.4.3. 选择特殊列
如果你不想看到表中的所有行, 就命名你感兴趣的列, 用逗号分开。例如, 如果 你想要知道你的动物什么时候出生的,选择 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
| Claws | cat
| Buffy | dog
| Fang | dog
| Bowser | dog
| 1993-02-04 |
| 1994-03-17 |
| 1989-05-13 |
| 1990-08-27 |
| 1989-08-31 |
+--------+---------+------------+
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 列的排序顺序。
3.4.5. 日期计算
MySQL 提供了几个函数,可以用来计算日期, 例如,计算年龄或提取日期部分。
要想确定每个宠物有多大,可以计算当前日期的年和出生日期之间的差。如果当前日期的日 历年比出生日期早, 则减去一年。以下查询显示了每个宠物的出生日期、当前日期和年龄数 值的年数字。
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 (日历年)部分的最右面 5 个 字符。比较 MM-DD 值的表达式部分的值一般为 1 或 0,如果 CURDATE()的年比 birth 的年 早,则年份应减去 1。整个表达式有些难懂, 使用 alias (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 是特殊的值,不能使用普
通比较符来比较
如果你想要知道哪个动物下个月过生日,怎么办? 对于这类计算,年和天是无关的,你只需 要提取 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 |
+----------+------------+--------------+
找出下个月生日的动物也是容易的。假定当前月是 4 月, 那么月值是 4,你可以找在 5 月出 生的动物 (5 月),方法是:
mysql> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+-------+------------+
| name | birth |
+-------+------------+
| Buffy | 1989-05-13 |
+-------+------------+
如果当前月份是 12 月, 就有点复杂了。你不能只把 1 加到月份数(12)上并寻找在 13 月出 生的动物, 因为没有这样的月份。相反, 你应寻找在 1 月出生的动物(1月) 。
你甚至可以编写查询,不管当前月份是什么它都能工作。采用这种方法不必在查询中使用一 个特定的月份,DATE_ADD( )允许在一个给定的日期上加上时间间隔。如果在 NOW( )值上 加上一个月,然后用 MONTH()提取月份,结果产生生日所在月份:
mysql> SELECT name, birth FROM pet
-> WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL
1 MONTH));
完成该任务的另一个方法是加 1 以得出当前月份的下一个月(在使用取模函数(MOD)后,如 果月份当前值是 12,则“回滚”到值 0):
mysql> SELECT name, birth FROM pet
-> WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;
注意, MONTH 返回在 1 和 12 之间的一个数字, 且 MOD(something,12)返回在 0 和 11 之 间的一个数字, 因此必须在 MOD( )以后加 1,否则我们将从 11 月( 11 )跳到 1 月(1)。
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 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 意味着假而其它值意味着真。布尔运算的默认真值是 1。
对 NULL 的特殊处理即是在前面的章节中,为了决定哪个动物不再是活着的,使用 death IS
NOT NULL 而不使用 death != NULL 的原因。
在 GROUP BY 中,两个 NULL 值视为相同。
执行 ORDER BY 时, 如果运行 ORDER BY ... ASC,则 NULL 值出现在最前面, 若运行 ORDER BY ... DESC,则 NULL 值出现在最后面。
NULL 操作的常见错误是不能在定义为 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 |
+-----------+---------------+------------+----------------+
因此完全可以在定义为 NOT NULL 的列内插入 0 或空字符串, 实际是 NOT NULL。
3.4.7. 模式匹配
MySQL 提供标准的 SQL 模式匹配, 以及一种基于象 Unix 实用程序如vi、grep 和sed 的 扩展正则表达式模式匹配的格式。
SQL 模式匹配允许你使用 “_”匹配任何单个字符,而 “%”匹配任意数目字符(包括零字 符)。在 MySQL 中, SQL 的模式默认是忽略大小写的。下面给出一些例子。注意使用 SQL 模式时,不能使用=或!=;而应使用 LIKE 或 NOT LIKE 比较操作符。
要想找出以 “b”开头的名字:
mysql> SELECT * FROM pet WHERE name LIKE 'b%';
要想找出以 “fy”结尾的名字:
mysql> SELECT * FROM pet WHERE name LIKE '%fy';
要想找出包含“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 '_____';
由 MySQL 提供的模式匹配的其它类型是使用扩展正则表达式。当你对这类模式进行匹配测试 时,使用 REGEXP 和 NOT REGEXP 操作符(或 RLIKE 和 NOT RLIKE,它们是同义词)。
扩展正则表达式的一些字符是:
„.‟匹配任何单个的字符。
字符类“[...]”匹配在方括号内的任何字符。例如, “[abc]”匹配“a” 、 “b”
或 “c”。为了命名字符的范围, 使用一个“-” 。 “[a-z]”匹配任何字母, 而 “[0-9]”匹配
任何数字。
· “ * ”匹配零个或多个在它前面的字符。例如, “x*”匹配任何数量的 “x”字符,
“[0-9]*”匹配任何数量的数字,而 “.*”匹配任何数量的任何字符。
如果 REGEXP 模式与被测试值的任何地方匹配, 模式就匹配(这不同于 LIKE 模式匹配,只有与整个值匹配,模式才匹配)。
为了定位一个模式以便它必须匹配被测试值的开始或结尾, 在模式开始处 使用 “^”或在模式的结尾用 “$”。
为了说明扩展正则表达式如何工作, 下面使用 REGEXP 重写上面所示的 LIKE 查询:
为了找出以 “b”开头的名字,使用“^”匹配名字的开始:
mysql> SELECT * FROM pet WHERE name REGEXP '^b';
如果你想强制使 REGEXP 比较区分大小写,使用 BINARY 关键字使其中一个字符串变为二 进制字符串。该查询只匹配名称首字母的小写„b‟。
mysql> SELECT * FROM pet WHERE name REGEXP BINARY '^b';
为了找出以 “fy”结尾的名字, 使用 “$”匹配名字的结尾:
mysql> SELECT * FROM pet WHERE name REGEXP 'fy$';
为了找出包含一个“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 模式那样。
为了找出包含正好 5 个字符的名字,使用 “^”和 “$”匹配名字的开始和结尾,和 5 个 “.” 实例在两者之间:
mysql> SELECT * FROM pet WHERE name REGEXP '^.....$';
你也可以使用“{n}”“重复 n 次”操作符重写前面的查询:
mysql> SELECT * FROM pet WHERE name REGEXP '^.{5}$';
3.4.8. 计数行
数据库经常用于回答这个问题, “某个类型的数据在表中出现的频度?”例如, 你可能想要知道你有多少宠物, 或每位主人有多少宠物, 或你可能想要对你的动 物进行各种类型的普查。
计算你拥有动物的总数目与“在 pet 表中有多少行?”是同样的问题, 因为每个宠物有一个记 录。 COUNT(*)函数计算行数,所以计算动物数目的查询应为:
mysql> SELECT COUNT(*) FROM pet;
+----------+
| COUNT(*) |
+----------+
| 9 |
+----------+
在前面,你检索了拥有宠物的人的名字。如果你想要知道每个主人有多少宠物, 你可以使用 COUNT( )函数:
mysql> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
注意, 使用 GROUP BY 对每个 owner 的所有记录分组, 没有它, 你会得到错误消息:
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 以各种方式分类你的数据。下列例子显示出进行动物普查操作的 不同方式。
每种动物的数量:
mysql> SELECT species, COUNT(*) FROM pet GROUP BY species;
每种性别的动物数量:
mysql> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
(在这个输出中,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;
或,如果你仅需要知道已知性别的按性别的动物数目:
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.4.9. 使用 1 个以上的表
pet 表追踪你有哪个宠物。如果你想要记录其它相关信息, 例如在他们一生中看 兽医或何时后代出生,你需要另外的表。这张表应该像什么呢?需要:
· 它需要包含宠物名字以便你知道每个事件属于哪个动物。
· 需要一个日期以便你知道事件是什么时候发生的。
· 需要一个描述事件的字段。
· 如果你想要对事件进行分类, 则需要一个事件类型字段。
综合上述因素, event 表的 CREATE TABLE 语句应为:
mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
-> type VARCHAR(15), remark VARCHAR(255));
对于 pet 表,最容易的方法是创建包含信息的用定位符分隔的文本文件来装载初始记录:
采用如下方式装载记录:
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 子句列出两个表,因为查询需要从两个表提取信息。
当从多个表组合(联结)信息时,你需要指定一个表中的记录怎样能匹配其 它表的记录。这很简单, 因为它们都有一个 name 列。查询使用 WHERE 子 句基于 name 值来匹配 2 个表中的记录。
因为 name 列出现在两个表中,当引用列时,你一定要指定哪个表。把表 名附在列名前即可以实现。
你不必有 2 个不同的表来进行联结。如果你想要将一个表的记录与同一个表的其它记录进 行比较,可以将一个表联结到自身。例如,为了在你的宠物之中繁殖配偶, 你可以用 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';
在这个查询中,我们为表名指定别名以便能引用列并且使得每一个列引用与哪个表实例相关 联更直观。
4. 获得数据库和表的信息
如果你忘记数据库或表的名字, 或给定的表的结构是什么(例如,它的列叫什么), 怎么办? MySQL 通过提供数据库及其支持的表的信息的几个语句解决这个问题。
你已经见到了 SHOW DATABASES,它列出由服务器管理的数据库。为了找出当前选择了 哪个数据库,使用 DATABASE( )函数:
mysql> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| menagerie |
+------------+
如果你还没选择任何数据库,结果是 NULL。
为了找出当前的数据库包含什么表(例如, 当你不能确定一个表的名字),使用这个命令:
mysql> SHOW TABLES;
如果你想要知道一个表的结构, 可以使用 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 生成有关索引的信息。
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 任务中运行查询时。 在这种情况下,你必须使用批模式。
当你以批模式运行 mysql 时,比起你交互地使用它时,其默认输出格式是不同的(更简明些)。 例如, 当交互式运行 SELECT DISTINCT species FROM pet 时,输出应为:
但是当以批模式运行时, 输出应为:
如果你想要在批模式中得到交互输出格式,使用 mysql -t 。为了回显以输出被执行的命令, 使用 mysql -vvv。
你还可以使用源代码或 \.命令从 mysql 提示符运行脚本:
mysql> source filename;
mysql> \. filename
6. 常用查询的例子
3.6.1. 列的最大值
3.6.2. 拥有某个列的最大值的行
3.6.3. 列的最大值:按组
3.6.4. 拥有某个字段的组间最大值的行
3.6.5. 使用用户变量
3.6.6. 使用外键
3.6.7. 根据两个键搜索
3.6.8. 根据天计算访问量
3.6.9. 使用 AUTO_INCREMENT
下面是一些学习如何用 MySQL 解决一些常见问题的例子。
在一些例子中,使用数据库表“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,
-> 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 | |
+---------+--------+-------+
6.1. 列的最大值
“最大的物品号是什么?”
SELECT MAX(article) AS article FROM shop;
6.2. 拥有某个列的最大值的行
任务:找出最贵物品的编号、销售商和价格。
这很容易用一个子查询做到:
SELECT article, dealer, price
FROM shop
WHERE price=(SELECT MAX(price) FROM shop);
另一个解决方案是按价格降序排序所有行并用 MySQL 特定 LIMIT 子句只得到第一行:
SELECT article, dealer, price
FROM shop
ORDER BY price DESC
LIMIT 1;
注:如果有多项最贵的物品( 例如每个的价格为 19.95),LIMIT 解决方案仅仅显示其中一个!
6.3. 列的最大值:按组
任务: 每项物品的的最高价格是多少?
SELECT article, MAX(price) AS price
FROM shop
GROUP BY article
6.4. 拥有某个字段的组间最大值的行
任务: 对每项物品, 找出最贵价格的物品的经销商。
可以用这样一个子查询解决该问题:
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
6.5. 使用用户变量
你可以清空 MySQL 用户变量以记录结果,不必将它们保存到客户端的临时变量中。
例如, 要找出价格最高或最低的物品的, 其方法是:
SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop;
SELECT * FROM shop WHERE price=@min_price OR price=@max_price;
6.6. 使用外键
在 MySQL 中, InnoDB 表支持对外部关键字约束条件的检查。
只是联接两个表时, 不需要外部关键字。对于除 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 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';
按照这种方式使用, 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
在列定义中,按这种方式使用 REFERENCES 作为注释或“提示”适用于表 MyISAM 和 BerkeleyDB。
6.7. 根据两个键搜索
可以充分利用使用单关键字的 OR 子句, 如同 AND 的处理。
一个比较灵活的例子是寻找两个通过 OR 组合到一起的关键字:
SELECT field1_index, field2_index FROM test_table
WHERE field1_index = '1' OR field2_index = '1'
还可以使用 UNION 将两个单独的SELECT 语句的输出合成到一起来更有效地解决该问题。
每个 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';
6.8. 根据天计算访问量
下面的例子显示了如何使用位组函数来计算每个月中用户访问网页的天数。
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);
示例表中含有代表用户访问网页的年-月-日值。可以使用以下查询来确定每个月的访问天 数:
SELECT year,month,BIT_COUNT(BIT_OR(1<<day)) AS days FROM t1
GROUP BY year,month;
将返回:
该查询计算了在表中按年/月组合的不同天数, 可以自动去除重复的询问。
6.9. 使用 AUTO_INCREMENT
可以通过 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 值。这些函数与具体连接有关,因此其返回值不会被其它执行插入功 能的连接影响。
注释: 对于多行插入,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 (
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 将使用该索引生成以 AUTO_INCREMENT 列开始的序列值。。例如,如果 animals 表含有索引 PRIMARY KEY (grp, id)和 INDEX(id),MySQL 生成序列值时将忽略 PRIMARY KEY。结果是,该表包含一 个单个的序列, 而不是符合 grp 值的序列。
要想以 AUTO_INCREMENT 值开始而不是 1,你可以通过 CREATE TABLE 或 ALTER TABLE 来设置该值,如下所示:
mysql> ALTER TABLE tbl AUTO_INCREMENT = 100;
7. 孪生项目的查询
3.7.1. 查找所有未分发的孪生项
3.7.2. 显示孪生对状态的表
这个项目是 Institute of Environmental Medicine at Karolinska Institutet Stockholm 和 the Section on Clinical Research in Aging and Psychology at the University of Southern California 的合作项目。
该项目包括筛选部分,即通过电话回访在瑞典超过 65 岁的所有孪生。满足某种标准的孪 生进入下一阶段。在下一阶段中,医生/护士小组将访问想参加的孪生。部分检查包括物理 检查和神经、心理检查、实验室试验、神经成像、心理状况评估和家族历史搜集。并且, 应 根据医疗和环境风险因素来搜集数据。
可从以下链接找到孪生研究的更多信息:
http://www.mep.ki.se/twinreg/index_en.html
用一个用 Perl 和 MySQL 编写的 web 接口来管理项目的后面部分。
每天晚上所有会谈的数据被移入一个 MySQL 数据库。
7.1. 查找所有未分发的孪生项
下列查询用来决定谁进入项目的第二部分:
|
一些解释:
· CONCAT(p1.id, p1.tvab) + 0 AS tvid
我们想要在 id 和tvab 的连接上以数字顺序排序。结果加 0 使得 MySQL 把结果变为一个数字。
· 列 id
这标识一对孪生。它是所有表中的一个键。
· 列 tvab
这标识孪生中的一个。它的值为 1 或 2。
· 列 ptvab
这是 tvab 的一个逆运算。当 tvab 是 1,它是 2,反之亦然。它用来保存输入并且使 MySQL 的优化查询更容易。
这个查询表明, 怎样用联结(p1和 p2)从同一个表中查找表。在例子中, 这被用来检查孪生 的一个是否在 65 岁前死了。如果如此,行不返回值。
上述所有孪生信息存在于所有表中。我们对 id,tvab (所有表) 和 id,ptvab (person_data) 上 采用键以使查询更快。
在我们的生产机器上(一台 200MHz UltraSPARC),这个查询返回大约 150-200 行并且时 间不超过一秒。
7.2. 显示孪生对状态的表
每一次会面以一个称为 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;
8. 与 Apache 一起使用 MySQL
还有一些项目, 你可以从 MySQL 数据库鉴别用户, 并且你还可以将日志文件写入 MySQL 数据库表。
你可以将以下内容放到 Apache 配置文件中,更改Apache 日志格式,使 MySQL 更容易读取:
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 行对应。