数据库软件应称为DBMS(数据库管理系统)。而数据库是通过DBMS创建和操纵的容器。
【定义1】数据库(database) 保存有组织的数据的容器(通常是一个文件或一组文件)。
【定义2】表(table) 是某种特定类型数据的结构化清单。表名的唯一性取决于多个因素,如数据库名和表名等的结合。这表示,虽然在相同数据库中不能两次使用相同的表名,但在不同的数据库中却可以使用相同的表名。
【定义3】模式(schema) 关于数据库和表的布局及特性的信息。
【定义4】表由列组成。列中存储着表中某部分的信息。而列(column) 表中的一个字段。所有表都是由一个或多个列组成的。
【定义5】数据类型(datatype) 所容许的数据的类型。每个表列都有相应的数据类型,它限制(或容许)该列中存储的数据。
【定义6】行(row) 表中的一个记录。表中的数据是按行存储的,所保存的每个记录存储在自己的行内。
【定义7】主键(primary key)一列(或一组列),其值能够唯一区分表中每个行。唯一标识表中每行的这个列(或这组列)称为主键。主键用来表示一个特定的行。没有主键,更新或删除表中特定行很困难,因为没有安全的方法保证只涉及相关的行。
其主键规定有两条:
主键通常定义在表的一列上,但这并不是必需的,也可以一起使用多个列作为主键,规则做出相应改变即可(单列值可不唯一,但一组列值需要唯一)。
SQL(发音为字母S-Q-L或sequel)是结构化查询语言(Structured QueryLanguage)的缩写。SQL是一种专门用来与数据库通信的语言。本书讲授的SQL是专门针对MySQL的,虽然书中所讲授的多数语法也适用于其他DBMS,但不要认为这些SQL语法是完全可移植的。
MySQL是一种DBMS,即它是一种数据库软件。
DBMS可分为两类:一类为基于共享文件系统的DBMS,另一类为基于客户机—服务器的DBMS。。前者(包括诸如Microsoft Access和FileMaker)用于桌面用途,通常不用于高端或更关键的应用。而MySQL、Oracle以及Microsoft SQL Server等数据库是基于客户机—服务器的数据库。
每个MySQL安装都有一个名为mysql的简单命令行实用程序。这个实用程序没有下拉菜单、流行的用户界面、鼠标支持或任何类似的东西。其需要注意:
MySQL Administrator(MySQL管理器)是一个图形交互客户机,用来简化MySQL服务器的管理。
MySQL Query Browser为一个图形交互客户机,用来编写和执行MySQL命令。
MySQL与所有客户机—服务器DBMS一样,要求在能执行命令之前登录到DBMS。并且,MySQL在内部保存自己的用户列表,并且把每个用户与各种权限关联起来。
为了连接到MySQL,需要以下信息:
格式为:mysql -h 主机IP -P 端口 -u 用户名 -p 密码
而对于服务器来说,想要修改MySQL开启服务的端口,仅需要:
打开mysql配置文件my.ini或my.cnf找到port=3306
把3306修改成你需要的端口,然后重启mysql就好了
【定义】关键字(key word) 作为MySQL语言组成部分的一个保留字。决不要用关键字命名一个表或列。
【例子】为了使用crashcourse数据库,应该输入以下内容:
USE crashcourse
记住,必须先使用USE打开数据库,才能读取其中的数据。
如果你不知道可以使用的数据库名时怎么办?可用MySQL的SHOW命令来显示这些信息。
SHOW DATABASES
返回可用数据库的一个列表
为了获得一个数据库内的表的列表,使用SHOW TABLES;
【案例】SHOW COLUMNS
要求给出一个表名(这个例子中的 FROM customers),它对每个字段返回一行,行中包含字段名、数据类型、是否允许NULL、键信息、默认值以及其他信息(如字段cust_id的auto_increment)
MySQL支持用DESCRIBE作为SHOW COLUMNS FROM的一种快捷方式。换句话说,
DESCRIBE customers
是SHOW COLUMNS FROM customers
的一种快捷方式。
SHOW STATUS
,用于显示广泛的服务器状态信息;SHOW CREATE DATABASE
和SHOW CREATE TABLE
,分别用来显示创建特定数据库或表的MySQL语句;SHOW GRANTS
,用来显示授予用户(所有用户或特定用户)的安全权限;SHOW ERRORS
和SHOW WARNINGS
,用来显示服务器错误或警告消息。前面放置--
--这是一段注释
SELECT num
FROM aa;
最经常使用的SQL语句就是SELECT语句了。它的用途是从一个或多个表中检索信息。为了使用SELECT检索表数据,必须至少给出两条信息——想选择什么,以及从什么地方选择。
子句 | 说明 | 是否必须使用 |
---|---|---|
SELECT | 要返回的列或表达式 | 是 |
FROM | 从中检索数据的表 | 仅在从表选择数据时使用 |
WHERE | 行级过滤 | 否 |
GROUP BY | 分组说明 | 仅在按组计算聚集时使用 |
HAVING | 组级过滤 | 否 |
ORDER BY | 输出排序顺序 | 否 |
LIMIT | 要检索的行数 | 否 |
SELECT prod_name
FROM products;
上述语句利用SELECT语句从products表中检索一个名为prod_name的列。所需的列名在SELECT关键字之后给出,FROM关键字指出从其中检索数据的表名。
【结束SQL语句】多条SQL语句必须以分号(;)分隔。MySQL如同多数DBMS一样,不需要在单条SQL语句后加分号。如果你使用的是mysql命令行,必须加上分号来结束SQL语句。(事实上,即使不一定需要,但加上分号肯定没有坏处。)
【SQL语句和大小写】请注意,SQL语句不区分大小写,因此SELECT与select是相同的。
但许多SQL开发人员喜欢对所有SQL关键字使用大写,而对所有列和表名使用小写,这样做使代码更易于阅读和调试。
【使用空格】在处理SQL语句时,其中所有空格都被忽略。SQL语句可以在一行上给出,也可以分成许多行。多数SQL开发人员认为将SQL语句分成多行更容易阅读和调试(例如上面的语句分成了两行,其实一行也ok)。
在SELECT关键字后给出多个列名,列名之间必须以逗号分隔(一定要在列名之间加上逗号,但最后一个列名后不加)。
SELECT prod_name, prod_name, prod_price
FROM products;
【数据表示】从上述输出可以看到,SQL语句一般返回原始的、无格式的数据。数据的格式化是一个表示问题,而不是一个检索问题。
这可以通过在实际列名的位置使用星号(*)通配符来达到。
SELECT *
FROM products;
PS: 一般,除非你确实需要表中的每个列,否则最好别使用*通配符。虽然使用通配符可能会使你自己省事,不用明确列出所需列,但检索不需要的列通常会降低检索和应用程序的性能
正如所见,SELECT返回所有匹配的行。但是,如果你不想要每个值每次都出现,怎么办?(例如,你查看商品价格,有10个14元,1个12元,你不想知道有几个,只想知道有多少元的)解决办法是使用DISTINCT
关键字,顾名思义,此关键字指示MySQL只返回不同的值。
SELECT DISTINCT vend_id
FROM products;
【注意事项】1、DISTINCT关键字,它必须直接放在列名的前面。
2、DISTINCT关键字应用于所有列而不仅是前置它的列。如果给出SELECT DISTINCT vend_id, prod_price,除非指定的两个列都不同,否则所有行都将被检索出来。即:没法部分使用DISTINCT
SELECT语句返回所有匹配的行,它们可能是指定表中的每个行。为了返回第一行或前几行,可使用LIMIT子句。
SELECT prod_name
FROM products
LIMIT 5;
此语句使用SELECT语句检索单个列。LIMIT 5指示MySQL返回不多于5行。
SELECT prod_name
FROM products
LIMIT 5, 5;
LIMIT 5, 5指示MySQL返回从行5(第四行)开始的5行。第一个数为开始位置,第二个数为要检索的行数。
【注意】行0检索出来的第一行为行0而不是行1。因此,LIMIT 1, 1将检索出第二行而不是第一行。并且MySQL 5支持LIMIT的另一种替代语法。LIMIT4 OFFSET 3意为从行3(第二行)开始取4行,就像LIMIT 3, 4一样。
迄今为止使用的SQL例子只通过列名引用列。也可能会使用完全限定的名字来引用列(同时使用表名和列字)同时,表名也可以是完全限定的,如下所示:
SELECT products.prod_name
FROM crashcourse.products;
这条SQL语句在功能上等于本章最开始使用的那一条语句。
【定义】子句(clause) SQL语句由子句构成,有些子句是必需的,而有的是可选的。一个子句通常由一个关键字和所提供的数据组成。子句的例子:SELECT语句的FROM子句。
为了明确地排序用SELECT语句检索出的数据,可使用ORDER BY
子句。ORDER BY子句取一个或多个列的名字,据此对输出进行排序。
SELECT prod_name, prod_price, prod_name
FROM products
ORDER BY prod_name;
这条语句除了指示MySQL对prod_name列以字母顺序排序数据的ORDER BY子句外,与前面的语句相同。
为了按多个列排序,只要指定列名,列名之间用逗号分开即可(就像选择多个列时所做的那样)。
SELECT prod_name, prod_price, prod_name
FROM products
ORDER BY prod_price , prod_name;
这段代码代码检索3个列,并按其中两个列对结果进行排序——首先按价格,然后再按名称排序。
数据排序不限于升序排序(从A到Z)。这只是默认的排序顺序,还可以使用ORDER BY子句以降序(从Z到A)顺序排序。为了进行降序排序,必须指定DESC
关键字。
SELECT prod_name, prod_price, prod_name
FROM products
ORDER BY prod_price DESC, prod_name;
【降序】DESC关键字只应用到直接位于其前面的列名。在上例中,只对prod_price列指定DESC,对prod_name列不指定。因此,prod_price列以降序排序,而prod_name列(在每个价格内)仍然按标准的升序排序。因此,如果想在多个列上进行降序排序,必须对每个列指定DESC关键字。
【升序】与DESC相反的关键字是ASC(ASCENDING),在升序排序时可以指定它。但实际上,ASC没有多大用处,因为升序是默认的(如果既不指定ASC也不指定DESC,则假定为ASC)。
【区分大小写】在字典(dictionary)排序顺序中,A被视为与a相同,这是MySQL(和大多数数据库管理系统)的默认行为。
【案例】使用ORDER BY
和LIMIT
的组合,能够找出一个列中最高或最低的值。
在SELECT
语句中,数据根据WHERE
子句中指定的搜索条件进行过滤。
WHERE子句在表名(FROM子句)之后给出。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE prod_price = 2.50;
【WHERE的位置】在同时使用ORDER BY
和WHERE
子句时,应该让ORDER BY
位于WHERE
之后,否则将会产生错误。
就是跟上文中的=
一样的操作符
操作符 | 说明 |
---|---|
= | 等于 |
<> 或 != | 不等于 |
< | 小于 |
<= | 小于等于 |
< | 大于 |
<= | 大于等于 |
BETWEEN | 在指定的两个值之间 |
【注意】MySQL在执行匹配时默认不区分大小写
SELECT prod_name, prod_price, prod_name
FROM products
WHERE prod_name = 'fuses';
【引号的使用】单引号用来限定字符串。如果将值与串类型的列进行比较,则需要限定引号。用来与数值列进行比较的值不用引号。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE prod_id <> 1003;
在使用BETWEEN时,必须指定两个值——所需范围的低端值和高端值。这两个值必须用AND关键字分隔。BETWEEN匹配范围中所有的值,包括指定的开始值和结束值。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE prod_price BETWEEN 5 AND 10;
在创建表时,表设计人员可以指定其中的列是否可以不包含值。在一个列不包含值时,称其为包含空值NULL。
SELECT语句有一个特殊的WHERE子句,可用来检查具有NULL值的列。这个WHERE子句就是IS NULL
子句。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE prod_price IS NULL;
NULL 无值(no value),它与字段包含0、空字符串或仅仅包含空格不同。
为了通过不止一个列进行过滤,可使用AND操作符给WHERE子句附加条件。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE vend_id = 1003 AND prod_price <= 10;
AND 用在WHERE子句中的关键字,用来指示检索满足所有给定条件的行。还可以添加多个过滤条件,每添加一条就要使用一个AND。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE vend_id = 1003 OR vend_id = 1004;
OR用在WHERE子句中使用的关键字,用来表示检索匹配任一给定条件的行。
WHERE可包含任意数目的AND和OR操作符。允许两者结合以进行复杂和高级的过滤。SQL(像多数语言一样)在处理OR操作符前,优先处理AND操作符,因此一定要记得使用圆括号明确地分组相应的操作符。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE (vend_id = 1003 OR vend_id = 1004) AND prod_price >= 10;
IN操作符用来指定条件范围,范围中的每个条件都可以进行匹配。IN取合法值的由逗号分隔的清单,全都括在圆括号中。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE vend_id IN (1002, 1003);
ORDER BY prod_name;
IN在WHERE子句中用来指定要匹配值的清单的关键字,功能与OR相当。但比OR执行更快,并且可以包含其他SELECT语句,使得能够更动态地建立WHERE子句。
WHERE子句中的NOT操作符有且只有一个功能,那就是否定它之后所跟的任何条件。
SELECT prod_name, prod_price, prod_name
FROM products
WHERE vend_id NOT IN (1002, 1003);
ORDER BY prod_name;
利用通配符可创建比较特定数据的搜索模式。在这个例子中,如果你想找出名称包含anvil的所有产品,可构造一个通配符搜索模式,找出产品名中任何位置出现anvil的产品。
【定义1】通配符(wildcard) 用来匹配值的一部分的特殊字符。
【定义2】搜索模式(search pattern)由字面值、通配符或两者组合构成的搜索条件。
为在搜索子句中使用通配符,必须使用LIKE
操作符。LIKE指示MySQL,后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。
【定义3】谓词(predicate)。当有人提问到操作符何时不是操作符?答案是在它作为谓词时。从技术上说,LIKE是谓词而不是操作符,虽然最终的结果是相同的。
最常使用的通配符是百分号(%)。在搜索串中,%表示任何字符出现任意次数。%代表搜索模式中给定位置的0个、1个或多个字符。
SELECT prod_id, prod_name
FROM products
WHERE prod_name LIKE 'jet%';
此例子使用了搜索模式'jet%'
。在执行这条子句时,将检索任意以jet
起头的词。
根据MySQL的配置方式,搜索可以是区分大小写的。如果区分大小写,'jet%'与JetPack 1000将不匹配。
SELECT prod_id, prod_name
FROM products
WHERE prod_name LIKE '%anvil%';
搜索模式'%anvil%'
表示匹配任何位置包含文本anvil
的值,而不论它之前或之后出现什么字符。
【注意】1、 尾空格可能会干扰通配符匹配。例如,在保存词anvil 时,如果它后面有一个或多个空格,则子句WHERE prod_name LIKE '%anvil’将不会匹配它们,因为在最后的l后有多余的字符。解决这个问题的一个简单的办法是在搜索模式最后附加一个%。一个更好的办法是使用函数(第11章将会介绍)去掉首尾空格。
2、虽然似乎%通配符可以匹配任何东西,但有一个例外,即NULL。
下划线的用途与%一样,但下划线只匹配单个字符而不是多个字符。
SELECT prod_id, prod_name
FROM products
WHERE prod_name LIKE '_ ton anvil';
与%能匹配0个字符不一样,_总是匹配一个字符,不能多也不能少。
1、 不要过度使用通配符(这个花时间很长)。如果其他操作符能达到相同的目的,应该使用其他操作符。
2、在确实需要使用通配符时,除非绝对有必要,否则不要把它们用在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起来是最慢的。
3、仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
正则表达式是用来匹配文本的特殊的串(字符集合)。
正则表达式用正则表达式语言来建立,正则表达式语言是用来完成刚讨论的所有工作以及更多工作的一种特殊语言。与任意语言一样,正则表达式具有你必须学习的特殊的语法和指令。
【使用MySQL正则表达式】正则表达式的作用是匹配文本,将一个模式(正则表达式)与一个文本串进行比较。MySQL用WHERE
子句对正则表达式提供了初步的支持,允许你指定正则表达式,过滤SELECT
检索出的数据。
下面的语句检索列prod_name包含文本1000的所有行:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000';
(看似与LIKE相同)其实它告诉MySQL:REGEXP后所跟的东西作为正则表达式(与文字正文1000匹配的一个正则表达式)处理。
MySQL中的正则表达式匹配不区分大小写(即,大写和小写都匹配)。为区分大小写,可使用
BINARY
关键字,如:WHERE prod_name REGEXP BINARY 'JetPack .000'
SELECT prod_name
FROM products
WHERE prod_name REGEXP '.000';
ORDER BY prod_name;
它表示匹配任意一个字符,相当于LIKE里面的(_)。
主要体现在不用通配符时(LIKE
匹配整个串而REGEXP
匹配子串):
WHERE prod_name LIKE '1000';//不返回数据
WHERE prod_name REGEXP '1000';//返回一行
LIKE匹配整个列(即内容完全一样才可以)。如果被匹配的文本在列值中出现,LIKE将不会找到它,相应的行也不被返回。
而REGEXP在列值内进行匹配(即内容为字串即可),如果被匹配的文本在列值中出现,REGEXP将会找到它,相应的行将被返回。
不过,虽然它们存在区别,但是REGEXP也是可以用来匹配整个列值的(从而起与LIKE相同的作用)——使用^
和$
定位符(anchor)即可。
为搜索两个串之一(或者为这个串,或者为另一个串),使用|
符号。如:
SELECT prod_name
FROM products
WHERE prod_name REGEXP '1000|2000|3000';
ORDER BY prod_name;
可通过指定一组用[和]
括起来的字符来完成只匹配特定的字符。
SELECT prod_name
FROM products
WHERE prod_name REGEXP '[123] Ton';
ORDER BY prod_name;
这里,使用了正则表达式[123] Ton
。[123]
定义一组字符,它的意思是匹配1或2或3,因此,1 ton
、2 ton
和3 ton
都匹配且返回)。事实上,正则表达式[123]Ton
为[1|2|3]Ton
的缩写,也可以使用后者来表示。
在集合的开始处放置一个
^
表示否定,即[^123] Ton
表示除了1、2、3以外带Ton的东西
集合可用来定义要匹配的一个或多个字符。如[0-9]将匹配数字0到9。此外,范围不一定只是数值的,[a-z]匹配任意字母字符。
SELECT prod_name
FROM products
WHERE prod_name REGEXP '[1-5] Ton';
ORDER BY prod_name;
为了匹配特殊字符,必须用\\
为前导。如\\-
表示查找-
,\\.
表示查找.
。(为了匹配反斜杠\
字符本身,需要使用\\\
。)
【定义】这种处理就是所谓的转义(escaping),正则表达式内具有特殊意义的所有字符都必须以这种方式转义。
\\
也用来引用元字符(具有特殊含义的字符),如下表所列。
元字符 | 说明 |
---|---|
\\f | 换页 |
\\n | 换行 |
\\r | 回车 |
\\t | 制表 |
\\v | 纵向制表 |
多数正则表达式实现使用单个反斜杠转义特殊字符,以便能使用这些字符本身。但MySQL要求两个反斜杠(MySQL自己解释一个,正则表达式库解释另一个)。
存在找出你自己经常使用的数字、所有字母字符或所有数字字母字符等的匹配。可以使用预定义的字符集。
可以用下表列出的正则表达式重复元字符来完成。
元 字 符 | 说 明 |
---|---|
* | 0个或多个匹配 |
+ | 1个或多个匹配(等于{1,} ) |
? | 0个或1个匹配(等于{0,1} ) |
{n} | 指定数目的匹配 |
{n,} | 不少于指定数目的匹配 |
{n,m} | 匹配数目的范围(m不超过255) |
【案例1】下面的\\([0-9] sticks?\\)
里,首先\\(
是正则表达式中的(
后面的\\)
同理;其次,[0-9]
匹配任意数字(这个例子中为1和5),而最后就是我们的元字符?
,sticks?
匹配stick
和sticks
(s
后的?
使s
可选,因为?
匹配它前面的任何字符的0次或1次出现)
【案例2】[:digit:]
匹配任意数字,因而它为数字的一个集合。{4}
确切地要求它前面的字符(任意数字)出现4次,所以[[:digit:]]{4}
匹配连在一起的任意4位数字。
为了匹配特定位置的文本,需要用到下表的定位符。而这里的^
和$
可以使REGEXP的作用与LIKE一样。
元字符 | 说明 |
---|---|
^ | 文本的开始 |
$ | 文本的结尾 |
[[:<:]] | 词的开始 |
[[:>:]] | 词的结尾 |
^有两种用法。在集合中(用[和]定义),用它来否定该集合,否则,用来指串的开始处。
SELECT prod_name
FROM products
WHERE prod_name REGEXP '^[0-9\\.]';
ORDER BY prod_name;
其中^[0-9\\.]
只在.
或任意数字为串中第一个字符时才匹配它们(为什么是点或数字,是因为[]
符号——可见匹配几个字符之一的内容)。
存储在数据库表中的数据一般不是应用程序所需要的格式。而计算字段便可以直接从数据库中检索出转换、计算或格式化过的数据;计算字段是运行时在SELECT语句内创建的。
解决办法是把两个列拼接起来。在MySQL的SELECT
语句中,可使用Concat()
函数来拼接两个列。
多数DBMS使用
+
或||
来实现拼接,MySQL则使用Concat()函数来实现。当把SQL语句转换成MySQL语句时一定要把这个区别铭记在心。
SELECT Concat(prod_name, '(', prod_country, ')')
FROM products
ORDER BY prod_name;
Concat()
拼接串,即把多个串连接起来形成一个较长的串。Concat()
需要一个或多个指定的串,各个串之间用逗号
分隔。此时返回的检索内容,是经过处理后的计算字段。
RTrim()
函数来完成删除右侧多余的空格;LTrim()
去掉串左侧多余的空格;Trim()
去掉串左右两边的空格。
SELECT Concat(RTrim(prod_name), '(', Rtrim(prod_country), ')')
FROM products
ORDER BY prod_name;
上面也提到过,计算字段是新创建的,其并不是实际的表列,那么客户机需要引用,便需要给其取一个名字。(其他用途:常见的用途包括在实际的表列名包含不符合规定的字符(如空格)时重新命名它,在原来的名字含混或容易误解时扩充它,等等。)
【定义】别名(alias)是一个字段或值的替换名。有时也称为导出列(derived column),别名用AS
关键字赋予。
SELECT Concat(prod_name, '(', prod_country, ')') AS pord_title
FROM products
ORDER BY prod_name;
它指示SQL创建一个包含指定计算的名为pord_title
的计算字段。任何客户机应用都可以按名引用这个列,就像它是一个实际的表列一样。
计算字段的另一常见用途是对检索出的数据进行算术计算。
SELECT prod_name,
quantity,
item_price,
quantity * item_price AS expanded_price
FROM products
输出中显示的expanded_price
列为一个计算字段,此计算为 q u a n t i t y ∗ i t e m p r i c e quantity*item_{price} quantity∗itemprice。客户机应用现在可以使用这个新计算列,就像使用其他列一样。
函数一般是在数据上执行的,它给数据的转换和处理提供了方便。但函数的可移植性并不好,因此如果你决定使用函数,应该保证做好代码注释。
常用的文本处理函数如下:
函 数 | 说 明 |
---|---|
Left() | 返回串左边的字符 |
Length() | 返回串的长度 |
Locate() | 找出串的一个子串 |
Lower() | 将串转换为小写 |
Right() | 返回串右边的字符 |
Soundex() | 返回串的SOUNDEX值 |
SubString() | 返回子串的字符 |
Upper() | 将串转换为大写 |
其中,SOUNDEX是一个将任何文本串转换为描述其语音表示的字母数字模式的算法。
日期和时间采用相应的数据类型和特殊的格式存储,以便能快速和有效地排序或过滤,并且节省物理存储空间。
函 数 | 说 明 |
---|---|
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两个日期之差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期的年份部分 |
在使用时,首先需要注意的是MySQL使用的日期格式:日期必须为格式yyyy-mm-dd
。
【案例1】提取订单记录的order_date为2005-09-01
的行:
SELECT cust_id,
FROM orders
WHERE DATA(order_data) = '2005-09-01';
此处用DATA的原因是:若没有DATA,而存 储 的 order_date 值 为2005-09-01 11:30:05
,则直接WHERE order_date = '2005-09-01'
会匹配失败。
【案例2】检索出2005年9月下的所有订单:
SELECT cust_id,
FROM orders
WHERE Year(order_data) = '2005' AND Month(order_data) = 9;
这些函数一般主要用于代数、三角或几何运算,因此没有串或日期—时间处理函数的使用那么频繁。
常用数值处理函数如下:
函 数 | 说 明 |
---|---|
Abs() | 返回一个数的绝对值 |
Cos() | 返回一个角度的余弦 |
Exp() | 返回一个数的指数值 |
Mod() | 返回除操作的余数 |
Pi() | 返回圆周率 |
Rand() | 返回一个随机数 |
Sin() | 返回一个角度的正弦 |
Sqrt() | 返回一个数的平方根 |
Tan() | 返回一个角度的正切 |
【定义】聚集函数(aggregate function) 运行在行组上,计算和返回单个值的函数。
函 数 | 说 明 |
---|---|
AVG() | 返回某列的平均值(该函数忽略列值为NULL的行且只用于单个列,但可以通过组合WHERE 来计算某列部分行的平均值) |
COUNT() | 返回某列的行数(COUNT(*) 对表中行的数目进行计数(包括空行和非空行);COUNT(column) 对特定列中具有值的行进行计数,忽略NULL值) |
MAX() | 返回某列的最大值(忽略列值为NULL的行) |
MIN() | 返回某列的最小值(忽略列值为NULL的行) |
SUM() | 返回某列值之和(同AVG函数的用法) |
【案例】合计每项物品的item_price*quantity
,得出总的订单金额:
SELECT SUM(quantity * item_price) AS total_price
FROM products
故SUM()也可以用来合计计算值。
利用标准的算术操作符,所有聚集函数都可用来执行多个列上的计算。
以上5个聚集函数都可以如下使用:
【案例】当平均值只考虑各个不同的价格:
SELECT AVG(DISTINCT item_price) AS avg_price
FROM products
【易错点】DISTINCT必须使用列名,不能用于计算或表达式。因此不能用于COUNT(*)
即不允许出现COUNT(DISTINCT)
SELECT语句可根据需要包含多个聚集函数。这样就返回多个列
【案例】输入为:
而输出为:
分组允许把数据分为多个逻辑组,以便能对每个组进行聚集计算。
分组是在SELECT
语句的GROUP BY
子句中建立的。
SELECT vend_id, COUNT(*) AS num_prods
FROM products
GROUP BY vend_id;
上面的SELECT语句指定了两个列,vend_id包含产品供应商的ID,num_prods为计算字段(用COUNT(*)函数建立)。
GROUP BY
子句指示MySQL按vend_id
排序并分组数据。这导致对每个vend_id
而不是整个表计算num_prods
一次。从输出中可以看到,供应商1001有3个产品,供应商1002有2个产品,供应商1003有7个产品,而供应商1005有2个产品。
在具体使用GROUP BY子句前,需要知道一些重要的规定:
使用WITH ROLLUP关键字,可以得到每个分组以及每个分组汇总级别(针对每个分组)的值。如
GROUP BY vend_id WITH ROLLUP
;
MySQL允许过滤分组,规定包括哪些分组,排除哪些分组。
MySQL为此目的提供了另外的子句,那就是HAVING
子句。WHERE子句都可以用HAVING来替代。唯一的差别是WHERE过滤行,而HAVING过滤分组。
【案例1】列出至少有两个订单的所有顾客
SELECT vend_id, COUNT(*) AS num_prods
FROM products
GROUP BY vend_id
HAVING COUNT(*) >= 2;
有时候,WHERE和HAVING可能会同时出现,如
【案例2】列出具有2个(含)以上、价格为10(含)以上的产品的供应商
SELECT vend_id, COUNT(*) AS num_prods
FROM products
WHERE prod_price >= 10
GROUP BY vend_id
HAVING COUNT(*) >= 2;
ORDER BY | GROUP BY |
---|---|
排序产生的输出 | 分组行。但输出可能不是分组的顺序 |
任意列都可以使用(甚至非选择的列也可以使用) | 只可能使用选择列或表达式列,而且不可以用别名 |
不一定需要 | 如果与聚集函数一起使用列(或表达式),则必须使用 |
一般在使用GROUP BY子句时,应该也给出ORDER BY子句。这是保证数据正确排序的唯一方法。
SQL还允许创建子查询(subquery),即嵌套在其他查询中的查询。
在SELECT语句中,子查询总是从内向外处理。
以一个案例为准:一共有三个表:
orders表存储每个订单的订单号、客户ID、订单日期。
orderitems表存储各订单的物品。
customers表存储客户的ID所对应的客户信息。
【案例】需要列出订购物品TNT2的所有客户。在这里我们使用子查询将对三个表的查询组合成一个语句。(下章的联结可以更好地做到)
SELECT cust_name, cust_contact
FROM customers
WHERE cust_id IN (SELECT cust_id
FROM orders
WHERE order_num IN (SELECT order_num
FROM orderitems
WHERE prod_id = 'TNT2'));
在WHERE子句中使用子查询能够编写出功能很强并且很灵活的SQL语句。对于能嵌套的子查询的数目没有限制,不过在实际使用时由于性能的限制,不能嵌套太多的子查询。
使用子查询的另一方法是创建计算字段。
【案例】显示customers表中每个客户的订单总数。
SELECT cust_name,
cust_state,
(SELECT COUNT(*)
FROM orders
WHERE orders.cust_id = customers.cust_id) AS orders
FROM customers
ORDER BY cust_name ;
这里用到了第四章的《完全限定的表名》这种类型的子查询称为相关子查询。
【定义】相关子查询(correlated subquery) 涉及外部查询的子查询。
输出结果如下:
【定义1】关系表的设计就是要保证把信息分解成多个表,一类数据一个表。
各表通过某些常用的值(即关系设计中的关系(relational))互相关联。就如同上一张的题目,根据客户信息,其customers和orders是关系表。
【定义2】外键(foreign key)为某个表中的一列,它包含另一个表的主键值,定义了两个表之间的关系。
【定义3】可伸缩性(scale)能够适应不断增加的工作量而不失败。设计良好的数据库或应用程序称之为可伸缩性好(scale well)。而关系数据库的可伸缩性远比非关系数据库要好。
【定义4】联结是一种机制,用来在一条SELECT语句中关联表,因此称之为联结。
联结的创建非常简单,规定要联结的所有表以及它们如何关联即可。
SELECT prod_name,prod_price, vend_name
FROM vendors, products
WHERE vendors.vend_id = products.vend_id
ORDER BY vend_name, prod_name;
注意这里的SELECT
中的三个列共属于两个表中,而FROM
便指明了这条SELECT
语句联结的两个表的名字。【关键】这两个表用WHERE
子句正确联结,WHERE子句指示MySQL匹配vendors
表中的vend_id
和products
表中的vend_id
。其中由于存在二义性,因此必须使用《完全限定列名》。
但若没有联结条件的两个表强行用上面的SELECT和FROM进行联结,结果就是会返回笛卡尔积。
【定义】笛卡儿积(cartesian product)由没有联结条件的表关系返回的结果为笛卡儿积。检索出的行的数目将是第一个表中的行数乘以第二个表中的行数。
PS:因此应该保证所有联结都有WHERE子句,否则MySQL将返回比想要的数据多得多的数据。
上面的联结称为等值联结(equijoin),它基于两个表之间的相等测试。这种联结也称为内部联结。也可使用INNER JOIN
来代替WHERE来创造联结。如下面语句等同于上面的语句:
SELECT prod_name,prod_price, vend_name
FROM vendors INNER JOIN products
ON vendors.vend_id = products.vend_id;
这里,两个表之间的关系是FROM子句的组成部分,以INNER JOIN指定。在使用这种语法时,联结条件用特定的ON子句而不是WHERE子句给出。传递给ON的实际条件与传递给WHERE的相同。
SQL对一条SELECT语句中可以联结的表的数目没有限制。创建联结的基本规则也相同。
【案例】正如第14章所述,子查询并不总是执行复杂SELECT操作的最有效的方法,下面是使用联结的相同查询:需要列出订购物品TNT2的所有客户。
SELECT cust_name, cust_contact
FROM customers, orders, orderitems
WHERE customers.cust_id = orders.cust_id
AND orders.order_num = orderitems.order_num
AND prod_id = 'TNT2';
这里有3个WHERE子句条件。前两个关联联结中的表,后一个过滤产品TNT2的数据。
别名除了用于列名和计算字段外,SQL还允许给表名起别名。
SELECT cust_name, cust_contact
FROM customers AS c, orders AS o, orderitems AS oi
WHERE c.cust_id = o.cust_id
AND o.order_num = oi.order_num
AND prod_id = 'TNT2';
在此例子中,表别名只用于WHERE子句。但是,表别名不仅能用于WHERE子句,它还可以用于SELECT的列表、ORDER BY子句以及语句的其他部分。
【注意与列别名的区别】表别名只在查询执行中使用,表别名不返回到客户机。
前面的只是内部联结或等值联结(equijoin)的简单联结,还有三种高级联结:
【案例】假如你发现某物品(其ID为DTNTR)存在问题,因此想知道生产该物
品的供应商生产的其他物品是否也存在这些问题。(而根据ID找到供货商和根据供货商找到所有的ID是在同一个表下的)
SELECT p1.prod_id, p1.prod_name
FROM products AS p1, products AS p2
WHERE p1.vend_id = p2.vend_id
AND p2.prod_id = 'DTNTR';
SELECT语句使用p1前缀明确地给出所需列的全名。如果不这样,MySQL将返回错误,因为分别存在两个名为prod_id、prod_name的列。MySQL不知道想要的是哪一个列(即使它们事实上是同一个列)。【步骤】WHERE(通过匹配p1中的vend_id和p2中的vend_id)首先联结两个表,然后按第二个表中的prod_id过滤数据,返回所需的数据(即便他们是同一个表)。
【通常用自联结而不用子查询】自联结通常作为外部语句用来替代从相同表中检索数据时使用的子查询语句。
标准的联结(前一章中介绍的内部联结)返回所有数据,甚至相同的列多次出现。自然联结排除多次出现,使每个列只返回一次(毕竟有联结关系的表可能不止一列有关系,可能多个列都有关系,都存在重复)。
事实上,迄今为止我们建立的每个内部联结都是自然联结,很可能我们永远都不会用到不是自然联结的内部联结。
许多联结将一个表中的行与另一个表中的行相关联。但有时候会需要包含没有关联行的那些行。联结包含了那些在相关表中没有关联行的行。这种类型的联结称为外部联结。
例如:
FROM
中的关键字OUTER JOIN
来指定联结的类型,并且在使用OUTER JOIN
语法时,必须使用RIGHT
或LEFT
关键字指定包括其所有行的表(RIGHT指出的是OUTER JOIN右边的表,而LEFT指出的是OUTER JOIN左边的表)。SELECT customer.cust_id, orders.order_num
FROM customer LEFT OUTER JOIN orders
ON customer.cust_id = orders.cust_id;
【案例】检索所有客户及每个客户下的订单数
SELECT customer.cust_name, customer.cust_id, COUNT(orders.order_num) AS num_ord
FROM customer LEFT OUTER JOIN orders
ON customer.cust_id = orders.cust_id
GROUP BY customer.cust_id;
此SELECT语句使用INNER JOIN将customers和orders表互相关联。GROUP BY
子句按客户分组数据,因此,函数调用 COUNT (orders.order_num)
对每个客户的订单计数,将它作为num_ord
返回。
MySQL也允许执行多个查询(多条SELECT语句),并将结果作为单个查询结果集返回。这些组合查询通常称为并(union)或复合查询(compound query)。以下两种需要使用组合查询:
可用UNION
操作符来组合数条SQL查询。
UNION的使用很简单。所需做的只是给出每条SELECT语句,在各条语句之间放上关键字UNION。
【案例】假如需要价格小于等于5的所有物品的一个列表,而且还想包括供应商1001和1002生产的所有物品(不考虑价格)——该题其实也可以靠WHERE
和OR
配合完成,但 这里用UNION
解决!
SELECT vend_id, prod_id, prod_price
FROM products
WHERE prod_price <=5 ;
UNION
SELECT vend_id, prod_id, prod_price
FROM products
WHERE vend_id IN (1001, 1002);
【PS】虽然本例是对单个表用UNION,但是对多个表同样可以UNION返回成一个结果
UNION
从查询结果集中自动去除了重复的行(换句话说,它的行为与单条SELECT
语句中使用多个WHERE
子句条件一样)。
如果想返回所有匹配行,可使用UNION ALL
而不是UNION
。
在用UNION组合查询时,只能使用一条ORDER BY
子句,它必须出现在最后一条SELECT
语句之后。
在使用全文本搜索时,MySQL不需要分别查看每个行,不需要分别分析和处理每个词。
其只需要创建指定列中各词的一个索引,搜索可以针对这些词进行。
为了进行全文本搜索,必须索引被搜索的列,而且要随着数据的改变不断地重新索引(这个过程MySQL会自动地进行)。
一般在创建表时启用全文本搜索。CREATE TABLE
语句接受FULLTEXT
子句,该子句给出被索引列的一个逗号分隔的列表。如FULLTEXT(note_text)
则先对note_text的列进行索引,以便后序对该列进行全文本搜索。
PS:不要在导入数据时使用FULLTEXT
,毕竟每次重新索引都耗费时间。
在索引之后,结合WHERE
关键字使用两个函数Match()
和Against()
执行全文本搜索,其中Match()
指定被搜索的列,Against()
指定要使用的搜索表达式。
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit');
【三点注意】1、Match
需要严格与当初定义的FULLTEXT
内容一样,如果指定多个列,则必须列出它们(而且次序正确)
2、除非使用BINARY方式,否则全文本搜索不区分大小写
3、虽然上面用WHERE
+LIKE'%rabbit%'
也能办到,但速度慢,并且其返回的次序不同,全文本搜索的一个重要部分就是以文本匹配的良好程度对结果排序,具有较高等级的行先返回!
查询扩展用来设法放宽所返回的全文本搜索结果的范围。
在使用查询扩展时,MySQL对数据和索引进行两遍扫描来完成搜索:
Against()
函数里面加入关键字WITH QUERY EXPANSION
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('rabbit' WITH QUERY EXPANSION);
MySQL支持全文本搜索的另外一种形式,称为布尔方式(boolean mode)。该方式即使没有定义FULLTEXT索引,也可以使用该方式进行搜索。但这是一种非常缓慢的操作。
【案例】为了匹配包含heavy但不包含任意以rope开始的词的行,可使用以下查询:
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy -rope*' IN BOOLEAN MODE);
其中,-rope*
明确地指示MySQL排除包含rope*
。
下标将列出所有布尔操作符(操作符都应该放在每个词的前面)
布尔操作符 | 说 明 |
---|---|
+ | 包含,词必须存在 |
- | 排除,词必须不出现 |
> | 包含,而且增加等级值 |
< | 包含,且减少等级值 |
() | 把词组成子表达式(允许这些子表达式作为一个组被包含、排除、排列等) |
~ | 取消一个词的排序值 |
* | 词尾的通配符 |
“” | 定义一个短语(与单个词的列表不一样,它匹配整个短语以便包含或排除这个短语) |
【案例–易错点】注意这个案例,若没有指定操作符时,空格表示或的意思:
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('heavy rope' IN BOOLEAN MODE);
这个搜索匹配包含heavy和rope至少一个词的行!
SELECT note_text
FROM productnotes
WHERE Match(note_text) Against('"heavy rope"' IN BOOLEAN MODE);
这个才是搜索heavy rope这个词!
排列而不排序:在布尔方式中,不按等级值降序排序返回的行。