Skip to content
本页目录

mysql 必知必会

第 4 章—检索数据

检索不同的行【去重】

  • 解决办法是使用 DISTINCT 关键字,顾名思义,此关键字指示 MySQL 只返回不同的值。
sql
SELECT DISTINCT vend_id FROM products

限制结果

  • LIMIT 5, 5 指示 MySQL 返回从行 5 开始的 5 行。第一个数为开始位置,第二个数为要检索的行数。
sql
SELECT vend_id FROM products LIMIT 5,5;

第 5 章—排序检索数据

默认排序【ORDER BY ASC】

  • 为了明确地排序用 SELECT 语句检索出的数据,可使用 ORDER BY 子句。 ORDER BY 子句取一个或多个列的名字,据此对输出进行排序。请看下面的例子:
sql
SELECT prod_name FROM products ORDER BY prod_name;

📢

通常,ORDER BY 子句中使用的列将 是为显示所选择的列。但是,实际上并不一定要这样,用非检索的列排序数据是完全合法的。

按多个列排序

  • 首先按价格,然后再按名称排序。
sql
SELECT * FROM products ORDER BY prod_price,prod_name;

指定排序方向

  • 数据排序不限于升序排序(从 A 到 Z)。这只是 默认 的排序顺序,还可 以使用 ORDER BY 子句以降序(从 Z 到 A)顺序排序。为了进行降序排序, 必须指定 DESC 关键字。下面的例子按价格以降序排序产品(最贵的排在最前面):
sql
SELECT * FROM products ORDER BY prod_price DESC;
  • 但是,如果打算用多个列排序怎么办?下面的例子以产品价格降序排序产品 (最贵的在最前面),然后再对产品名排序:
sql
SELECT * FROM products ORDER BY prod_price DESC, prod_name;
  • DESC 关键字只应用到直接位于其前面的列名。在上例中,只对 prod_price 列指定 DESC,对 prod_name 列不指定。因此, prod_price 列以降序排序,而 prod_name 列(在每个价格内)仍然按标准 的升序排序。

  • 如果想在多个列上进行降序排序,必须对每个列指定 DESC 关键字。

  • 使用 ORDER BY 和 LIMIT 的组合,能够找出一个列中最高或最低的值。 下面的例子演示如何找出最昂贵物品的值:

sql
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 制造的所有产品:
sql
SELECT * FROM products WHERE vend_id <> 1003;
SELECT * FROM products WHERE vend_id != 1003;

范围值检查

  • 下面的例子说明如何使用 BETWEEN 操作符,它检索价格在 5 美元和 10 美元之间的所有产品:
sql
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 子句附加 条件。下面的代码给出了一个例子:
sql
SELECT * FROM products WHERE vend_id = 1003 AND prod_price <= 10;
  • 此 SQL 语句检索由供应商 1003 制造 价格小于等于 10 美元的所有产品信息。
  • 上述例子中使用了只包含一个关键字 AND 的语句,把两个过滤条件组 合在一起。还可以添加多个过滤条件,每添加一条就要使用一个 AND。

OR 操作符

  • OR 操作符与 AND 操作符不同,它指示 MySQL 检索匹配任一条件的行。
sql
SELECT * FROM products WHERE vend_id = 1003 OR prod_price <= 10;
  • 此 SQL 语句检索由供应商 1003 制造 或者 价格小于等于 10 美元的所有产品信息。

计算次序

  • WHERE 可包含任意数目的 AND 和 OR 操作符。允许两者结合以进行复杂和高级的过滤。
  • 假如需要列出价格为 10 美元(含)以上且由 1002 或 1003 制 造的所有产品。下面的 SELECT 语句使用 AND 和 OR 操作符的组合建立了一个 WHERE 子句:
sql
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 语句:
sql
SELECT * FROM products WHERE (vend_id = 1002 OR vend_id = 1003) AND prod_price >= 10;

🌈 在 WHERE 子句中使用圆括号

任何时候使用具有 AND 和 OR 操作 符的 WHERE 子句,都应该使用圆括号明确地分组操作符。不要过分依赖默认计算次序,即使它确实是你想要的东西也是如 此。使用圆括号没有什么坏处,它能消除歧义。

IN 操作符

  • 圆括号在 WHERE 子句中还有另外一种用法。IN 操作符用来指定条件范 围,范围中的每个条件都可以进行匹配。IN 取合法值的由逗号分隔的清 单,全都括在圆括号中。下面的例子说明了这个操作符:
sql
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 之外的所有供应 商制造的产品

sql
SELECT * FROM products WHERE vend_id not IN (1002,1003) ORDER BY prod_price;

第 8 章—用通配符进行过滤

  • 为在搜索子句中使用通配符,必须使用 LIKE 操作符。LIKE 指示 MySQL, 后跟的搜索模式利用通配符匹配而不是直接相等匹配进行比较。

百分号(%)通配符

  • 最常使用的通配符是百分号 %。在搜索串中,% 表示出现

  • 为了找出所有以词 jet 起头的产品,可使用以下 SELECT 语句:

sql
SELECT * FROM products WHERE prod_name LIKE 'jet%'

区分大小写

根据 MySQL 的配置方式,搜索可以是区分大小写的。如果区分大小写,'jet%'与 JetPack 1000 将不匹配。

  • 通配符可在搜索模式中任意位置使用,并且可以使用多个通配符。下面的例子使用两个通配符,它们位于模式的两端:
sql
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 章—用正则表达式进行搜索

  • MySQLWHERE 子句对正则表达式提供了初步的支持,允许你指定正则表达式,过滤 SELECT 检索出的数据。

基本字符匹配

  • 我们从一个非常简单的例子开始。下面的语句检索列 prod_name 包含文本 1000 的所有行:
sql
SELECT * FROM products WHERE prod_name REGEXP '1000'
  • 除关键字 LIKE 被 REGEXP 替代外,这条语句看上去非常像使用 LIKE 的语句(第 8 章)。它告诉 MySQL:REGEXP 后所跟的东西作为正则表达式处理。

  • 为什么要费力地使用正则表达式?在刚才的例子中,正则表达式确实没有带来太多好处(可能还会降低性能),不过,请考虑下面的例子:

sql
SELECT * FROM products WHERE prod_name REGEXP '.000'
  • 这里使用了正则表达式 .000 。. 是正则表达式语言中一个特殊的字符。它表示匹配任意一个字符,因此,1000 和 2000 都匹配且返回。

!LIKE 与 REGEXP

  • 在 LIKE 和 REGEXP 之间有一个重要的差别。请看以下两条语句:
sql
SELECT * FROM products WHERE prod_name LIKE '1000'

sql
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 匹配

  • 为搜索两个串之一(或者为这个串,或者为另一个串)使用 | 如下所示:
sql
SELECT * FROM products WHERE prod_name REGEXP '1000|2000';
  • 语句中使用了正则表达式 1000|2000。| 为正则表达式的 OR 操作符。它表示匹配其中之一,因此 1000 和 2000 都匹配并返回。

  • 使用|从功能上类似于在 SELECT 语句中使用 OR 语句,多个 OR 条件可并入单个正则表达式。

匹配几个字符之一

  • 匹配几个字符之一可通过指定一组用[和]括起来的字符来完成:
sql
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]匹配任意字母字符。

匹配特殊字符

  • 为了匹配特殊字符,必须用\\为前导。\\-表示查找-,\\.表示查找.
sql
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:]空白字符中的空格和制表符
sql
SELECT * FROM products WHERE prod_name REGEXP '[:digit:]';

重复元字符(量词)

重复元字符含义
*匹配前一个元素零次或多次
+匹配前一个元素一次或多次 (等于{1,}
?匹配前一个元素零次或一次 (等于{0,1}
{n}匹配前一个元素恰好 n 次
{n,}匹配前一个元素至少 n 次
{n,m}匹配前一个元素至少 n 次且不超过 m 次 (m 不超过 255)
  • 下面举几个例子。
sql
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?匹配sticksticks
  • s 后的 ? 使 s 可选,因为 ? 匹配它前面的任何字符的 0 次或 1 次出现
  • \\)匹配)。没有?,匹配 sticksticks 会非常困难。
sql
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:]\\.]不行,因为它将在文本内任意位置查找匹配。
  • 解决办法是使用 ^ 定位符,如下所示:
sql
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 语句内创建的。

拼接字段

  • MySQLSELECT 语句中,可使用 Concat() 函数来拼接两个列。
sql
SELECT CONCAT(vend_name,'(', vend_country, ')') FROM vendors ORDER BY vend_name;
  • Concat()需要一个或多个指定的串,各个串之间用逗号分隔。上面的 SELECT 语句连接以下 4 个元素:
    • 存储在 vend_name 列中的名字;
    • 包含一个空格和一个左圆括号的串;
    • 存储在 vend_country 列中的国家;
    • 包含一个右圆括号的串。

使用别名

  • 别名(alias)是一个字段或值的替换名。别名用 AS 关键字赋予
sql
SELECT CONCAT(vend_name,'(', vend_country, ')') AS vend_title FROM vendors ORDER BY vend_name;

执行算术计算

操作符描述
+
-
*
/
sql
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()将匹配给定条件的列值连接为一个字符串

MIT Licensed