MySQL操作教程
连接与断开服务器
为了连接服务器,当调用mysql时,通常需要提供一个MySQL用户名并且很可能需要一个
密码。如果服务器运行在登录服务器之外的其它机器上,还需要指定主机名。联系管理员以找出进行连接所使用的参数 (
即,连接的主机、用户名和使用的密码)。知道正确的参数后,可以按照以下方式进行连接:
mysql -h host -u user -p
例如:
mysql -hlocalhost -uroot -p11111
输入查询
- 了解版本号和当前日期
SELECT VERSION(), CURRENT_DATE;
这询问说明mysql的几个方面:
· 一个命令通常由SQL语句组成,随后跟着一个分号。(有一些例外不需要分号。早先提到的QUIT是一个例子。后面我们将看到其它的例子。)
· 当发出一个命令时,mysql将它发送给服务器并显示执行结果,然后显示另一个mysql>显示它准备好接受其它命令。
· mysql用表格(行和列)
方式显示查询输出。第一行包含列的标签,随后的行是查询结果。通常,列标签是你取自数据库表的列的名字。如果你正在检索一个表达式而非表列的值(
如刚才的例子),mysql用表达式本身标记列。
· mysql显示返回了多少行,以及查询花了多长时间,它给你提供服务器性能的一个大致概念。因为他们表示时钟时间(不是 CPU
或机器时间),并且因为他们受到诸如服务器负载和网络延时的影响,因此这些值是不精确的。(为了简洁,在本章其它例子中不再显示“集合中的行”。)
- 这是另外一个查询,它说明你能将mysql用作一个简单的计算器
SELECT SIN(PI() / 4), (4 + 1) * 5;
- 至此显示的命令是相当短的单行语句。你可以在一行上输入多条语句,只需要以一个分号间隔开各语句:
SELECT VERSION();
SELECT NOW();
不必全在一个行内给出一个命令,较长命令可以输入到多个行中。mysql通过寻找终止分号而不是输入行的结束来决定语句在哪儿结束。(换句话说,mysql接受自由格式的输入:它收集输入行但直到看见分号才执行。)
SELECT USER()
,
CURRENT_DATE;
在这个例子中,在输入多行查询的第一行后,要注意提示符如何从mysql>变为->
,这正是mysql如何指出它没见到完整的语句并且正在等待剩余的部分。提示符是你的朋友,因为它提供有价值的反馈,如果使用该反馈,将总是知道mysql正在等待什么。
如果你决定不想执行正在输入过程中的一个命令,输入\c取消它:
SELECT USER() \C;
这里也要注意提示符,在你输入\c以后,它切换回到mysql>,提供反馈以表明mysql准备接受一个新命令。
- 下表显示出可以看见的各个提示符并简述它们所表示的mysql的状态:
提示符 | 含义 |
---|---|
mysql> | 准备好接受新命令。 |
-> | 等待多行命令的下一行。 |
'> | 等待下一行,等待以单引号(“'”)开始的字符串的结束。 |
"> | 等待下一行,等待以双引号(“"”)开始的字符串的结束。 |
`> | 等待下一行,等待以反斜点(‘`’)开始的识别符的结束。 |
/*> | 等待下一行,等待以/*开始的注释的结束。 |
- 当你打算在一个单行上发出一个命令时,通常会“偶然”出现多行语句,但是没有终止分号。在这种情况中,mysql等待进一步输入:
SELECT USER()
- >
- 如果出现这种情况(你认为输完了语句,但是只有一个->提示符响应)
,很可能mysql正在等待分号。如果你没有注意到提示符的提示,在意识到你需要做什么之前,你可能会呆坐一会儿。输入一个分号完成语句,mysql将执行:
SELECT USER()
- >;
- 在字符串收集期间将出现 ‘> 和 "> 提示符(提示MySQL正等待字符串的结束)。在MySQL中,可以写由‘’’或‘“’字符括起来的字符串 (
例如,'hello’或"goodbye”),并且mysql允许输入跨越多行的字符串。当看到一个 ‘> 或 ">
提示符时,这意味着已经输入了包含以‘’’或‘"’括号字符开始的字符串的一行,但是还没有输入终止字符串的匹配引号。这显示你粗心地省掉了一个引号字符。
SELECT *
FROM my_table
WHERE name = 'Smith AND age < 30;
' >
如果你输入SELECT语句,然后按Enter(回车)键并等待结果,什么都没有出现。不要惊讶,“为什么该查询这么长呢?”,注意">
提示符提供的线索。它告诉你mysql期望见到一个未终止字符串的余下部分。(你看见语句中的错误吗?字符串"Smith丢掉了第二个引号。)
走到这一步,你该做什么?最简单的是取消命令。然而,在这种情况下,你不能只是输入\c,因为mysql作为它正在收集的字符串的一部分来解释它!相反,应输入关闭的引号字符(
这样mysql知道你完成了字符串),然后输入\c:
SELECT *
FROM my_table
WHERE name = 'Smith AND age < 30;
' > '\c
提示符回到mysql>,显示mysql准备好接受一个新命令了。
> 提示符类似于 '> 和"> 提示符,但表示你已经开始但没有结束以
> 开始的识别符。
知道’>和">提示符的含义很重要,因为如果你错误地输入一个未终止的字符串,任何后面输入的行将要被mysql忽略–包括包含QUIT的行!这可能令人相当困惑,特别是如果取消当前命令前还不知道你需要提供终止引号。
创建并使用数据库
- 查看数据库
SHOW
DATABASES;
可能你的机器上的数据库列表是不同的,但是很可能有mysql和test数据库。mysql是必需的,因为它描述用户访问权限,test数据库经常作为用户试身手的工作区。
请注意如果没有SHOW DATABASES权限,则不能看见所有数据库。
- 如果test数据库存在,则可以使用它。
USE
test;
注意,USE,类似QUIT,不需要一个分号。(如果你喜欢,你可以用一个分号终止这样的语句;这无碍)USE语句在使用上也有另外一个特殊的地方:它必须在一个单行上给出。
你可列在后面的例子中使用test数据库(如果你能访问它)
,但是你在该数据库创建的任何东西可以被访问它的其它人删除,因此,你应该询问MySQL管理员许可你使用自己的一个数据库。假定你想要调用你的menagerie,管理员需要执行这样一条命令:
GRANT
ALL
ON menagerie.* TO 'your_mysql_name'@'your_client_host';
这里your_mysql_name是分配给你的MySQL用户名,your_client_host是所连接的服务器所在的主机。
创建和使用数据库
设置数据库语句
CREATE
DATABASE menagerie;
在Unix下,数据库名称是区分大小写的(不像SQL关键字)
,因此你必须总是以menagerie访问数据库,而不能用Menagerie、MENAGERIE或其它一些变量。对表名也是这样的。(在Windows下,该限制不适用,尽管你必须在一个给定的查询中使用同样的大小写来引用数据库和表。但是,由于诸多原因,作为最好的惯例,一定使用与数据创建时的同样的大小写。)
USE
menagerie;
数据库只需要创建一次,但是每次必须启动mysql会话在使用前先选择它。你可以根据上面的例子执行一个USE语言来实现。还可以在调用mysql时,通过命令行选择数据库,只需要在提供连接参数之后指定数据库名称即可。
mysql
-h host -u user -p menagerire;
注意,刚才显示的命令行中的menagerie不是你的 密码。如果你想要在命令行上在-p选项后提供 密码,则不能插入空格(
例如,如-pmypassword,不是-p mypassword)。但是,不建议在命令行输入密码,因为这样会暴露 密码,能被在机器上登录的其它用户窥探到。
创建数据库表
- 查看数据库表信息
SHOW
TABLES;
- 创建数据库表
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数据类。
- 为了验证你的表是按你期望的方式创建,使用一个DESCRIBE语句:
DESCRIBE pet;
你可以随时使用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 | Whistler | bird | \N | 1997-12-09 | \N |
要想将文本文件“pet.txt”装载到pet表中,使用这个命令:
LOAD
DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet;
请注意如果用Windows中的编辑器(使用\r\n做为行的结束符)创建文件,应使用:
LOAD
DATA LOCAL INFILE '/path/pet.txt' INTO TABLE pet LINES TERMINATED BY '\r\n';
在运行OS X的Apple机上,应使用行结束符’\r’。)
如果你愿意,你能明确地在LOAD DATA语句中指出列值的分隔符和行尾标记,但是默认标记是定位符和换行符。这对读入文件“pet.txt”的语句已经足够。
如果该语句失败,可能是你安装的MySQL不与使用默认值的本地文件兼容。
如果想要一次增加一个新记录,可以使用INSERT语句。最简单的形式是,提供每一列的值,其顺序与CREATE
TABLE语句中列的顺序相同。假定Diane把一只新仓鼠命名为Puffball,你可以使用下面的INSERT语句添加一条新记录。
INSERT INTO pet
VALUES ('Puffball', 'Diane', 'hamster', 'f', '1999-03-30', NULL);
注意,这里字符串和日期值均为引号扩起来的字符串。另外,可以直接用INSERT语句插入NULL代表不存在的值。不能使用LOAD DATA中所示的的\N。
从这个例子,你应该能看到涉及很多的键入用多个INSERT语句而非单个LOAD DATA语句装载你的初始记录。
从表检索信息
- 选择所有数据
- 选择特殊行
- 选择特殊列~~
- 分类行
- 日期计算
- NULL值操作
- 模式匹配
- 计数行
- 使用1个以上的表
SELECT语句用来从数据表中检索信息。语句的一般格式是:
SELECT what_to_select
FROM which_table
WHERE conditions_to_satisfy;
what_to_select指出你想要看到的内容,可以是列的一个表,或*
表示“所有的列”。which_table指出你想要从其检索数据的表。WHERE子句是可选项,如果选择该项,conditions_to_satisfy指定行必须满足的检索条件。
- 选择所有数据
SELECT最简单的形式是从一个表中检索所有记录SELECT * FROM pet;
如果你想要浏览整个表,可以使用这种形式的SELECT,例如,刚刚装载了初始数据集以后。也有可能你想到Bowser的生日看起来不很对。查阅你原来的家谱,你发现正确的出生年是1989,而不是1979。
至少有两种修正方法:- 编辑文件“pet.txt”改正错误,然后使用DELETE和LOAD DATA清空并重新装载表:
DELETE FROM pet;
然而, 如果这样操做,必须重新输入Puffball记录。LOAD DATA LOCAL INFILE 'pet.txt' INTO TABLE pet;
- 用一个UPDATE语句仅修正错误记录:
UPDATE只更改有问题的记录,不需要重新装载数据库表。UPDATE pet SET birth = '1989-08-31' WHERE name = 'Bowser';
- 编辑文件“pet.txt”改正错误,然后使用DELETE和LOAD DATA清空并重新装载表:
- 选择特殊行
如上所示,检索整个表是容易的。只需要从SELECT语句中删掉WHERE子句。但是一般你不想看到整个表,特别地当表变得很大时。相反,你通常对回答一个具体的问题更感兴趣,在这种情况下在你想要的信息上进行一些限制。让我们看一些他们回答的有关你宠物的问题的选择查询。
可以从表中只选择特定的行。例如,如果你想要验证你对Bowser的生日所做的更改,按下述方法选择Bowser的记录:
输出证实正确的年份记录为1989,而不是1979。SELECT * FROM pet WHERE name ='';
字符串比较时通常对大小些不敏感,因此你可以将名字指定为"bowser"、"BOWSER"等,查询结果相同。
你可以在任何列上指定条件,不只仅仅是name。例如,如果你想要知道哪个动物在1998以后出生的,测试birth列:
SELECT *
FROM pet
WHERE birth > '1998-1-1';
可以组合条件,例如,找出雌性的狗:
SELECT *
FROM pet
WHERE species = 'dog'
AND sex = 'f';
上面的查询使用AND逻辑操作符,也有一个OR操作符:
SELECT *
FROM pet
WHERE species = 'snake'
OR species = 'bird';
AND和OR可以混用,但AND比OR具有更高的优先级。如果你使用两个操作符,使用圆括号指明如何对条件进行分组是一个好主意:
SELECT *
FROM pet
WHERE (species = 'cat' AND sex = 'm')
OR (species = 'dog' AND sex = 'f');
- 选择特殊列
如果你不想看到表中的所有行,就命名你感兴趣的列,用逗号分开。例如,如果你想要知道你的动物什么时候出生的,选择name和birth列:
SELECT name, birth
FROM pet;
找出谁拥有宠物,使用这个查询:
SELECT owner
FROM pet;
请注意该查询只是简单地检索每个记录的owner列,并且他们中的一些出现多次。为了使输出减到最少,增加关键字DISTINCT检索出每个唯一的输出记录:
SELECT DISTINCT owner
FROM pet;
可以使用一个WHERE子句结合行选择与列选择。例如,要想查询狗和猫的出生日期,使用这个查询:
SELECT name, species, birth
FROM pet
WHERE species = 'dog'
OR species = 'cat';
- 分类行
你可能已经注意到前面的例子中结果行没有以特定的顺序显示。然而,当行按某种方式排序时,检查查询输出通常更容易。为了排序结果,使用ORDER
BY子句。
这里是动物生日,按日期排序:
你可能已经注意到前面的例子中结果行没有以特定的顺序显示。然而,当行按某种方式排序时,检查查询输出通常更容易。为了排序结果,使用ORDER
BY子句。
这里是动物生日,按日期排序:
SELECT name, birth
FROM pet
ORDER BY birth;
在字符类型列上,与所有其他比较操作类似,分类功能正常情况下是以区分大小写的方式执行的。这意味着,对于等同但大小写不同的列,并未定义其顺序。对于某一列,可以使用BINARY强制执行区分大小写的分类功能,如:ORDER
BY BINARY col_name.
默认排序是升序,最小的值在第一。要想以降序排序,在你正在排序的列名上增加DESC(降序 )关键字:
SELECT name, birth
FROM pet
ORDER BY birth DESC;
可以对多个列进行排序,并且可以按不同的方向对不同的列进行排序。例如,按升序对动物的种类进行排序,然后按降序根据生日对各动物种类进行排序(最年轻的动物在最前面),使用下列查询:
SELECT name, species, birth
FROM pet
ORDER BY species, birth DESC;
注意DESC关键字仅适用于在它前面的列名(birth);不影响species列的排序顺序。
- 日期计算
MySQL提供了几个函数,可以用来计算日期,例如,计算年龄或提取日期部分。
要想确定每个宠物有多大,可以计算当前日期的年和出生日期之间的差。如果当前日期的日历年比出生日期早,则减去一年。以下查询显示了每个宠物的出生日期、当前日期和年龄数值的年数字。
SELECT name,
birth,
CURDATE(),
(YEAR(CURDATE())- YEAR (birth))
- (RIGHT(CURDATE(),5)< RIGHT (birth,5))
AS age
FROM pet;
此处,YEAR()提取日期的年部分,RIGHT()提取日期的MM-DD (日历年)
部分的最右面5个字符。比较MM-DD值的表达式部分的值一般为1或0,如果CURDATE()
的年比birth的年早,则年份应减去1。整个表达式有些难懂,使用alias (age)来使输出的列标记更有意义。
尽管查询可行,如果以某个顺序排列行,则能更容易地浏览结果。添加ORDER BY name子句按照名字对输出进行排序则能够实现。
SELECT name,
birth,
CURDATE(),
(YEAR(CURDATE())- YEAR (birth))
- (RIGHT(CURDATE(),5)< RIGHT (birth,5))
AS age
FROM pet
ORDER BY name;
为了按age而非name排序输出,只要再使用一个ORDER BY子句:
SELECT name,
birth,
CURDATE(),
(YEAR(CURDATE())- YEAR (birth))
- (RIGHT(CURDATE(),5)< RIGHT (birth,5))
AS age
FROM pet
ORDER BY age;
可以使用一个类似的查询来确定已经死亡动物的死亡年龄。你通过检查death值是否是NULL来确定是哪些动物,然后,对于那些非NULL值的动物,需要计算出death和birth值之间的差:
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;
查询使用death IS NOT NULL而非death != NULL,因为NULL是特殊的值,不能使用普通比较符来比较.
如果你想要知道哪个动物下个月过生日,怎么办?对于这类计算,年和天是无关的,你只需要提取birth列的月份部分。MySQL提供几个日期部分的提取函数,例如YEAR( )
、MONTH( )和DAYOFMONTH( )。在这里MONTH()是适合的函数。为了看它怎样工作,运行一个简单的查询,显示birth和MONTH(birth)的值:
SELECT name, birth, MONTH (birth)
FROM pet;
找出下个月生日的动物也是容易的。假定当前月是4月,那么月值是4,你可以找在5月出生的动物 (5月),方法是:
SELECT name, birth
FROM pet
WHERE MONTH (birth) = 5;
如果当前月份是12月,就有点复杂了。你不能只把1加到月份数(12)
上并寻找在13月出生的动物,因为没有这样的月份。相反,你应寻找在1月出生的动物(1月) 。
你甚至可以编写查询,不管当前月份是什么它都能工作。采用这种方法不必在查询中使用一个特定的月份,DATE_ADD( )
允许在一个给定的日期上加上时间间隔。如果在NOW( )值上加上一个月,然后用MONTH()提取月份,结果产生生日所在月份:
SELECT name, birth
FROM pet
WHERE MONTH (birth) = MONTH (DATE_ADD(CURDATE(), INTERVAL 1 MONTH));
完成该任务的另一个方法是加1以得出当前月份的下一个月(在使用取模函数(MOD)后,如果月份当前值是12,则“回滚”到值0):
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意味着“没有值”或“未知值”,且它被看作与众不同的值。为了测试NULL,你不能使用算术比较
操作符例如=、<或!=。为了说明它,试试下列查询:
ELECT
1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
很显然你不能通过这些比较得到有意义的结果。相反使用IS NULL和IS NOT NULL操作符:
SELECT 1 IS NULL, 1 IS NOT NULL;
请注意在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则可以很容易地进行测试,如下所示:
SELECT 0 IS NULL, 0 IS NOT NULL, '' IS NULL, '' IS NOT NULL;
因此完全可以在定义为NOT NULL的列内插入0或空字符串,实际是NOT NULL
- 模式匹配
MySQL提供标准的SQL模式匹配,以及一种基于象Unix实用程序如vi、grep和sed的扩展正则表达式模式匹配的格式。
SQL模式匹配允许你使用“_”匹配任何单个字符,而“%”匹配任意数目字符(包括零字符)。在
MySQL中,SQL的模式默认是忽略大小写的。下面给出一些例子。注意使用SQL模式时,不能使用=或!=;而应使用LIKE或NOT LIKE比较操作符。
要想找出以“b”开头的名字:
SELECT *
FROM pet
WHERE name LIKE 'b%';
要想找出以“fy”结尾的名字:
SELECT *
FROM pet
WHERE name LIKE '%fy';
要想找出包含“w”的名字:
SELECT *
FROM pet
WHERE name LIKE '%w%';
要想找出正好包含5个字符的名字,使用“_”模式字符:
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模式匹配,只有与整个值匹配,模式才匹配)。
获得数据库和表的信息
MySQL通过提供数据库及其支持的表的信息的几个语句解决这个问题。
你已经见到了SHOW DATABASES,它列出由服务器管理的数据库。为了找出当前选择了哪个数据库,使用DATABASE( )函数:
SELECT DATABASE();
如果你还没选择任何数据库,结果是NULL。
为了找出当前的数据库包含什么表(例如,当你不能确定一个表的名字),使用这个命令:
SHOW
TABLES;
如果你想要知道一个表的结构,可以使用DESCRIBE命令;它显示表中每个列的信息:
DESCRIBE pet;
Field显示列名字,Type是列的数据类型,Null表示列是否能包含NULL值,Key显示列是否被索引而Default指定列的默认值。
如果表有索引,SHOW INDEX FROM tbl_name生成有关索引的信息
在批处理模式下使用mysql
为了做到这些,把你想要运行的命令放在一个文件中,然后告诉mysql从文件读取它的输入:
mysql
< batch-file
如果在Windows下运行mysql,并且文件中有一些可以造成问题的特殊字符,可以这样操作:
mysql
-e "source batch-file"
如果你需要在命令行上指定连接参数,命令应为:
mysql
-h host -u user -p < batch-file
当这样操作mysql时,则创建一个脚本文件,然后执行脚本。
如果你想在语句出现错误的时候仍想继续执行脚本,则应使用–force命令行选项。
为什么要使用一个脚本?有很多原因:
- 如果你需要重复运行查询(比如说,每天或每周),可以把它编成一个脚本,则每次执行时不必重新键入。
- 可以通过拷贝并编辑脚本文件从类似的现有的查询生成一个新查询。
- 当你正在开发查询时,批模式也是很有用的,特别对多行命令或多语句命令序列。如果你犯了一个错误,你不必重新输入所有内容,只需要编辑脚本来改正错误,然后告诉mysql再次执行脚本。
- 如果你有一个产生多个输出的查询,你可以通过一个分页器而不是盯着它翻屏到屏幕的顶端来运行输出:
mysql
< batch-file | more
- 你可以捕捉文件中的输出以便进行进一步的处理:
mysql
< batch-file > mysql.out
- 你可以将脚本分发给另外的人,以便他们也能运行命令。
- 某些情况不允许交互地使用,例如, 当你从一个cron任务中运行查询时。在这种情况下,你必须使用批模式。
如果你想要在批模式中得到交互输出格式,使用mysql -t。为了回显以输出被执行的命令,使用mysql -vvv。
你还可以使用源代码或 .命令从mysql提示符运行脚本:
source
filename;
常用查询的例子
- 列的最大值
SELECT MAX(article) AS article
FROM shop;
- 拥有某个列的最大值的行
SELECT article, dealer, price
FROM shop
WHERE price = (SELECT MAX(price) FROM shop);
SELECT article, dealer, price
FROM shop
ORDER BY price DESC LIMIT 1;
- 列的最大值:按组
SELECT article, MAX(price) AS price
FROM shop
GROUP BY article
- 拥有某个字段的组间最大值的行
- 使用用户变量
SELECT article, dealer, price
FROM shop s1
WHERE price = (SELECT MAX(s2.price)
FROM shop s2
WHERE s1.article = s2.article);
- 使用外键
SELECT @min_price:=MIN(price),@max_price:=MAX(price)
FROM shop;
SELECT *
FROM shop
WHERE price = @min_price
OR price = @max_price;
- 根据两个键搜索
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);
SELECT *
FROM person;
SELECT *
FROM shirt;
SELECT s.*
FROM person p,
shirt s
WHERE p.name LIKE 'Lilliana%'
AND s.owner = p.id
AND s.color <> 'white';
在列定义中,按这种方式使用REFERENCES作为注释或“提示”适用于表MyISAM和BerkeleyDB.
- 根据天计算访问量
可以充分利用使用单关键字的OR子句,如同AND的处理。
一个比较灵活的例子是寻找两个通过OR组合到一起的关键字:
SELECT field1_index, field2_index
FROM test_table
WHERE field1_index = '1'
OR field2_index = '1'
还可以使用UNION将两个单独的SELECT语句的输出合成到一起来更有效地解决该问题。参见13.2.7.2节,“UNION语法
”。
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;
- 使用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;
你可以使用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;
请注意在这种情况下(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来设置该值,如下所示:
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)
假定每个商人对每项物品有一个固定价格,那么(物品,商人)即为该记录的主关键字。启动命令行工具mysql并选择数据库:
mysql
your-database-name
(在大多数MySQL中,你可以使用test数据库)。
你可以使用以下语句创建示例表:
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));
导入数据
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);
执行语句后,表应包含以下内容:
SELECT *
FROM shop;
- 列的最大值
- 拥有某个列的最大值的行
- 列的最大值:按组
- 拥有某个字段的组间最大值的行
- 使用用户变量
- 使用外键
- 根据两个键搜索
- 根据天计算访问量
- 使用AUTO_INCREMENT
孪生项目的查询
这个项目是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接口来管理项目的后面部分。
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 行并且时间不超过一秒。
显示孪生对状态的表
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;