mysql 必知必会
第 4 章—检索数据
检索不同的行【去重】
- 解决办法是使用 DISTINCT 关键字,顾名思义,此关键字指示 MySQL 只返回不同的值。
SELECT DISTINCT vend_id FROM products限制结果
- LIMIT 5, 5 指示 MySQL 返回从行 5 开始的 5 行。第一个数为开始位置,第二个数为要检索的行数。
SELECT vend_id FROM products LIMIT 5,5;第 5 章—排序检索数据
默认排序【ORDER BY ASC】
- 为了明确地排序用 SELECT 语句检索出的数据,可使用
ORDER BY子句。ORDER BY子句取一个或多个列的名字,据此对输出进行排序。请看下面的例子:
SELECT prod_name FROM products ORDER BY prod_name;📢
通常,ORDER BY 子句中使用的列将 是为显示所选择的列。但是,实际上并不一定要这样,用非检索的列排序数据是完全合法的。
按多个列排序
- 首先按价格,然后再按名称排序。
SELECT * FROM products ORDER BY prod_price,prod_name;指定排序方向
- 数据排序不限于升序排序(从 A 到 Z)。这只是 默认 的排序顺序,还可 以使用
ORDER BY子句以降序(从 Z 到 A)顺序排序。为了进行降序排序, 必须指定DESC关键字。下面的例子按价格以降序排序产品(最贵的排在最前面):
SELECT * FROM products ORDER BY prod_price DESC;- 但是,如果打算用多个列排序怎么办?下面的例子以产品价格降序排序产品 (最贵的在最前面),然后再对产品名排序:
SELECT * FROM products ORDER BY prod_price DESC, prod_name;DESC 关键字只应用到直接位于其前面的列名。在上例中,只对 prod_price 列指定 DESC,对 prod_name 列不指定。因此, prod_price 列以降序排序,而 prod_name 列(在每个价格内)仍然按标准 的升序排序。
如果想在多个列上进行降序排序,必须对每个列指定 DESC 关键字。
使用 ORDER BY 和 LIMIT 的组合,能够找出一个列中最高或最低的值。 下面的例子演示如何找出最昂贵物品的值:
SELECT * FROM products ORDER BY prod_price DESC LIMIT 1;📢
在给出 ORDER BY 子句时,应该保证它 位于 FROM 子句之后。如果使用 LIMIT,它必须位于 ORDER BY 之后。使用子句的次序不对将产生错误消息。
区分大小写和排序顺序
🌈
在对文本性的数据进行排序时,A 与 a 相同吗?a 位于 B 之前还是位于 Z 之后?这些问题不是理论问题,其答案取决于数据库如何设置。 在字典(dictionary)排序顺序中,A 被视为与 a 相同,这是 MySQL (和大多数数据库管理系统)的默认行为。但是,许多数据库 管理员能够在需要时改变这种行为(如果你的数据库包含大量 外语字符,可能必须这样做)。 这里,关键的问题是,如果确实需要改变这种排序顺序,用简 单的 ORDER BY 子句做不到。
第 6 章—过滤数据
WHERE 子句操作符
| 操作符 | 描述 |
|---|---|
| = | 等于 |
| <> 或 != | 不等于 |
| > | 大于 |
| < | 小于 |
| >= | 大于等于 |
| <= | 小于等于 |
| BETWEEN | 在某个范围内 |
| LIKE | 模糊匹配 |
| IN | 在一组值中 |
| IS NULL | 为空 |
| IS NOT NULL | 不为空 |
| AND | 逻辑与 |
| OR | 逻辑或 |
| NOT | 逻辑非 |
不匹配检查
- 以下例子列出不是由供应商 1003 制造的所有产品:
SELECT * FROM products WHERE vend_id <> 1003;
SELECT * FROM products WHERE vend_id != 1003;范围值检查
- 下面的例子说明如何使用 BETWEEN 操作符,它检索价格在 5 美元和 10 美元之间的所有产品:
SELECT * FROM products WHERE prod_price BETWEEN 5 AND 10;空值检查
- 在创建表时,表设计人员可以指定其中的列是否可以不包含值。在 一个列不包含值时,称其为包含空值 NULL。
- NULL 无值(no value),它与字段包含 0、空字符串或仅仅包含空格不同。
- SELECT 语句有一个特殊的 WHERE 子句,可用来检查具有 NULL 值的列。 这个 WHERE 子句就是 IS NULL 子句。
🌈
NULL 与不匹配 在通过过滤选择出不具有特定值的行时,你可能希望返回具有 NULL 值的行。但是不行。因为未知具有特殊的含义,数据库不知道它们是否匹配,所以在匹配过滤或不匹配过滤时不返回它们。 因此在过滤数据时,一定要验证返回数据中确实给出了被过滤列具有 NULL 的行。
第 7 章—数据过滤
组合 WHERE 子句
🌈
第 6 章中介绍的所有 WHERE 子句在过滤数据时使用的都是单一的条 件。为了进行更强的过滤控制,MySQL 允许给出多个 WHERE 子句。这些子句可以两种方式使用:以 AND 子句的方式或 OR 子句的方式使用。
AND 操作符
- 为了通过不止一个列进行过滤,可使用
AND操作符给WHERE子句附加 条件。下面的代码给出了一个例子:
SELECT * FROM products WHERE vend_id = 1003 AND prod_price <= 10;- 此 SQL 语句检索由供应商 1003 制造 且 价格小于等于 10 美元的所有产品信息。
- 上述例子中使用了只包含一个关键字 AND 的语句,把两个过滤条件组 合在一起。还可以添加多个过滤条件,每添加一条就要使用一个 AND。
OR 操作符
OR操作符与AND操作符不同,它指示MySQL检索匹配任一条件的行。
SELECT * FROM products WHERE vend_id = 1003 OR prod_price <= 10;- 此 SQL 语句检索由供应商 1003 制造 或者 价格小于等于 10 美元的所有产品信息。
计算次序
- WHERE 可包含任意数目的 AND 和 OR 操作符。允许两者结合以进行复杂和高级的过滤。
- 假如需要列出价格为 10 美元(含)以上且由 1002 或 1003 制 造的所有产品。下面的
SELECT语句使用 AND 和 OR 操作符的组合建立了一个 WHERE 子句:
SELECT * FROM products WHERE vend_id = 1002 OR vend_id = 1003 AND prod_price >= 10;- 返回的行中会有两行价格小于 10 美元,显然, 返回的行未按预期的进行过滤。为什么会这样呢?原因在于计 算的次序。SQL(像多数语言一样)在处理
OR操作符前,优先处理AND操作符。当SQL看到上述WHERE子句时,它理解为由供应商 1003 制造的任何价格为 10 美元(含)以上的产品,或者由供应商 1002 制造的任何产品,而不管其价格如何。换句话说,由于AND在计算次序中优先级更高,操作符被错误地组合了。 - 此问题的解决方法是使用圆括号明确地分组相应的操作符。请看下 面的
SELECT语句:
SELECT * FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10;🌈 在 WHERE 子句中使用圆括号
任何时候使用具有 AND 和 OR 操作 符的 WHERE 子句,都应该使用圆括号明确地分组操作符。不要过分依赖默认计算次序,即使它确实是你想要的东西也是如 此。使用圆括号没有什么坏处,它能消除歧义。
IN 操作符
- 圆括号在
WHERE子句中还有另外一种用法。IN 操作符用来指定条件范 围,范围中的每个条件都可以进行匹配。IN 取合法值的由逗号分隔的清 单,全都括在圆括号中。下面的例子说明了这个操作符:
SELECT * FROM products WHERE vend_id IN (1002,1003) ORDER BY prod_price;- 此 SELECT 语句检索供应商 1002 和 1003 制造的所有产品。IN 操作符能完成与 OR 相同的功能。
🌈 为什么要使用 IN 操作符?其优点具体如下:
- 在使用长的合法选项清单时,IN 操作符的语法更清楚且更直观。
- 在使用 IN 时,计算的次序更容易管理(因为使用的操作符更少)。
- IN 操作符一般比 OR 操作符清单执行更快。
- IN 的最大优点是可以包含其他 SELECT 语句,使得能够更动态地建立
WHERE子句。
NOT 操作符
WHERE子句中的NOT操作符有且只有一个功能,那就是否定它之后所跟的任何条件。下面的例子说明
NOT的使用。为了列出除 1002 和 1003 之外的所有供应 商制造的产品
SELECT * FROM products WHERE vend_id not IN (1002,1003) ORDER BY prod_price;第 8 章—用通配符进行过滤
- 为在搜索子句中使用通配符,必须使用
LIKE操作符。LIKE指示MySQL, 后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。
百分号(%)通配符
最常使用的通配符是百分号
%。在搜索串中,%表示出现为了找出所有以词
jet起头的产品,可使用以下SELECT语句:
SELECT * FROM products WHERE prod_name LIKE 'jet%'区分大小写
根据 MySQL 的配置方式,搜索可以是区分大小写的。如果区分大小写,'jet%'与 JetPack 1000 将不匹配。
- 通配符可在搜索模式中任意位置使用,并且可以使用多个通配符。下面的例子使用两个通配符,它们位于模式的两端:
SELECT * FROM products WHERE prod_name LIKE '%anvil%'- 除了一个或多个字符外,% 还能匹配 0 个字符。% 代表搜索模式中给定位置的。
!注意尾空格
尾空格可能会干扰通配符匹配。例如,在保存词 anvil 时,如果它后面有一个或多个空格, 则子句 WHERE prod_name LIKE '%anvil' 将不会匹配它们,因为在最后的 l 后有多余的字符。 解决这个问题的一个简单的办法是在搜索模式最后附加一个%。 一个更好的办法是使用函数(第 11 章将会介绍)去掉首尾空格。
!注意 NULL
虽然似乎%通配符可以匹配任何东西,但有一个例外,即 NULL。即使是 WHERE prod_name LIKE '%'也不能匹配用值 NULL 作为产品名的行。
下划线(_)通配符
- 另一个有用的通配符是下划线 _ 。下划线的用途与%一样,但下划线只匹配。
- 与 % 能匹配 0 个字符不一样,_总是匹配一个字符,不能多也不能少。
使用通配符的技巧
MySQL 的通配符很有用。但这种功能是有代价的: 通配符搜索的处理一般要比前面讨论的其他搜索所花时间更长。 这里给出一些使用通配符要记住的技巧。
- 不要过度使用通配符。如果其他操作符能达到相同的目的,应该 使用其他操作符。
- 在确实需要使用通配符时,除非绝对有必要,否则不要把它们用 在搜索模式的开始处。把通配符置于搜索模式的开始处,搜索起来是最慢的。
第 9 章—用正则表达式进行搜索
MySQL用WHERE子句对正则表达式提供了初步的支持,允许你指定正则表达式,过滤SELECT检索出的数据。
基本字符匹配
- 我们从一个非常简单的例子开始。下面的语句检索列
prod_name包含文本1000的所有行:
SELECT * FROM products WHERE prod_name REGEXP '1000'除关键字 LIKE 被 REGEXP 替代外,这条语句看上去非常像使用 LIKE 的语句(第 8 章)。它告诉 MySQL:REGEXP 后所跟的东西作为正则表达式处理。
为什么要费力地使用正则表达式?在刚才的例子中,正则表达式确实没有带来太多好处(可能还会降低性能),不过,请考虑下面的例子:
SELECT * FROM products WHERE prod_name REGEXP '.000'- 这里使用了正则表达式 .000 。. 是正则表达式语言中一个特殊的字符。它表示匹配任意一个字符,因此,1000 和 2000 都匹配且返回。
!LIKE 与 REGEXP
- 在 LIKE 和 REGEXP 之间有一个重要的差别。请看以下两条语句:
SELECT * FROM products WHERE prod_name LIKE '1000'SELECT * FROM products WHERE prod_name REGEXP '1000'- 如果执行上述两条语句,会发现第一条语句不返回数据,而第二条语句返回一行。为什么?
- LIKE 必须完全匹配整个列值(除非使用通配符)。
- 而 REGEXP 在列值内进行匹配,如果被匹配的文本在列值中出现,REGEXP 将会找到它,相应的行将被返回(部分匹配)
- 那么,REGEXP 能不能用来匹配整个列值(从而起与 LIKE 相同的作用)?答案是肯定的,使用 ^ 和 $ 定位符(anchor)即可
!匹配不区分大小写
- MySQL 中的正则表达式匹配(自版本 3.23.4 后)不区分大小写(即,大写和小写都匹配)。
- 为区分大小写,可使用 关键字,如 WHERE prod_name REGEXP BINARY 'JetPack .000'。
进行 OR 匹配
- 为搜索两个串之一(或者为这个串,或者为另一个串)使用 | 如下所示:
SELECT * FROM products WHERE prod_name REGEXP '1000|2000';语句中使用了正则表达式 1000|2000。| 为正则表达式的 OR 操作符。它表示匹配其中之一,因此 1000 和 2000 都匹配并返回。
使用|从功能上类似于在 SELECT 语句中使用 OR 语句,多个 OR 条件可并入单个正则表达式。
匹配几个字符之一
- 匹配几个字符之一可通过指定一组用[和]括起来的字符来完成:
SELECT * FROM products WHERE prod_name REGEXP '[123] ton';- 这里,使用了正则表达式[123] Ton。[123]定义一组字符,它的意思是匹配 1 或 2 或 3,因此,1 ton 和 2 ton 都匹配且返回(数据库里没有 3 ton)。
^
- 字符集合也可以被否定,即它们将匹配除指定字符外的任何东西。
- 为否定一个字符集,在集合的开始处放置一个 ^ 即可。
- [123]匹配字符 1、2 或 3,但[^123]却匹配除这些字符外的任何东西。
匹配范围
- 集合可用来定义要匹配的一个或多个字符。例如,下面的集合将匹配数字 0 到 9:
[0123456789] - 为简化这种类型的集合,可使用-来定义一个范围。下面的式子功能上等同于上述数字列表:
[0-9] - 范围不限于完整的集合,
[1-3]和[6-9]也是合法的范围。此外,范围不一定只是数值的,[a-z]匹配任意字母字符。
匹配特殊字符
- 为了匹配特殊字符,必须用
\\为前导。\\-表示查找-,\\.表示查找.。
SELECT * FROM products WHERE prod_name REGEXP '\\.';- 这种处理就是所谓的转义(escaping),正则表达式内具有特殊意义的所有字符都必须以这种方式转义。
- 这包括
.、|、[]以及迄今为止使用过的其他特殊字符。 - 为了匹配反斜杠
\字符本身,需要使用\\。
\或\\?
- 多数正则表达式实现使用单个反斜杠转义特殊字符,以便能使用这些字符本身。
- 但
MySQL要求两个反斜杠MySQL自己解释一个,正则表达式库解释另一个。
匹配 字符类
INFO
- 存在找出你自己经常使用的数字、所有字母字符或所有数字字母字符等的匹配。
- 为更方便工作,可以使用预定义的字符集,称为字符类(character class)。
| POSIX 字符类 | 含义 | 等效表示 |
|---|---|---|
[:alnum:] | 字母和数字字符 | [A-Za-z0-9] |
[:alpha:] | 字母字符 | [A-Za-z] |
[:digit:] | 数字字符 | [0-9] |
[:xdigit:] | 十六进制数字字符 | [A-Fa-f0-9] |
[:lower:] | 小写字母字符 | [a-z] |
[:upper:] | 大写字母字符 | [A-Z] |
[:space:] | 空白字符 | 包括空格、制表符等 |
[:blank:] | 空白字符中的空格和制表符 |
SELECT * FROM products WHERE prod_name REGEXP '[:digit:]';重复元字符(量词)
| 重复元字符 | 含义 |
|---|---|
* | 匹配前一个元素零次或多次 |
+ | 匹配前一个元素一次或多次 (等于{1,}) |
? | 匹配前一个元素零次或一次 (等于{0,1}) |
{n} | 匹配前一个元素恰好 n 次 |
{n,} | 匹配前一个元素至少 n 次 |
{n,m} | 匹配前一个元素至少 n 次且不超过 m 次 (m 不超过 255) |
- 下面举几个例子。
SELECT * FROM products WHERE prod_name REGEXP '\\([0-9] sticks?\\)';
TNT1 1003 TNT (1 stick) 2.50 TNT, red, single stick
TNT2 1003 TNT (5 sticks) 10.00 TNT, red, pack of 10 sticks解析
- 正则表达式
\\([0-9] sticks?\\)需要解说一下。\\(匹配) [0-9]匹配任意数字(这个例子中为 1 和 5),sticks?匹配stick和stickss后的?使s可选,因为?匹配它前面的任何字符的0次或1次出现\\)匹配)。没有?,匹配stick和sticks会非常困难。
SELECT * FROM products WHERE prod_name REGEXP '[:digit:]{4}';
JP1000 1005 JetPack 1000 35.00 JetPack 1000, intended for single use
JP2000 1005 JetPack 2000 55.00 JetPack 2000, multi-use解析
[:digit:]匹配任意数字,因而它为数字的一个集合。{4}确切地要求它前面的字符(任意数字)出现4次,- 所以
[[:digit:]]{4}匹配连在一起的任意4位数字。
定位符
- 目前为止的所有例子都是匹配一个串中任意位置的文本。为了匹配特定位置的文本,需要使用定位符。
| 定位元字符 | 含义 |
|---|---|
^ | 匹配输入字符串的开始位置 |
$ | 匹配输入字符串的结束位置 |
\b | 匹配单词的边界 |
\B | 匹配非单词的边界 |
- 如果你想找出以一个数(包括以小数点开始的数)开始的所有产品,怎么办?
- 简单搜索
[0-9\\.]或[[:digit:]\\.]不行,因为它将在文本内任意位置查找匹配。 - 解决办法是使用
^定位符,如下所示:
SELECT * FROM products WHERE prod_name REGEXP '^[0-9\\.]';
ANV01 1001 .5 ton anvil 5.99 .5 ton anvil, black, complete with handy hook
ANV02 1001 1 ton anvil 9.99 1 ton anvil, black, complete with handy hook and carrying case
ANV03 1001 2 ton anvil 14.99 2 ton anvil, black, complete with handy hook and carrying case^匹配串的开始。因此,^[0-9\\.]只在``.` 或任意数字为串中第一个字符时才匹配它们。- 没有
^则还会多检索出 4 个别的行(那些中间有数字的行)。
第 10 章—创建计算字段
计算字段
- 有时候存储在表中的数据都不是客户端所需要的。
- 我们需要直接从数据库中检索出转换、计算或格式化过的数据;而不是检索出数据,然后再在客户机应用程序或报告程序中重新格式化。
- 这就是计算字段发挥作用的所在了。与前面各章介绍过的列不同
- 计算字段并不实际存在于数据库表中。计算字段是运行时在
SELECT语句内创建的。
拼接字段
- 在
MySQL的SELECT语句中,可使用Concat()函数来拼接两个列。
SELECT CONCAT(vend_name,'(', vend_country, ')') FROM vendors ORDER BY vend_name;Concat()需要一个或多个指定的串,各个串之间用逗号分隔。上面的SELECT语句连接以下 4 个元素:- 存储在
vend_name列中的名字; - 包含一个空格和一个左圆括号的串;
- 存储在
vend_country列中的国家; - 包含一个右圆括号的串。
- 存储在
使用别名
- 别名(alias)是一个字段或值的替换名。别名用 AS 关键字赋予
SELECT CONCAT(vend_name,'(', vend_country, ')') AS vend_title FROM vendors ORDER BY vend_name;执行算术计算
| 操作符 | 描述 |
|---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
SELECT prod_id, quantity, item_price, quantity*item_price AS expanded_price FROM orderitems WHERE order_num = 20005
ANV01 10 5.99 59.90
ANV02 3 9.99 29.97
TNT2 5 10.00 50.00
FB 1 10.00 10.00第 11 章—使用数据处理函数
文本处理函数
| 函数名称 | 描述 |
|---|---|
CONCAT() | 将两个或多个字符串连接在一起 |
SUBSTRING() | 返回一个字符串的子串 |
LEFT() | 返回字符串的左边指定数量的字符 |
RIGHT() | 返回字符串的右边指定数量的字符 |
LENGTH() | 返回一个字符串的字符个数 |
LOWER() | 将字符串转换为小写 |
UPPER() | 将字符串转换为大写 |
REPLACE() | 替换字符串中的指定子串 |
TRIM() | 去除字符串开头或末尾的空格或指定字符 |
REVERSE() | 反转字符串的顺序 |
SUBSTRING_INDEX() | 返回一个字符串在指定分隔符出现的指定次数后的子串 |
REGEXP_REPLACE() | 用正则表达式替换字符串中的指定内容 |
INSERT() | 将一个字符串插入到另一个字符串的指定位置 |
LOCATE() | 返回一个子串在字符串中第一次出现的位置 |
CHAR_LENGTH() | 返回一个字符串的字符数(与 LENGTH() 类似,但计算方式不同) |
CONCAT_WS() | 将多个字符串连接在一起,使用指定的分隔符 |
FIND_IN_SET() | 在一个逗号分隔的字符串列表中查找一个值的位置 |
JSON_EXTRACT() | 从 JSON 字符串中提取指定的数据 |
JSON_UNQUOTE() | 去除 JSON 字符串中的引号 |
常用日期和时间处理函数
| 函数名称 | 描述 |
|---|---|
AddDate() | 增加一个日期(天、周等) |
AddTime() | 增加一个时间(时、分等) |
CurDate() | 返回当前日期 |
CurTime() | 返回当前时间 |
Date() | 返回日期时间的日期部分 |
DateDiff() | 计算两个日期之差 |
Date_Add() | 高度灵活的日期运算函数 |
Date_Format() | 返回一个格式化的日期或时间串 |
Day() | 返回一个日期的天数部分 |
DayOfWeek() | 对于一个日期,返回对应的星期几 |
Hour() | 返回一个时间的小时部分 |
Minute() | 返回一个时间的分钟部分 |
Month() | 返回一个日期的月份部分 |
Now() | 返回当前日期和时间 |
Second() | 返回一个时间的秒部分 |
Time() | 返回一个日期时间的时间部分 |
Year() | 返回一个日期的年份部分 |
数值处理函数
| 函数 | 描述 |
|---|---|
ABS() | 返回一个数的绝对值 |
CEILING() | 返回大于或等于给定数的最小整数 |
FLOOR() | 返回小于或等于给定数的最大整数 |
ROUND() | 返回最接近给定数的整数 |
RAND() | 返回一个随机数 |
EXP() | 返回 e 的给定次幂 |
LOG() | 返回给定数的自然对数 |
LOG10() | 返回给定数的以 10 为底的对数 |
SQRT() | 返回给定数的平方根 |
POWER() | 返回给定数的给定次幂 |
MOD() | 返回给定数的模运算结果 |
SIN() | 返回给定角度的正弦值 |
COS() | 返回给定角度的余弦值 |
TAN() | 返回给定角度的正切值 |
ASIN() | 返回给定数的反正弦值 |
ACOS() | 返回给定数的反余弦值 |
ATAN() | 返回给定数的反正切值 |
DEGREES() | 将弧度转换为角度 |
RADIANS() | 将角度转换为弧度 |
PI() | 返回圆周率 Pi 的值 |
SIGN() | 返回给定数的符号值 |
TRUNCATE() | 截断给定数到指定的小数位数 |
FORMAT() | 格式化给定数的显示方式 |
GREATEST() | 返回给定值列表中的最大值 |
LEAST() | 返回给定值列表中的最小值 |
第 12 章-汇总数据
聚集函数
INFO
我们经常需要汇总数据而不用把它们实际检索出来,为此 MySQL 提供了专门的函数。 使用这些函数,MySQL 查询可用于检索数据,以便分析和报表生成。这种类型的检索例子有以下几种。
- 确定表中行数(或者满足某个条件或包含某个特定值的行数)。
- 获得表中行组的和。
- 找出表列(或所有行或某些特定的行)的最大值、最小值和平均值。
上述例子都需要对表中数据(而不是实际数据本身)汇总。因此,返回实际表数据是对时间和处理资源的一种浪费(更不用说带宽了)。 为方便这种类型的检索,MySQL 给出了 5 个聚集函数,这些函数能进行上述罗列的检索。
| 聚集函数 | 描述 |
|---|---|
COUNT() | 计算匹配给定条件的行数 |
SUM() | 计算匹配给定条件的列值之和 |
AVG() | 计算匹配给定条件的列值的平均值 |
MAX() | 找出匹配给定条件的列值的最大值 |
MIN() | 找出匹配给定条件的列值的最小值 |
GROUP_CONCAT() | 将匹配给定条件的列值连接为一个字符串 |
russ