MYSQL使用手冊
本章通过演示怎样使用mysql客户程序创造和使用一个简单的数据库,提供一个MySQL的新手教程。mysql(有时称为“终端监视器”或仅仅是“监视”)是一个交互式程序。同意你连接一个MySQLserver,执行查询并察看结果。
mysql能够用于批模式:你预先把查询放在一个文件里,然后告诉mysql执行文件的内容。
本章将介绍使用mysql的两个方法。
要想查看由mysql提供的选择项目表,能够用--help选项来调用:
shell> mysql --help
本章假定mysql已经被安装在你的机器上。而且有一个MySQLserver能够连接。否则,请联络MySQL管理员。(假设你是管理员。则须要查阅本手冊的其他章节,比如第5章:数据库管理。
)
本章描写叙述建立和使用一个数据库的全过程。假设你只对訪问一个已经存在的数据库感兴趣。能够跳过描写叙述如何创建数据库及它所包括的表的章节。
因为本章是一个教程,省略了很多细节。关于这里所涉及的主题的具体信息,请查阅本手冊的相关章节。
假设server执行在登录server之外的其他机器上。还须要指定主机名。联系管理员以找出进行连接所使用的參数 (即。连接的主机、username和使用的password)。知道正确的參数后。能够依照下面方式进行连接:
shell> mysql -h host -u user -p
Enter password: ********
host和user分别代表MySQLserver执行的主机名和MySQL账户username。
设置时替换为正确的值。******** 代表你的密码;当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安装同意用户以匿名(未命名)用户连接到本地主机上执行的server。假设你的机器是这样的情况。你应该能不带不论什么选项地调用mysql与该server连接:
shell> mysql
成功地连接后。能够在mysql>提示下输入QUIT (或/q)随时退出:
mysql> QUIT
Bye
在Unix中,也能够按control-D键断开server。
在下列章节的大多数样例都如果你连接到了server。由mysql>提示指明。
3.2. 输入查询
确保你连接上了server。如在先前的章节讨论的。连接上server并布代表选择了不论什么数据库。但这样就能够了。知道关于怎样查询的基本知识。比立即跳至创建表、给他们装载数据而且从他们检索数据更重要。本节描写叙述输入命令的基本原则,使用几个查询,你能尝试了解mysql是怎样工作的。
这是一个简单的命令,要求server告诉它的版本和当前日期。在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将它发送给server并显示运行结果,然后显示还有一个mysql>显示它准备好接受其他命令。
· mysql用表格(行和列)方式显示查询输出。
第一行包括列的标签,随后的行是查询结果。
通常,列标签是你取自数据库表的列的名字。假设你正在检索一个表达式而非表列的值(如刚才的样例),mysql用表达式本身标记列。
· mysql显示返回了多少行,以及查询花了多长时间,它给你提供server性能的一个大致概念。由于他们表示时钟时间(不是 CPU 或机器时间),而且由于他们受到诸如server负载和网络延时的影响,因此这些值是不精确的。
(为了简洁,在本章其他样例中不再显示“集合中的行”。)
可以以大写和小写输入关键词。下列查询是等价的:
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等待进一步输入:
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的行!这可能令人相当困惑,特别是假设取消当前命令前还不知道你须要提供终止引號。
知道如何输入命令,便能够訪问数据库了。
假定在你的家(你的“动物园”)中有非常多宠物,而且你想跟踪关于它们各种类型的信息。
你能够通过创建表来保存你的数据并依据所须要的信息装载他们,然后你能够从表中检索数据来回答关于动物不同种类的问题。本节显示怎样做到全部这些事情:
· 创建数据库
· 创建数据库表
· 装载数据到数据库表
· 以各种方法从表中检索数据
· 使用多个表
动物园数据库非常easy(特意的)。可是不难把它想象成可能用到类似数据库的真实世界情况。比如。农夫能够使用这种一个数据库来追踪家畜。或者兽医能够用它跟踪病畜记录。从MySQL网址上能够获得后面章节中将用到的含有部分查询和例子数据的动物园分发。有tar压缩格式 (http://downloads.mysql.com/docs/menagerie-db.tar.gz)和Zip压缩格式 (http://downloads.mysql.com/docs/menagerie-db.zip)。
使用SHOW语句找出server上当前存在什么数据库:
mysql> SHOW DATABASES;
+----------+
| Database |
+----------+
| mysql |
| test |
| tmp |
+----------+
可能你的机器上的数据库列表是不同的。可是非常可能有mysql和test数据库。mysql是必需的。由于它描写叙述用户訪问权限,test数据库常常作为用户试身手的工作区。
请注意假设没有SHOW DATABASES权限。则不能看见全部数据库。參见13.5.1.3节。“GRANT和REVOKE语法”。
假设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是分配给你的MySQLusername,your_client_host是所连接的server所在的主机。
mysql> CREATE DATABASE menagerie;
在Unix下,数据库名称是区分大写和小写的(不像SQLkeyword),因此你必须总是以menagerie訪问数据库,而不能用Menagerie、MENAGERIE或其他一些变量。对表名也是这种。(在Windows下。该限制不适用。虽然你必须在一个给定的查询中使用相同的大写和小写来引用数据库和表。
可是。因为多种原因,作为最好的惯例。一定要使用与数据库创建时的相同的大写和小写。)
创建数据库并不表示选定并使用它,你必须明白地操作。为了使menagerie成为当前的数据库,使用这个命令:
mysql> USE menagerie
Database changed
数据库仅仅须要创建一次,可是必须在每次启动mysql会话时在使用前先选择它。你能够依据上面的样例运行一个USE语句来实现。
还能够在调用mysql时。通过命令行选择数据库,仅仅须要在提供连接參数之后指定数据库名称。比如:
shell> mysql -h host -u user -p menagerie
Enter password: ********
注意,刚才显示的命令行中的menagerie不是你的 password。假设你想要在命令行上在-p选项后提供 password,则不能插入空格(比如。如-pmypassword,不是-p mypassword)。可是,不建议在命令行输入password,由于这样会暴露 password,能被在机器上登录的其他用户窥探到。
3.3.2. 创建表
创建数据库是非常easy的部分。可是在这时它是空的。正如SHOW TABLES将告诉你的:
mysql> SHOW TABLES;
Empty set (0.00 sec)
较难的部分是决定你的数据库结构应该是什么:你须要什么数据库表。各数据库表中有什么样的列。
你将须要一个包括你每一个宠物的记录的表。它可称为pet表。而且它应该包括,最少,每一个动物的名字。由于名字本身不是非常有趣,表应该包括另外的信息。比如,假设在你豢养宠物的家庭有超过一个人。你可能想要列出每一个动物的主人。
你可能也想要记录比如种类和性别的一些主要的描写叙述信息。
年龄呢?那可能有趣。可是存储到一个数据库中不是一件好事情。
年龄随着时间流逝而变化,这意味着你将要不断地更新你的记录。相反, 存储一个固定值比如生日比較好。那么。不管何时你须要年龄。能够以当前日期和出生日期之间的差来计算它。MySQL提供了日期运算函数,因此这并不困难。存储出生日期而非年龄还有其他长处:
· 你能够使用数据库完毕这种任务,比如生成即将到来的宠物生日的提示。(假设你觉得这类查询有点蠢,注意,这与从商务数据库来识别出不久要发给生日祝贺的客户是同一个问题,由于计算机帮助私人联络。)
· 你能够相对于日期而不止是当前日期来计算年龄。
比如。假设你在数据库存储死亡日期,你能非常easy地计算出一仅仅宠物死时有多大。
你可能想到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的不论什么长度,从中选择一个最合理的值。
(假设选择得不合适,后来证明你须要一个更长的字段,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;
+---------+-------------+------+-----+---------+-------+
| 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,比如,假设你忘记表中的列的名称或类型时。
创建表后,须要填入内容。通过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”,每行包括一个记录,用定位符(tab)把值分开,而且以CREATE TABLE语句中列出的列次序给出。对于丢失的值(比如未知的性别,或仍然活着的动物的死亡日期)。你能够使用NULL值。为了在你的文本文件里表示这些内容。使用/N(反斜线。字母N)。
比如,Whistler鸟的记录应为(这里值之间的空白是一个定位符):
name |
owner |
species |
sex |
birth |
death |
Whistler |
Gwen |
bird |
/N |
1997-12-09 |
/N |
要想将文本文件“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”的语句已经足够。
假设该语句失败,可能是你安装的MySQL不与使用默认值的本地文件兼容。关于怎样更改请參见5.6.4节,“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语句装载你的初始记录。
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. 选择特殊行
可是一般你不想看到整个表,特别地当表变得非常大时。相反,你通常对回答一个详细的问题更感兴趣,在这样的情况下在你想要的信息上进行一些限制。
让我们看一些他们回答的有关你宠物的问题的选择查询。
能够从表中仅仅选择特定的行。比如。假设你想要验证你对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');
+-------+--------+---------+------+------------+-------+
| 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. 选择特殊列
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列,而且他们中的一些出现多次。为了使输出减到最少。添加keywordDISTINCT检索出每一个唯一的输出记录:
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. 分类行
然而。当行按某种方式排序时,检查查询输出通常更easy。为了排序结果,使用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(降序 )keyword:
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 |
+----------+---------+------------+
注意DESCkeyword仅适用于在它前面的列名(birth);不影响species列的排序顺序。
3.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)来使输出的列标记更有意义。
虽然查询可行,假设以某个顺序排列行,则能更easy地浏览结果。
加入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是特殊的值,不能使用普通比較符来比較。以后会给出解释。參见3.3.4.6节。“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 |
+----------+------------+--------------+
找出下个月生日的动物也是easy的。
假定当前月是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)。
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则能够非常easy地进行測试,例如以下所看到的:
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。參见A.5.3节,“与NULL值有关的问题”。
MySQL提供标准的SQL模式匹配。以及一种基于象Unix有用程序如vi、grep和sed的扩展正則表達式模式匹配的格式。
SQL模式匹配同意你使用“_”匹配不论什么单个字符。而“%”匹配随意数目字符(包含零字符)。在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]”匹配不论什么数字。
· “ * ”匹配零个或多个在它前面的字符。比如。“x*”匹配不论什么数量的“x”字符,“[0-9]*”匹配不论什么数量的数字,而“.*”匹配不论什么数量的不论什么字符。
- 假设REGEXP模式与被測试值的不论什么地方匹配,模式就匹配(这不同于LIKE模式匹配,仅仅有与整个值匹配,模式才匹配)。
- 为了定位一个模式以便它必须匹配被測试值的開始或结尾,在模式開始处使用“^”或在模式的结尾用“$”。
为了说明扩展正則表達式怎样工作,以下使用REGEXP重写上面所看到的的LIKE查询:
为了找出以“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比較区分大写和小写,使用BINARYkeyword使当中一个字符串变为二进制字符串。
该查询仅仅匹配名称首字母的小写‘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模式那样。
为了找出包括正好5个字符的名字。使用“^”和“$”匹配名字的開始和结尾。和5个“.”实例在两者之间:
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}”“反复n次”操作符重写前面的查询:
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 |
+-------+--------+---------+------+------------+-------+
附录G:MySQL正則表達式 提供了关于正則表達式的句法的具体信息。
”比如。你可能想要知道你有多少宠物,或每位主人有多少宠物,或你可能想要对你的动物进行各种类型的普查。
计算你拥有动物的总数目与“在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对每一个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;
+---------+----------+
| 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. 使用1个以上的表
· 它须要包括宠物名字以便你知道每一个事件属于哪个动物。
· 须要一个日期以便你知道事件是什么时候发生的。
· 须要一个描写叙述事件的字段。
· 假设你想要对事件进行分类。则须要一个事件类型字段。
综合上述因素,event表的CREATE TABLE语句应为:
mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
-> type VARCHAR(15), remark VARCHAR(255));
对于pet表,最easy的方法是创建包括信息的用定位符分隔的文本文件来装载初始记录:
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子句列出两个表。由于查询须要从两个表提取信息。
- 当从多个表组合(联结)信息时。你须要指定一个表中的记录如何能匹配其他表的记录。这非常easy。由于它们都有一个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';
+--------+------+--------+------+---------+
| name | sex | name | sex | species |
+--------+------+--------+------+---------+
| Fluffy | f | Claws | m | cat |
| Buffy | f | Fang | m | dog |
| Buffy | f | Bowser | m | dog |
+--------+------+--------+------+---------+
在这个查询中,我们为表名指定别名以便能引用列而且使得每个列引用与哪个表实例相关联更直观。
3.4. 获得数据库和表的信息
你已经见到了SHOW DATABASES,它列出由server管理的数据库。为了找出当前选择了哪个数据库,使用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从文件读取它的输入:
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时,输出应为:
+---------+
| species |
+---------+
| bird |
| cat |
| dog |
| hamster |
| snake |
+---------+
可是当以批模式执行时,输出应为:
species
bird
cat
dog
hamster
snake
假设你想要在批模式中得到交互输出格式。使用mysql -t。为了回显以输出被运行的命令,使用mysql -vvv。
你还能够使用源码或 /.命令从mysql提示符执行脚本:
mysql> source filename;
mysql> /. filename
3.6. 经常使用查询的样例
以下是一些学习怎样用MySQL解决一些常见问题的样例。
在一些样例中,使用数据库表“shop”来储存某个商人(经销商)的每件物品(物品号)的价格。假定每一个商人对每项物品有一个固定价格。那么(物品。商人)即为该记录的主keyword。
启动命令行工具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 |
+---------+--------+-------+
“最大的物品号是什么?”
SELECT MAX(article) AS article FROM shop; +---------+ | article | +---------+ | 4 | +---------+
这非常easy用一个子查询做到:
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解决方式只显示当中一个!
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 | +---------+-------+
任务:对每项物品,找出最贵价格的物品的经销商。
能够用这样一个子查询解决该问题:
SELECT article, dealer, price FROM shop s1 WHERE price=(SELECT MAX(s2.price) FROM shop s2 WHERE s1.article = s2.article);
你能够清空MySQL用户变量以记录结果,不必将它们保存到client的暂时变量中。(參见 9.3节,“用户变量”.)。
比如。要找出价格最高或最低的物品的,其方法是:
mysql> <strong class="userinput">SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop;</strong> mysql> <strong class="userinput">SELECT * FROM shop WHERE price=@min_price OR price=@max_price;</strong> +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0003 | D | 1.25 | | 0004 | D | 19.95 | +---------+--------+-------+
在MySQL中,InnoDB表支持对外部keyword约束条件的检查。
还能够參见1.8.5.5节,“外键”。
仅仅是联接两个表时。不须要外部keyword。
对于除InnoDB类型的表,当使用REFERENCES tbl_name(col_name)子句定义列时能够使用外部keyword,该子句没有实际的效果,仅仅作为备忘录或凝视来提醒,你眼下正定义的列指向还有一个表中的一个列。运行该语句时,实现以下非常重要:
· MySQL不运行表tbl_name 中的动作,比如作为你正定义的表中的行的动作的响应而删除行。换句话说,该句法不会致使ON DELETE或ON UPDATE行为(假设你在REFERENCES子句中写入ON DELETE或ON UPDATE子句,将被忽略)。
· 该句法能够创建一个column;但不创建不论什么索引或keyword。
· 假设用该句法定义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
在列定义中,按这样的方式使用REFERENCES作为凝视或“提示”适用于表MyISAM和BerkeleyDB。
能够充分利用使用单keyword的OR子句。如同AND的处理。
一个比較灵活的样例是寻找两个通过OR组合到一起的keyword:
SELECT field1_index, field2_index FROM test_table
WHERE field1_index = '1' OR field2_index = '1'
该情形是已经优化过的。參见7.2.6节。“索引合并优化”。
还能够使用UNION将两个单独的SELECT语句的输出合成到一起来更有效地解决该问题。參见13.2.7.2节,“UNION语法
”。
每一个SELECT仅仅搜索一个keyword,能够进行优化:
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';
以下的样例显示了怎样使用位组函数来计算每一个月中用户訪问网页的天数。
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;
将返回:
+------+-------+------+
| year | month | days |
+------+-------+------+
| 2000 | 01 | 3 |
| 2000 | 02 | 2 |
+------+-------+------+
该查询计算了在表中按年/月组合的不同天数。能够自己主动去除反复的询问。
能够通过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_INCREMENTkeyword。在复制设置中。通过该函数能够在其他server上正确复制多行插入。
对于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;
关于AUTO_INCREMENT的具体信息:
· 怎样为列指定AUTO_INCREMENT属性:13.1.5节。“CREATE TABLE语法”和 13.1.2节。“ALTER TABLE语法”。
· AUTO_INCREMENT的动作取决于SQL模式:5.3.2节,“SQL服务器模式”。
· 找出含有最新AUTO_INCREMENT值的行:12.1.3节,“比較函数和操作符”。
· 设置将用到的AUTO_INCREMENT值: 13.5.3节。“SET语法” 。
· AUTO_INCREMENT和复制:6.7节。“复制特性和已知问题”.
· AUTO_INCREMENT相关的可用于复制的Server-system变量(auto_increment_increment和auto_increment_offset):5.3.3节。“服务器系统变量”。
这个项目是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数据库。
下列查询用来决定谁进入项目的第二部分:
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
我们想要在id和tvab的连接上以数字顺序排序。结果加0使得MySQL把结果变为一个数字。
· 列id
这标识一对孪生。它是全部表中的一个键。
· 列tvab
这标识孪生中的一个。它的值为1或2。
· 列ptvab
这是tvab的一个逆运算。当tvab是1,它是2,反之亦然。
它用来保存输入而且使MySQL的优化查询更easy。
这个查询表明,如何用联结(p1和p2)从同一个表中查找表。
在样例中,这被用来检查孪生的一个是否在65岁前死了。假设如此。行不返回值。
上述全部孪生信息存在于全部表中。我们对id,tvab(全部表)和id,ptvab (person_data) 上採用键以使查询更快。
在我们的生产机器上(一台200MHz UltraSPARC)。这个查询返回大约 150-200 行而且时间不超过一秒。
表 |
行数 |
person_data |
71074 |
lentus |
5291 |
twin_project |
5286 |
twin_data |
2012 |
informant_data |
663 |
harmony |
381 |
postal_groups |
100 |
这表明多少对孪生已经完毕,多少对的当中之中的一个已完毕而还有一个拒绝了,等等。
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
你能够将下面内容放到Apache配置文件里。更改Apache日志格式,使MySQL更easy读取:
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行相应。