目录
本章通过演示如何使用mysql客户程序创造和使用一个简单的数据库,提供一ä¸�MySQL的入门教程ã€�mysql(有时称为“终端监视器”或只是“监视”)是一个交互式程序,允许你连接一ä¸�MySQL服务器,运行查询并察看结果ã€�mysql可以用于批模式:你预先把查询放在一个文件中,然后告è¯�mysql执行文件的内容。本章将介绍使用mysql的两个方法ã€�
要想查看ç”�mysql提供的选择项目表,可以ç”�--help选项来调用:
shell> mysql --help
本章假定mysql已经被安装在你的机器上,并且有一ä¸�MySQL服务器可以连接。否则,请联ç»�MySQL管理员。(如果ä½�是管理员,则需要查阅本手册的其它章节,例如ç¬�5章:数据库管ç��。)
本章描述建立和使用一个数据库的全过程。如果你仅仅对访问一个已经存在的数据库感兴趣,可以跳过描述怎样创建数据库及它所包含的表的章节ã€�
由于本章是一个教程,省略了许多细节。关于这里所涉及的主题的详细信息,请查阅本手册的相关章节ã€�
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>提示指明ã€�
确保你连接上了服务器,如在先前的章节讨论的。连接上服务器并布代表选择了任何数据库,但这样就可以了。知道关于如何查询的基本知识,比马上跳至创建表、给他们装载数据并且从他们检索数据更重要。本节描述输入命令的基本原则,使用几个查询,你能尝试了解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等待进一步输入:
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的行!这可能令人相当困惑,特别是如果取消当前命令前还不知道你需要提供终止引号ã€�
知道怎样输入命令,便可以访问数据库了ã€�
假定在你的家(你的“动物园â€�)中有很多宠物,并且你想跟踪关于它们各种类型的信息。你可以通过创建表来保存你的数据并根据所需要的信息装载他们,然后你可以从表中检索数据来回答关于动物不同种类的问题。本节显示如何做到所有这些事情:
· 创建数据åº�
· 创建数据库表
· 装载数据到数据库è¡�
· 以各种方法从表中检索数æ��
· 使用多个è¡�
动物园数据库很简å��(特意çš�),但是不难把它想象成可能用到类似数据库的真实世界情况。例如,农夫可以使用这样的一个数据库来追踪家畜,或者兽医可以用它跟踪病畜记录。从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权限,则不能看见所有数据库。参è§�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是分配给你的MySQL用户名,your_client_host是所连接的服务器所在的主机ã€�
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 -p menagerie
Enter password: ********
注意,刚才显示的命令行中çš�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的任何长度,从中选择一个最合理的值。(如果选择得不合适,后来证明你需要一个更长的字段ï¼�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只更改有问题的记录,不需要重新装载数据库表ã€�
可以从表中只选择特定的行。例如,如果你想要验证你å¯�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 |
+-------+--------+---------+------+------------+-------+
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 |
+--------+---------+------------+
这里是动物生日,按日期排序:
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提供了几个函数,可以用来计算日期,例如,计算年龄或提取日期部分ã€�
要想确定每个宠物有多大,可以计算当前日期的年和出生日期之间的差。如果当前日期的日历年比出生日期早,则减去一年。以下查询显示了每个宠物的出生日期、当前日期和年龄数值的年数字ã€�
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是特殊的值,不能使用普通比较符来比较,以后会给出解释。参è§�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 |
+----------+------------+--------------+
找出下个月生日的动物也是容易的。假定当前月æ˜�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则可以很容易地进行测试,如下所示:
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查询ï¼�
为了找出ä»�â€�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模式那样ã€�
为了找出包含正好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 |
+---------+------+----------+
· 它需要包含宠物名字以便你知道每个事件属于哪个动物ã€�
· 需要一个日期以便你知道事件是什么时候发生的ã€�
· 需要一个描述事件的字段ã€�
· 如果你想要对事件进行分类,则需要一个事件类型字段ã€�
综合上述因素ï¼�event表的CREATE TABLE语句应为ï¼�
mysql> CREATE TABLE event (name VARCHAR(20), date DATE,
-> type VARCHAR(15), remark VARCHAR(255));
对于pet表,最容易的方法是创建包含信息的用定位符分隔的文本文件来装载初始记录:
|
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 |
+--------+------+-----------------------------+
关于该查询要注意的几件事情:
你不必有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 |
+--------+------+--------+------+---------+
在这个查询中,我们为表名指定别名以便能引用列并且使得每一个列引用与哪个表实例相关联更直观ã€�
你已经见到了SHOW DATABASES,它列出由服务器管理的数据库。为了找出当前选择了哪个数据库,使ç”�DATABASE( )函数ï¼�
mysql> SELECT DATABASE();
+------------+
| DATABASE() |
+------------+
| menagerie |
+------------+
如果你还没选择任何数据库,结果æ˜�NULLã€�
为了找出当前的数据库包含什么表(例如,当你不能确定一个表的名å�),使用这个命令:
mysql> SHOW TABLES;
+---------------------+
| Tables in menagerie |
+---------------------+
| event |
| pet |
+---------------------+
如果你想要知道一个表的结构,可以使用DESCRIBE命令;它显示表中每个列的信息ï¼�
mysql> DESCRIBE pet;
+---------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| name | varchar(20) | YES | | NULL | |
| owner | varchar(20) | YES | | NULL | |
| species | varchar(20) | YES | | NULL | |
| sex | char(1) | YES | | NULL | |
| birth | date | YES | | NULL | |
| death | date | YES | | NULL | |
+---------+-------------+------+-----+---------+-------+
Field显示列名字,Type是列的数据类型,Null表示列是否能包含NULL值,Key显示列是否被索引è€�Default指定列的默认值ã€�
如果表有索引ï¼�SHOW INDEX FROM tbl_name生成有关索引的信息ã€�
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命令行选项ã€�
为什么要使用一个脚本?有很多原因:
· shell> mysql < batch-file | more
· shell> mysql < batch-file > mysql.out
当你以批模式运行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
下面是一些学习如何用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 |
+---------+--------+-------+
“最大的物品号是什么?â€�
SELECT MAX(article) AS article FROM shop; +---------+ | article | +---------+ | 4 | +---------+
这很容易用一个子查询做到ï¼�
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解决方案仅仅显示其中一个!
任务:每项物品的的最高价格是多少ï¼�
SELECT article, MAX(price) AS price FROM shop GROUP BY article +---------+-------+ | article | price | +---------+-------+ | 0001 | 3.99 | | 0002 | 10.99 | | 0003 | 1.69 | | 0004 | 19.95 | +---------+-------+
任务:对每项物品,找出最贵价格的物品的经销商ã€�
可以用这样一个子查询解决该问题:
SELECT article, dealer, price
FROM shop s1
WHERE price=(SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
你可以清ç©�MySQL用户变量以记录结果,不必将它们保存到客户端的临时变量中。(参见 9.3节,“用户变量â€�.)ã€�
例如,要找出价格最高或最低的物品的,其方法是ï¼�
mysql> SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop; mysql> SELECT * FROM shop WHERE price=@min_price OR price=@max_price; +---------+--------+-------+ | article | dealer | price | +---------+--------+-------+ | 0003 | D | 1.25 | | 0004 | D | 19.95 | +---------+--------+-------+
åœ�MySQL中,InnoDB表支持对外部关键字约束条件的检查。参è§�15.2节,“InnoDB存储引擎â€�。还可以参见 1.8.5.5节,“外键â€�ã€�
只是联接两个表时,不需要外部关键字。对于除InnoDB类型的表,当使用REFERENCES tbl_name(col_name)子句定义列时可以使用外部关键字,该子句没有实际的效果ï¼�只作为备忘录或注释来提醒,你目前正定义的列指向另一个表中的一个列ã€�执行该语句时,实现下面很重要ï¼�
· MySQL不执行表tbl_name 中的动作,例如作为你正定义的表中的行的动作的响应而删除行;换句话说,该句法不会致ä½�ON DELETEæˆ�ON UPDATE行为(如果你åœ�REFERENCES子句中写å…�ON DELETEæˆ�ON UPDATE子句,将被忽略)ã€�
· 该句法可以创建一ä¸�column;但不创建任何索引或关键字ã€�
· 如果用该句法定义InnoDB表,将会导致错误ã€�
你可以使用作为联接列创建的列,如下所示:
CREATE TABLE person (
id SMALLINT UNSIGNED 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ã€�
可以充分利用使用单关键字çš�OR子句,如å��AND的处理ã€�
一个比较灵活的例子是寻找两个通过OR组合到一起的关键字:
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只搜索一个关键字,可以进行优化:
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_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;
关于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çš�优化查询更容易ã€�
这个查询表明,怎样用联ç»�(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;
你可以将以下内容放到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行对应ã€�
这是MySQL参考手册的翻译版本,关于MySQL参考手册,请访é—�dev.mysql.comã€� 原始参考手册为英文版,与英文版参考手册相比,本翻译版可能不是最新的ã€�