第3章教程

目录

3.1连接和断开服务器
3.2输入查询
3.3创建和使用数据库
3.3.1创建和选择数据库
3.3.2创建表
3.3.3将数据加载到表中
3.3.4从表中检索信息
3.4获取有关数据库和表的信息
3.5在批处理模式下使用mysql
3.6常见查询示例
3.6.1列的最大值
3.6.2保持某一列最大值的行
3.6.3每组最大列数
3.6.4保持某一列的分组最大值的行
3.6.5使用用户定义的变量
3.6.6使用外键
3.6.7搜索两个密钥
3.6.8计算每日访问量
3.6.9使用AUTO_INCREMENT
3.7在Apache中使用MySQL

本章通过展示如何使用 mysql 客户端程序创建和使用简单数据库 来提供MySQL的教程介绍 。 mysql (有时称为 “ 终端监视器 ” 或只是 “ 监视器 ” )是一个交互式程序,使您可以连接到MySQL服务器,运行查询和查看结果。 mysql 也可以在批处理模式下使用:事先将查询放在文件中,然后告诉 mysql 执行文件的内容。 这里介绍 了使用 mysql的 两种方法 。

要查看 mysql 提供的选项列表,请 使用以下 --help 选项 调用它 :

外壳> mysql --help

本章假设 MySQL的 安装在您的机器上,以及一个MySQL服务器是提供给你可以连接。 如果不是这样,请联系您的MySQL管理员。 (如果 您 是管理员,则需要查阅本手册的相关部分,例如 第5章, MySQL服务器管理 。)

本章介绍了设置和使用数据库的整个过程。 如果您只对访问现有数据库感兴趣,则可能需要跳过描述如何创建数据库及其包含的表的部分。

因为本章本质上是教程,所以必须省略许多细节。 有关此处所涉及主题的更多信息,请参阅手册的相关章节。

3.1连接和断开服务器

要连接到服务器,通常需要在调用 mysql 时提供MySQL用户名, 并且很可能是密码。 如果服务器在您登录的计算机以外的计算机上运行,​​则还需要指定主机名。 请与您的管理员联系,以了解您应该使用哪些连接参数进行连接(即,要使用的主机,用户名和密码)。 一旦知道了正确的参数,就应该能够像这样连接:

shell> 
输入密码:mysql -h host -u user -p********

host 并 user 表示运行MySQL服务器的主机名和MySQL帐户的用户名。 替换适合您的设置的值。 该 ******** 代表你的密码; 当 mysql 显示 Enter password: 提示 时输入它 。

如果可行,您应该看到一些介绍性信息,然后是 mysql> 提示:

shell> 
输入密码:mysql -h host -u user -p********
欢迎使用MySQL监视器。命令以;结尾; 或\ g。
您的MySQL连接ID是25338到服务器版本:8.0.18-standard

输入'help;' 或'\ h'寻求帮助。输入'\ c'清除缓冲区。

MySQL的>

该 mysql> 提示告诉你 的mysql 准备为你输入SQL语句。

如果您在运行MySQL的同一台计算机上登录,则可以省略主机,只需使用以下命令:

外壳> mysql -u user -p

如果,当您尝试登录时,会收到错误消息,例如 ERROR 2002(HY000):无法通过套接字'/tmp/mysql.sock'(2)连接到本地MySQL服务器 ,这意味着MySQL服务器守护程序(Unix)或服务(Windows)未运行。 请咨询管理员或参阅 适用于您的操作系统 的 第2章“ 安装和升级MySQL ”一节 。

有关尝试登录时经常遇到的其他问题的帮助,请参见 第B.4.2节“使用MySQL程序时的常见错误” 。

某些MySQL安装允许用户以匿名(未命名)用户身份连接到本地主机上运行的服务器。 如果你的机器上是这种情况,你应该可以通过调用 没有任何选项的 mysql 连接到该服务器 :

外壳> mysql

成功连接后,您可以通过 在 提示符 下键入 QUIT (或 \q ) 来随时断开连接 mysql> :

MySQL的> QUIT
再见

在Unix上,您也可以通过按Control + D断开连接。

以下部分中的大多数示例都假定您已连接到服务器。 他们通过 mysql> 提示 表明了这一点 。

3.2输入查询

确保已连接到服务器,如上一节中所述。 这样做本身并不会选择任何可以使用的数据库,但这没关系。 此时,更重要的是要找到一些关于如何发布查询的信息,而不是直接创建表,将数据加载到它们中以及从中检索数据。 本节介绍了输入查询的基本原则,使用您可以尝试的几个查询来熟悉 mysql的 工作原理。

这是一个简单的查询,要求服务器告诉您它的版本号和当前日期。 按照 mysql> 提示输入 如下所示 ,然后按Enter键:

MySQL的> SELECT VERSION(), CURRENT_DATE;
+ ----------- -------------- + +
| 版本()| CURRENT_DATE |
+ ----------- -------------- + +
| 5.8.0-m17 | 2015-12-21 |
+ ----------- -------------- + +
1排(0.02秒)
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排(0.02秒)

到目前为止显示的查询是相对较短的单行语句。 您甚至可以在一行中输入多个语句。 用分号结束每一个:

MySQL的> SELECT VERSION(); SELECT NOW();
+ ----------- +
| 版本()|
+ ----------- +
| 8.0.13 |
+ ----------- +
1排(0.00秒)

+ --------------------- +
| 现在()|
+ --------------------- +
| 2018-08-24 00:56:40 |
+ --------------------- +
1排(0.00秒)

不需要在一行上提供查询,因此需要多行的冗长查询不是问题。 mysql 通过查找终止分号来确定语句的结束位置,而不是查找输入行的结尾。 (换句话说, mysql 接受自由格式输入:它收集输入行但在看到分号之前不执行它们。)

这是一个简单的多行语句:

mysql> SELECT
    - > USER()
    - > ,
    - >CURRENT_DATE;
+ --------------- + -------------- +
| USER()| CURRENT_DATE |
+ --------------- + -------------- +
| jon @ localhost | 2018-08-24 |
+ --------------- + -------------- +

在这个例子中,请注意提示符从 mysql> 对 -> 您输入一个多行查询的第一行后。 这就是 mysql 如何 表明它还没有看到完整的声明并且正在等待其余的声明。 提示是您的朋友,因为它提供了有价值的反馈。 如果您使用该反馈,您始终可以了解 mysql 正在等待 什么 。

如果您决定不想执行正在输入的查询,请键入 \c 以下命令 取消它 :

mysql> SELECT
    - > USER()
    - >\c
MySQL的>

在这里,也请注意提示。 它会 mysql> 在您键入后 切换回来 \c ,提供反馈以指示 mysql 已为新查询做好准备。

下表显示了您可能看到的每个提示,并总结了它们对 mysql 所处状态的含义 。

提示 含义
mysql> 准备好进行新查询
-> 等待下一行的多行查询
'> 等待下一行,等待以单引号( ' ) 开头的字符串的完成
"> 等待下一行,等待以双引号开头的字符串的完成( " )
`> 等待下一行,等待以反引号( ` ) 开头的标识符的完成
/*> 等待下一行,等待以#开头的评论完成 /*

当您打算在一行上发出查询时,通常会出现多行语句,但忘记了终止分号。 在这种情况下, mysql 等待更多输入:

MySQL的> SELECT USER()
    - >

如果你遇到这种情况(你认为你已经输入了一个语句,但唯一的响应是 -> 提示),很可能 mysql 正在等待分号。 如果您没有注意到提示告诉您的内容,您可能会在那里坐一会儿,然后才意识到您需要做什么。 输入分号以完成语句, mysql 执行它:

mysql> SELECT USER()
    - >;
+ --------------- +
| USER()|
+ --------------- +
| jon @ localhost |
+ --------------- +

在 '> 和 "> 字符串集合(说,MySQL是在等待一个字符串完成的另一种方式)中的提示信息出现。 在MySQL中,您可以编写由任一个 ' 或 " 字符 包围的字符串 (例如, 'hello' 或 "goodbye" ),并且 mysql 允许您输入跨越多行的字符串。 当您看到 '> 或 "> 提示时,表示您输入的行包含以a ' 或 " 引号字符 开头的字符串 ,但尚未输入终止字符串的匹配引号。 这通常表明您无意中遗漏了引号字符。 例如:

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 。 这可能非常令人困惑,特别是如果您不知道在取消当前查询之前需要提供终止引用。

注意

此时的多行语句是在没有辅助( -> 或其他)提示的 情况下编写的 ,以便于复制和粘贴语句以便自己尝试。

3.3创建和使用数据库

一旦知道如何输入SQL语句,就可以访问数据库了。

假设您家中有几只宠物(您的动物园),并且您希望跟踪有关它们的各种类型的信息。 您可以通过创建表来保存数据并使用所需信息加载数据。 然后,您可以通过从表中检索数据来回答有关动物的各种问题。 本节介绍如何执行以下操作:

  • 创建一个数据库

  • 创建一个表

  • 将数据加载到表中

  • 以各种方式从表中检索数据

  • 使用多个表

动物园数据库很简单(故意),但要想到可能使用类似数据库的真实情况并不困难。 例如,农民可以使用这样的数据库来跟踪牲畜,或者由兽医跟踪患者记录。 可以从MySQL网站获得包含以下部分中使用的一些查询和样本数据的动物园分布。 它可以 通过 https://dev.mysql.com/doc/ 以压缩 tar 文件和Zip格式提供 。

使用该 SHOW 语句查找服务器上当前存在的数据库:

MySQL的> SHOW DATABASES;
+ ---------- +
| 数据库|
+ ---------- +
| mysql |
| 测试|
| tmp |
+ ---------- +

该 mysql 数据库描述了用户访问权限。 该 test 数据库通常可作为用户工作区尝试的事情了。

声明显示的数据库列表可能与您的计算机不同; SHOW DATABASES 如果您没有该 SHOW DATABASES 权限 ,则不会显示您没有权限的数据库 。 请参见 第13.7.6.14节“显示数据库语法” 。

如果 test 数据库存在,请尝试访问它:

MySQL的> USE test
数据库已更改

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 您连接到服务器的主机。

3.3.1创建和选择数据库

如果管理员在设置权限时为您创建数据库,则可以开始使用它。 否则,您需要自己创建它:

MySQL的> CREATE DATABASE menagerie;

在Unix下,数据库名称是区分大小写的(不像SQL关键字),所以你必须总是指到你的数据库 menagerie ,而不是 Menagerie , MENAGERIE 或一些其他变种。 表名也是如此。 (在Windows下,此限制不适用,但您必须在给定查询中使用相同的字母表引用数据库和表。但是,出于各种原因,建议的最佳做法始终是使用在使用时使用的相同字母。数据库已创建。)

注意

如果您收到错误,例如 ERROR 1044(42000): 尝试创建数据库时, 用户'micah'@'localhost'拒绝访问数据库'menagerie' ,这意味着您的用户帐户没有必要的权限所以。 与管理员讨论或参见 第6.2节“访问控制和帐户管理” 。

创建数据库不会选择它来使用; 你必须明确地这样做。 要创建 menagerie 当前数据库,请使用以下语句:

MySQL的> USE menagerie
数据库已更改

您的数据库只需创建一次,但每次开始 mysql 会话 时都必须选择它才能使用 。 您可以通过发出 USE 示例中所示 的 语句 来完成此操作 。 或者,您可以在调用 mysql 时在命令行上选择数据库 。 只需在您可能需要提供的任何连接参数之后指定其名称。 例如:

shell> 
输入密码:mysql -h host -u user -p menagerie********
重要

menagerie 在刚刚显示的命令中 不是 您的密码。 如果要在 -p 选项 后的命令行上提供密码,则 必须在没有中间空间的情况下执行此操作(例如,as ,not as )。 但是,建议不要在命令行中输入密码,因为这样做会使其暴露给在您的计算机上登录的其他用户窥探。 -ppassword -p password

注意

您可以随时查看当前选择使用哪个数据库 。 SELECT DATABASE()

3.3.2创建表

创建数据库很容易,但此时它是空的, SHOW TABLES 告诉你:

MySQL的> SHOW TABLES;
空集(0.00秒)

更难的部分是决定数据库的结构应该是什么:您需要哪些表以及每个表中应该包含哪些列。

您想要一张包含每只宠物记录的表格。 这可以称为 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 , 因为列值的长度不同。 这些列定义中的长度不必全部相同,也不必相同 。 通常,您可以选择从任何长度 到 ,无论似乎是最合理的给你。 如果你选择不好而后来需要更长的字段,那么MySQL会提供一个 声明。 owner species 20 1 65535 ALTER TABLE

可以选择几种类型的值来表示动物记录中的性别,例如 'm' 和 'f' ,或者可能 'male' 和 'female' 。 最简单的是使用单个字符 'm' 和 'f' 。

DATE 对于 birth 和 death 列 使用 数据类型 是一个相当明显的选择。

创建表后, SHOW TABLES 应该产生一些输出:

MySQL的> SHOW TABLES;
+ --------------------- +
| 动物园中的表格
+ --------------------- +
| 宠物|
+ --------------------- +

要验证您的表是否按预期方式创建,请使用以下 DESCRIBE 语句:

MySQL的> DESCRIBE pet;
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +
| 领域| 输入| 空| 钥匙| 默认| 额外的|
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +
| 名字| varchar(20)| 是的| | NULL | |
| 老板| varchar(20)| 是的| | NULL | |
| 物种| varchar(20)| 是的| | NULL | |
| 性别 char(1)| 是的| | NULL | |
| 出生| 日期| 是的| | NULL | |
| 死亡| 日期| 是的| | NULL | |
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +

您可以随时使用 DESCRIBE ,例如,如果您忘记了表中列的名称或它们具有的类型。

有关MySQL数据类型的更多信息,请参见 第11章, 数据类型 。

3.3.3将数据加载到表中

创建表后,您需要填充它。 该 LOAD DATA 和 INSERT 语句是这个有用的。

假设您的宠物记录可以如此处所示。 (观察MySQL期望 'YYYY-MM-DD' 格式的 日期 ;这可能与您习惯的不同。)

名称 所有者 种类 性别 分娩 死亡
蓬松 哈罗德 猫 F 1993年2月4日
爪 格温 猫 米 1994年3月17日
巴菲 哈罗德 狗 F 1989年5月13日
方 班尼 狗 米 1990年8月27日
鲍泽 黛安 狗 米 1979年8月31日 1995年7月29日
扢 格温 鸟 F 1998年9月11日
惠斯勒 格温 鸟 1997年12月9日
瘦 班尼 蛇 米 1996年4月29日

因为您从空表开始,所以填充它的一种简单方法是为每个动物创建一个包含行的文本文件,然后使用单个语句将文件内容加载到表中。

您可以创建一个文本文件 pet.txt ,每行包含一个记录,其值由制表符分隔,并按照 CREATE TABLE 语句 中列出的顺序给出 。 对于缺失值(例如未知性别或仍然生活的动物的死亡日期),您可以使用 NULL 值。 要在文本文件中表示这些,请使用 \N (反斜杠,大写-N)。 例如,惠斯勒鸟的记录看起来像这样(值之间的空格是单个制表符):

惠斯勒Gwen鸟\ 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计算机上,您可能希望使用 LINES TERMINATED BY '\r' 。)

如果需要,可以在 LOAD DATA 语句中 显式指定列值分隔符和行结束标记 ,但默认 值为 制表符和换行符。 这些语句足以使语句 pet.txt 正确 读取文件 。

如果语句失败,则默认情况下您的MySQL安装可能没有启用本地文件功能。 有关 如何更改此信息的信息, 请参见 第6.1.6节“LOAD DATA LOCAL的安全问题” 。

如果要一次添加一条新记录,该 INSERT 语句很有用。 在最简单的形式中,您按照 CREATE TABLE 语句 中列出的列的顺序为每列提供值 。 假设黛安得到了一只名为 “ Puffball ” 的新仓鼠 。 “ 您可以使用如下 INSERT 语句 添加新记录 :

MySQL的> INSERT INTO pet
       VALUES ('Puffball','Diane','hamster','f','1999-03-30',NULL);

字符串和日期值在此处指定为带引号的字符串。 此外, INSERT 您可以 NULL 直接 插入 以表示缺失值。 你没有 \N 像你一样使用 LOAD DATA 。

从这个例子中,您应该能够看到,最初使用多个 INSERT 语句而不是单个 LOAD DATA 语句 来加载记录会涉及更多类型 。

3.3.4从表中检索信息

该 SELECT 语句用于从表中提取信息。 声明的一般形式是:

SELECT what_to_select
FROM which_table
WHERE conditions_to_satisfy;

what_to_select 表示你想看到什么。 这可以是列列表,也 * 可以表示 “ 所有列”。 “ which_table 表示要从中检索数据的表。 该 WHERE 条款是可选的。 如果存在,则 conditions_to_satisfy 指定行必须满足的一个或多个条件才有资格进行检索。

3.3.4.1选择所有数据

最简单的形式 SELECT 从表 中 检索所有内容:

MySQL的> SELECT * FROM pet;
+ ---------- + -------- + --------- + ------ + ------------ + ------------ +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ---------- + -------- + --------- + ------ + ------------ + ------------ +
| 蓬松| 哈罗德| 猫| f | 1993-02-04 | NULL |
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
| 方| 班尼| 狗| m | 1990-08-27 | NULL |
| 鲍泽尔| 黛安娜 狗| m | 1979-08-31 | 1995-07-29 |
| Chirpy | 格温| 鸟| f | 1998-09-11 | NULL |
| 惠斯勒| 格温| 鸟| NULL | 1997-12-09 | NULL |
| 苗条| 班尼| 蛇| m | 1996-04-29 | NULL |
| 马勃| 黛安娜 仓鼠| 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 变化只在讨论记录,并不需要您重新加载表。

3.3.4.2选择特定行

如上一节所示,可以轻松检索整个表。 只是省略 声明中 的 WHERE 条款 SELECT 。 但通常你不希望看到整个表格,特别是当它变大时。 相反,您通常对回答特定问题更感兴趣,在这种情况下,您可以对所需信息指定一些约束。 让我们看看他们回答的有关您的宠物的问题的一些选择查询。

您只能从表中选择特定行。 例如,如果您想验证您对Bowser出生日期所做的更改,请选择Bowser的记录,如下所示:

MySQL的> SELECT * FROM pet WHERE name = 'Bowser';
+ -------- + ------- + --------- + ------ + ------------ +  - ---------- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ -------- + ------- + --------- + ------ + ------------ +  - ---------- +
| 鲍泽尔| 黛安娜 狗| m | 1989-08-31 | 1995-07-29 |
+ -------- + ------- + --------- + ------ + ------------ +  - ---------- +

输出确认年份被正确记录为1989年,而不是1979年。

字符串比较通常不区分大小写,因此您可以将名称指定为 'bowser' , 'BOWSER' 等等。 查询结果是一样的。

您可以在任何列上指定条件,而不仅仅是 name 。 例如,如果您想知道1998年或之后出生的动物,请测试该 birth 栏:

MySQL的> SELECT * FROM pet WHERE birth >= '1998-1-1';
+ ---------- + ------- + --------- + ------ + ------------ + ------- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ---------- + ------- + --------- + ------ + ------------ + ------- +
| Chirpy | 格温| 鸟| f | 1998-09-11 | NULL |
| 马勃| 黛安娜 仓鼠| f | 1999-03-30 | NULL |
+ ---------- + ------- + --------- + ------ + ------------ + ------- +

例如,您可以结合条件来定位雌性狗:

MySQL的> SELECT * FROM pet WHERE species = 'dog' AND sex = 'f';
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +

前面的查询使用 AND 逻辑运算符。 还有一个 OR 运营商:

MySQL的> SELECT * FROM pet WHERE species = 'snake' OR species = 'bird';
+ ---------- + ------- + --------- + ------ + ------------ + ------- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ---------- + ------- + --------- + ------ + ------------ + ------- +
| Chirpy | 格温| 鸟| f | 1998-09-11 | NULL |
| 惠斯勒| 格温| 鸟| NULL | 1997-12-09 | NULL |
| 苗条| 班尼| 蛇| 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');
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +

3.3.4.3选择特定列

如果您不想查看表中的整行,只需将您感兴趣的列命名为逗号分隔。 例如,如果您想知道您的动物何时出生,请选择 name 和 birth 列:

MySQL的> SELECT name, birth FROM pet;
+ ---------- + ------------ +
| 名字| 出生|
+ ---------- + ------------ +
| 蓬松| 1993-02-04 |
| 爪子| 1994-03-17 |
| 巴菲| 1989-05-13 |
| 方| 1990-08-27 |
| 鲍泽尔| 1989-08-31 |
| Chirpy | 1998-09-11 |
| 惠斯勒| 1997-12-09 |
| 苗条| 1996-04-29 |
| 马勃| 1999-03-30 |
+ ---------- + ------------ +

要找出谁拥有宠物,请使用此查询:

MySQL的> SELECT owner FROM pet;
+ -------- +
| 老板|
+ -------- +
| 哈罗德|
| 格温|
| 哈罗德|
| 班尼|
| 黛安娜
| 格温|
| 格温|
| 班尼|
| 黛安娜
+ -------- +

请注意,查询只是 owner 从每条记录中 检索 列,其中一些只出现一次。 要最小化输出,请通过添加关键字检索每个唯一的输出记录一次 DISTINCT :

MySQL的> SELECT DISTINCT owner FROM pet;
+ -------- +
| 老板|
+ -------- +
| 班尼|
| 黛安娜
| 格温|
| 哈罗德|
+ -------- +

您可以使用 WHERE 子句将行选择与列选择组合在一起。 例如,要仅获取狗和猫的出生日期,请使用以下查询:

MySQL的> SELECT name, species, birth FROM pet
       WHERE species = 'dog' OR species = 'cat';
+ -------- + --------- + ------------ +
| 名字| 物种| 出生|
+ -------- + --------- + ------------ +
| 蓬松| 猫| 1993-02-04 |
| 爪子| 猫| 1994-03-17 |
| 巴菲| 狗| 1989-05-13 |
| 方| 狗| 1990-08-27 |
| 鲍泽尔| 狗| 1989-08-31 |
+ -------- + --------- + ------------ +

3.3.4.4排序行

您可能已在前面的示例中注意到结果行没有按特定顺序显示。 当行以某种有意义的方式排序时,通常更容易检查查询输出。 要对结果进行排序,请使用 ORDER BY 子句。

这是动物的生日,按日期排序:

MySQL的> SELECT name, birth FROM pet ORDER BY birth;
+ ---------- + ------------ +
| 名字| 出生|
+ ---------- + ------------ +
| 巴菲| 1989-05-13 |
| 鲍泽尔| 1989-08-31 |
| 方| 1990-08-27 |
| 蓬松| 1993-02-04 |
| 爪子| 1994-03-17 |
| 苗条| 1996-04-29 |
| 惠斯勒| 1997-12-09 |
| Chirpy | 1998-09-11 |
| 马勃| 1999-03-30 |
+ ---------- + ------------ +

在字符类型列上,排序与所有其他比较操作一样,通常以不区分大小写的方式执行。 这意味着除了它们的情况之外,对于相同的列,订单是未定义的。 您可以使用 BINARY 类似的 方式强制对列进行区分大小写的排序 : 。 ORDER BY BINARY col_name

默认排序顺序是升序,首先是最小值。 要按反向(降序)排序,请将 DESC 关键字 添加到要排序 的列的名称:

MySQL的> SELECT name, birth FROM pet ORDER BY birth DESC;
+ ---------- + ------------ +
| 名字| 出生|
+ ---------- + ------------ +
| 马勃| 1999-03-30 |
| Chirpy | 1998-09-11 |
| 惠斯勒| 1997-12-09 |
| 苗条| 1996-04-29 |
| 爪子| 1994-03-17 |
| 蓬松| 1993-02-04 |
| 方| 1990-08-27 |
| 鲍泽尔| 1989-08-31 |
| 巴菲| 1989-05-13 |
+ ---------- + ------------ +

您可以对多个列进行排序,并且可以按不同方向对不同列进行排序。 例如,要按动物类型按升序排序,然后按动物类型中的出生日期按降序排序(最年轻的动物首先),请使用以下查询:

MySQL的> SELECT name, species, birth FROM pet
       ORDER BY species, birth DESC;
+ ---------- + --------- + ------------ +
| 名字| 物种| 出生|
+ ---------- + --------- + ------------ +
| Chirpy | 鸟| 1998-09-11 |
| 惠斯勒| 鸟| 1997-12-09 |
| 爪子| 猫| 1994-03-17 |
| 蓬松| 猫| 1993-02-04 |
| 方| 狗| 1990-08-27 |
| 鲍泽尔| 狗| 1989-08-31 |
| 巴菲| 狗| 1989-05-13 |
| 马勃| 仓鼠| 1999-03-30 |
| 苗条| 蛇| 1996-04-29 |
+ ---------- + --------- + ------------ +

该 DESC 关键字仅适用于紧邻其前面的列名( birth ); 它不会影响 species 列排序顺序。

3。3。4。5日期计算

MySQL提供了几个可用于在日期上执行计算的函数,例如,计算年龄或提取日期的部分。

要确定每只宠物的年龄,请使用此 TIMESTAMPDIFF() 功能。 它的参数是你想要表达结果的单位,以及两个可以取得差异的日期。 以下查询显示了每只宠物的出生日期,当前日期和年龄。 一个 别名 ( age )是用来制造最终输出列标签更有意义。

MySQL的> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet;
+ ---------- + ------------ + ------------ + ------ +
| 名字| 出生| CURDATE()| 年龄|
+ ---------- + ------------ + ------------ + ------ +
| 蓬松| 1993-02-04 | 2003-08-19 | 10 |
| 爪子| 1994-03-17 | 2003-08-19 | 9 |
| 巴菲| 1989-05-13 | 2003-08-19 | 14 |
| 方| 1990-08-27 | 2003-08-19 | 12 |
| 鲍泽尔| 1989-08-31 | 2003-08-19 | 13 |
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| 惠斯勒| 1997-12-09 | 2003-08-19 | 5 |
| 苗条| 1996-04-29 | 2003-08-19 | 7 |
| 马勃| 1999-03-30 | 2003-08-19 | 4 |
+ ---------- + ------------ + ------------ + ------ +

查询有效,但如果以某种顺序显示行,则可以更轻松地扫描结果。 这可以通过添加一个 ORDER BY name 子句来按名称对输出进行排序 来完成 :

MySQL的> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet ORDER BY name;
+ ---------- + ------------ + ------------ + ------ +
| 名字| 出生| CURDATE()| 年龄|
+ ---------- + ------------ + ------------ + ------ +
| 鲍泽尔| 1989-08-31 | 2003-08-19 | 13 |
| 巴菲| 1989-05-13 | 2003-08-19 | 14 |
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| 爪子| 1994-03-17 | 2003-08-19 | 9 |
| 方| 1990-08-27 | 2003-08-19 | 12 |
| 蓬松| 1993-02-04 | 2003-08-19 | 10 |
| 马勃| 1999-03-30 | 2003-08-19 | 4 |
| 苗条| 1996-04-29 | 2003-08-19 | 7 |
| 惠斯勒| 1997-12-09 | 2003-08-19 | 5 |
+ ---------- + ------------ + ------------ + ------ +

要通过 age 而不是 对输出进行排序 name ,只需使用不同的 ORDER BY 子句:

MySQL的> SELECT name, birth, CURDATE(),
       TIMESTAMPDIFF(YEAR,birth,CURDATE()) AS age
       FROM pet ORDER BY age;
+ ---------- + ------------ + ------------ + ------ +
| 名字| 出生| CURDATE()| 年龄|
+ ---------- + ------------ + ------------ + ------ +
| Chirpy | 1998-09-11 | 2003-08-19 | 4 |
| 马勃| 1999-03-30 | 2003-08-19 | 4 |
| 惠斯勒| 1997-12-09 | 2003-08-19 | 5 |
| 苗条| 1996-04-29 | 2003-08-19 | 7 |
| 爪子| 1994-03-17 | 2003-08-19 | 9 |
| 蓬松| 1993-02-04 | 2003-08-19 | 10 |
| 方| 1990-08-27 | 2003-08-19 | 12 |
| 鲍泽尔| 1989-08-31 | 2003-08-19 | 13 |
| 巴菲| 1989-05-13 | 2003-08-19 | 14 |
+ ---------- + ------------ + ------------ + ------ +

类似的查询可用于确定死亡动物的死亡年龄。 您可以通过检查 death 值 是否确定这些动物 NULL 。 然后,对于那些具有非 NULL 值的人,计算 death 和 birth 值 之间的差异 :

MySQL的> SELECT name, birth, death,
       TIMESTAMPDIFF(YEAR,birth,death) AS age
       FROM pet WHERE death IS NOT NULL ORDER BY age;
+ -------- + ------------ + ------------ + ------ +
| 名字| 出生| 死亡| 年龄|
+ -------- + ------------ + ------------ + ------ +
| 鲍泽尔| 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;
+ ---------- + ------------ + -------------- +
| 名字| 出生| 月(出生)|
+ ---------- + ------------ + -------------- +
| 蓬松| 1993-02-04 | 2 |
| 爪子| 1994-03-17 | 3 |
| 巴菲| 1989-05-13 | 5 |
| 方| 1990-08-27 | 8 |
| 鲍泽尔| 1989-08-31 | 8 |
| Chirpy | 1998-09-11 | 9 |
| 惠斯勒| 1997-12-09 | 12 |
| 苗条| 1996-04-29 | 4 |
| 马勃| 1999-03-30 | 3 |
+ ---------- + ------------ + -------------- +

在接下来的一个月里寻找有生日的动物也很简单。 假设当前月份是4月。 然后是月份值 4 ,您可以查找5月(月 5 ) 出生的动物, 如下所示:

MySQL的> SELECT name, birth FROM pet WHERE MONTH(birth) = 5;
+ ------- + ------------ +
| 名字| 出生|
+ ------- + ------------ +
| 巴菲| 1989-05-13 |
+ ------- + ------------ +

如果当前月份是12月,则会出现一个小的复杂情况。 你不能只在月份数字( 12 )中 添加一个 并查找月份出生的动物 13 ,因为没有这样的月份。 相反,你寻找1月(月 1 ) 出生的动物 。

您可以编写查询,以便无论当前月份是什么,它都可以工作,因此您不必使用特定月份的数字。 DATE_ADD() 使您可以将时间间隔添加到给定日期。 如果您将值添加一个月 CURDATE() ,然后 使用 ,则提取月份部分 MONTH() ,结果将生成查找生日的月份:

MySQL的> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MONTH(DATE_ADD(CURDATE(),INTERVAL 1 MONTH));

完成相同任务的另一种方法是 1 在使用模数函数( MOD )将月份值换行 ( 0 如果它是当前 值) 之后 添加 以获取当前 任务 之后的下一个月 12 :

MySQL的> SELECT name, birth FROM pet
       WHERE MONTH(birth) = MOD(MONTH(CURDATE()), 12) + 1;

MONTH() 返回 1 和 之间的数字 12 。 并 MOD(something,12) 在 0 和 之间返回一个数字 11 。 所以添加必须在之后 MOD() ,否则我们将从11月( 11 )到1月( 1 )。

如果计算使用无效日期,则计算失败并生成警告:

MySQL的> SELECT '2018-10-31' + INTERVAL 1 DAY;
+ ------------------------------- +
| '2018-10-31'+ INTERVAL 1 DAY |
+ ------------------------------- +
| 2018-11-01 |
+ ------------------------------- +
MySQL的> SELECT '2018-10-32' + INTERVAL 1 DAY;
+ ------------------------------- +
| '2018-10-32'+ INTERVAL 1 DAY |
+ ------------------------------- +
| NULL |
+ ------------------------------- +
MySQL的> SHOW WARNINGS;
+ --------- + ------ + -------------------------------- -------- +
| 等级| 代码| 消息|
+ --------- + ------ + -------------------------------- -------- +
| 警告| 1292 | 日期时间值不正确:'2018-10-32'|
+ --------- + ------ + -------------------------------- -------- +

3.3.4.6使用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 |
+ ----------- + --------------- +

你不能使用算术比较操作符,如 = , < 或 <> 以测试 NULL 。 要自己演示,请尝试以下查询:

MySQL的> SELECT 1 = NULL, 1 <> NULL, 1 < NULL, 1 > NULL;
+ ---------- + ----------- + ---------- + ---------- +
| 1 = NULL | 1 <> NULL | 1 <空| 1> NULL |
+ ---------- + ----------- + ---------- + ---------- +
| NULL | NULL | NULL | NULL |
+ ---------- + ----------- + ---------- + ---------- +

因为任何算术比较的结果 NULL 也是 NULL ,所以你不能从这种比较中获得任何有意义的结果。

在MySQL中, 0 或 NULL 意味着虚假,其他任何意味着真实。 布尔运算的默认真值是 1 。

这种特殊处理 NULL 是为什么在上一节中,有必要确定哪些动物不再使用 death IS NOT NULL 而不是 death <> NULL 。

NULL 在a中, 两个 值被视为相等 GROUP BY 。

在执行操作时 ORDER BY , NULL 如果您这样做 ORDER BY ... ASC ,则 首先显示值,如果您执行 ,则显示最后 值 ORDER BY ... DESC 。

使用时常见的错误 NULL 是假设无法将零或空字符串插入到定义为的列中 NOT NULL ,但事实并非如此。 这些实际上是价值,而 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 | ''是空的| ''不是空|
+ ----------- + -------- + ------------ + -------- -------- +
| 0 | 1 | 0 | 1 |
+ ----------- + -------- + ------------ + -------- -------- +

因此,完全可以将零或空字符串插入到 NOT NULL 列中,因为这些实际上是这样的 NOT NULL 。 请参见 第B.4.4.3节“NULL值的问题” 。

3.3.4.7模式匹配

MySQL提供标准的SQL模式匹配以及基于扩展正则表达式的模式匹配形式,类似于Unix实用程序(如 vi , grep 和 sed) 使用的扩展正则表达式 。

SQL模式匹配使您可以使用 _ 匹配任何单个字符并 % 匹配任意数量的字符(包括零个字符)。 在MySQL中,SQL模式默认情况下不区分大小写。 这里显示了一些例子。 不要使用 = 或 <> 使用SQL模式时。 请改用 LIKE 或 NOT LIKE 比较运算符。

要查找以 b 下列 开头的名称 :

MySQL的> SELECT * FROM pet WHERE name LIKE 'b%';
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
| 鲍泽尔| 黛安娜 狗| m | 1989-08-31 | 1995-07-29 |
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +

要查找以以下结尾的名称 fy :

MySQL的> SELECT * FROM pet WHERE name LIKE '%fy';
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +
| 蓬松| 哈罗德| 猫| f | 1993-02-04 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +

要查找包含名称 w :

MySQL的> SELECT * FROM pet WHERE name LIKE '%w%';
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 鲍泽尔| 黛安娜 狗| m | 1989-08-31 | 1995-07-29 |
| 惠斯勒| 格温| 鸟| NULL | 1997-12-09 | NULL |
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +

要查找包含五个字符的名称,请使用 _ 模式字符的 五个实例 :

MySQL的> SELECT * FROM pet WHERE name LIKE '_____';
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +

MySQL提供的另一种模式匹配使用扩展的正则表达式。 当您测试此类模式的匹配项时,请使用 REGEXP_LIKE() 函数( REGEXP 或 RLIKE 运算符,它们是同义词 REGEXP_LIKE() )。

以下列表描述了扩展正则表达式的一些特征:

  • . 匹配任何单个字符。

  • 字符类 [...] 匹配括号内的任何字符。 例如, [abc] 匹配 a , b 或 c 。 要命名一系列字符,请使用短划线。 [a-z] 匹配任何字母,而 [0-9] 匹配任何数字。

  • * 匹配前面事物的零个或多个实例。 例如, x* 匹配任意数量的 x 字符, [0-9]* 匹配任意数量的数字,并 .* 匹配任意数量的任何数字。

  • 如果模式匹配正在测试的值中的任何位置,则正则表达式模式匹配成功。 (这与 LIKE 模式匹配 不同, 模式匹配仅在模式与整个值匹配时才会成功。)

  • 要锚定模式以使其必须与要测试的值的开头或结尾匹配,请使用模式 ^ 的开头或 $ 结尾。

为了演示扩展正则表达式的工作原理, LIKE 先前显示 的 查询将在此处重写以供使用 REGEXP_LIKE() 。

要查找 以...开头的名称 b ,请使用 ^ 以匹配名称的开头:

MySQL的> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^b');
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
| 鲍泽尔| 黛安娜 狗| m | 1979-08-31 | 1995-07-29 |
+ -------- + -------- + --------- + ------ + ------------ +  - ----------- +

要强制将正则表达式比较区分大小写,请使用区分大小写的排序规则,或使用 BINARY 关键字使其中一个字符串成为二进制字符串,或指定 c 匹配控制字符。 这些查询中的每 b 一个在名称的开头 仅匹配小写 :

SELECT * FROM pet WHERE REGEXP_LIKE(name,'^ b'COLLATE utf8mb4_0900_as_cs);
SELECT * FROM pet WHERE REGEXP_LIKE(name,BINARY'^ b');
SELECT * FROM pet WHERE REGEXP_LIKE(name,'^ b','c');

要查找以?结尾的名称 fy ,请使用 $ 以匹配名称的末尾:

MySQL的> SELECT * FROM pet WHERE REGEXP_LIKE(name, 'fy$');
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +
| 蓬松| 哈罗德| 猫| f | 1993-02-04 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ -------- + -------- + --------- + ------ + ------------ +  - ------ +

要查找包含a的名称 w ,请使用以下查询:

MySQL的> SELECT * FROM pet WHERE REGEXP_LIKE(name, 'w');
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 鲍泽尔| 黛安娜 狗| m | 1989-08-31 | 1995-07-29 |
| 惠斯勒| 格温| 鸟| NULL | 1997-12-09 | NULL |
+ ---------- + ------- + --------- + ------ + ------------ + ------------ +

因为正则表达式模式匹配,如果它出现在值的任何位置,则在上一个查询中不必在模式的任何一侧放置通配符以使其与整个值匹配,就像SQL模式一样。

要查找包含五个字符的名称,请使用 ^ 和 $ 匹配名称的开头和结尾,以及 . 中间的 五个实例 :

MySQL的> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^.....$');
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +

您还可以使用 ( “ repeat- -times ” )运算符 编写上一个查询 : {n} n

MySQL的> SELECT * FROM pet WHERE REGEXP_LIKE(name, '^.{5}$');
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 名字| 老板| 物种| 性别 出生| 死亡|
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +
| 爪子| 格温| 猫| m | 1994-03-17 | NULL |
| 巴菲| 哈罗德| 狗| f | 1989-05-13 | NULL |
+ ------- + -------- + --------- + ------ + ------------ +  - ----- +

有关正则表达式语法的更多信息,请参见 第12.5.2节“正则表达式” 。

3.3.4.8计数行

数据库通常用于回答 “ 表格中某种类型的数据出现频率 的问题 ? “ 例如,您可能想知道您拥有多少只宠物,或每个拥有者拥有多少只宠物,或者您可能想要对您的动物进行各种类型的人口普查操作。

计算你拥有的动物总数与 “ 桌子中 有多少行 pet ” 是同一个问题 ? “ 因为每只宠物有一条记录。 COUNT(*) 计算行数,因此计算动物的查询如下所示:

MySQL的> SELECT COUNT(*) FROM pet;
+ ---------- +
| COUNT(*)|
+ ---------- +
| 9 |
+ ---------- +

之前,您检索了拥有宠物的人的姓名。 COUNT() 如果您想知道每个拥有者有多少宠物, 您可以使用 :

MySQL的> SELECT owner, COUNT(*) FROM pet GROUP BY owner;
+ -------- + ---------- +
| 老板| COUNT(*)|
+ -------- + ---------- +
| 班尼| 2 |
| 黛安娜 2 |
| 格温| 3 |
| 哈罗德| 2 |
+ -------- + ---------- +

上述查询用于 GROUP BY 对每个记录进行分组 owner 。 使用的 COUNT() 结合 GROUP BY 是在各种分组表征您的数据非常有用。 以下示例显示了执行动物普查操作的不同方法。

每种动物数量:

MySQL的> SELECT species, COUNT(*) FROM pet GROUP BY species;
+ --------- + ---------- +
| 物种| COUNT(*)|
+ --------- + ---------- +
| 鸟| 2 |
| 猫| 2 |
| 狗| 3 |
| 仓鼠| 1 |
| 蛇| 1 |
+ --------- + ---------- +

每性别的动物数量:

MySQL的> SELECT sex, COUNT(*) FROM pet GROUP BY sex;
+ ------ + ---------- +
| 性别 COUNT(*)|
+ ------ + ---------- +
| NULL | 1 |
| f | 4 |
| m | 4 |
+ ------ + ---------- +

(在此输出中, NULL 表示性别未知。)

每种物种和性别组合的动物数量:

MySQL的> SELECT species, sex, COUNT(*) FROM pet GROUP BY species, sex;
+ --------- + ------ + ---------- +
| 物种| 性别 COUNT(*)|
+ --------- + ------ + ---------- +
| 鸟| NULL | 1 |
| 鸟| f | 1 |
| 猫| f | 1 |
| 猫| m | 1 |
| 狗| f | 1 |
| 狗| m | 2 |
| 仓鼠| f | 1 |
| 蛇| m | 1 |
+ --------- + ------ + ---------- +

使用时无需检索整个表 COUNT() 。 例如,以前的查询,只在狗和猫上执行时,如下所示:

MySQL的> SELECT species, sex, COUNT(*) FROM pet
       WHERE species = 'dog' OR species = 'cat'
       GROUP BY species, sex;
+ --------- + ------ + ---------- +
| 物种| 性别 COUNT(*)|
+ --------- + ------ + ---------- +
| 猫| f | 1 |
| 猫| m | 1 |
| 狗| f | 1 |
| 狗| m | 2 |
+ --------- + ------ + ---------- +

或者,如果你想要每性别的动物数量仅适用于已知性别的动物:

MySQL的> SELECT species, sex, COUNT(*) FROM pet
       WHERE sex IS NOT NULL
       GROUP BY species, sex;
+ --------- + ------ + ---------- +
| 物种| 性别 COUNT(*)|
+ --------- + ------ + ---------- +
| 鸟| f | 1 |
| 猫| f | 1 |
| 猫| m | 1 |
| 狗| f | 1 |
| 狗| m | 2 |
| 仓鼠| f | 1 |
| 蛇| m | 1 |
+ --------- + ------ + ---------- +

如果除了 COUNT() 值 之外还要指定要选择的列 ,则 GROUP BY 应该存在一个用于命名相同列 的 子句。 否则,会发生以下情况:

  • 如果 ONLY_FULL_GROUP_BY 启用 了 SQL模式,则会发生错误:

    MySQL的> SET sql_mode = 'ONLY_FULL_GROUP_BY';
    查询正常,0行受影响(0.00秒)
    
    MySQL的> SELECT owner, COUNT(*) FROM pet;
    错误1140(42000):在没有GROUP BY,表达式的聚合查询中
    SELECT列表的#1包含非聚合列'menagerie.pet.owner';
    这与sql_mode = only_full_group_by不兼容
    
  • 如果 ONLY_FULL_GROUP_BY 未启用,则通过将所有行视为单个组来处理查询,但为每个命名列选择的值是不确定的。 服务器可以自由选择任何行中的值:

    MySQL的> SET sql_mode = '';
    查询正常,0行受影响(0.00秒)
    
    MySQL的> SELECT owner, COUNT(*) FROM pet;
    + -------- + ---------- +
    | 老板| COUNT(*)|
    + -------- + ---------- +
    | 哈罗德| 8 |
    + -------- + ---------- +
    1排(0.00秒)
    

另请参见 第12.20.3节“GROUP BY的MySQL处理” 。 有关 行为和相关优化的 信息 , 请参见 第12.20.1节“聚合(GROUP BY)函数描述” 。 COUNT(expr)

3.3.4.9使用多个表

该 pet 表记录了您拥有的宠物。 如果您想记录有关它们的其他信息,例如生活中的事件,例如兽医的访问或者出生时,您需要另一张桌子。 这张桌子应该是什么样的? 它需要包含以下信息:

  • 宠物名称,以便您了解每个事件所属的动物。

  • 日期,以便您知道事件发生的时间。

  • 描述事件的字段。

  • 如果您希望能够对事件进行分类,则为事件类型字段。

鉴于这些注意事项, 表 的 CREATE TABLE 语句 event 可能如下所示:

MySQL的> CREATE TABLE event (name VARCHAR(20), date DATE,
       type VARCHAR(15), remark VARCHAR(255));

与 pet 表一样,最简单的方法是通过创建包含以下信息的制表符分隔文本文件来加载初始记录。

名称 日期 类型 备注
蓬松 1995年5月15日 窝 4只小猫,3只雌性,1只雄性
巴菲 1993-06-23 窝 5只小狗,2只雌性,3只雄性
巴菲 1994年6月19日 窝 3只小狗,3只雌性
扢 1999年3月21日 兽医 需要喙拉直
瘦 1997年8月3日 兽医 断肋骨
鲍泽 1991年10月12日 狗窝
方 1991年10月12日 狗窝
方 1998年8月28日 生日 给了他一个新的咀嚼玩具
爪 1998年3月17日 生日 给了他一个新的跳蚤项圈
惠斯勒 1998年12月9日 生日 第一个生日

像这样加载记录:

MySQL的> LOAD DATA LOCAL INFILE 'event.txt' INTO TABLE event;

根据您从 pet 表中 运行的查询中学到的内容 ,您应该能够对 event 表中 的记录执行检索 ; 原则是一样的。 但是, event 表格本身 何时 不足以回答您可能会问的问题?

假设你想要找出每只宠物的窝的年龄。 我们之前看到过如何计算两个日期的年龄。 母亲的垃圾日期在 event 表格中,但要计算她在该日期的年龄,您需要她的出生日期,该日期存储在 pet 表格中。 这意味着查询需要两个表:

MySQL的> SELECT pet.name,
       TIMESTAMPDIFF(YEAR,birth,date) AS age,
       remark
       FROM pet INNER JOIN event
         ON pet.name = event.name
       WHERE event.type = 'litter';
+ -------- + ------ + ----------------------------- +
| 名字| 年龄| 备注|
+ -------- + ------ + ----------------------------- +
| 蓬松| 2 | 4只小猫,3只雌性,1只雄性|
| 巴菲| 4 | 5只小狗,2只雌性,3只雄性|
| 巴菲| 5 | 3只小狗,3只雌性|
+ -------- + ------ + ----------------------------- +

有关此查询的注意事项有以下几点:

  • 该 FROM 子句连接两个表,因为查询需要从两个表中提取信息。

  • 组合(连接)来自多个表的信息时,需要指定一个表中的记录如何与另一个表中的记录匹配。 这很容易,因为它们都有一个 name 列。 该查询使用一个 ON 子句根据值匹配两个表中的记录 name 。

    该查询使用a INNER JOIN 来组合表。 一个 INNER JOIN 或者从表许可证行当且仅当两个表满足所规定的条件,以显示在结果 ON 子句。 在这个例子中, ON 子句指定 name 列中的 pet 表必须的匹配 name 列 event 表。 如果名称出现在一个表中但不出现在另一个表中,则该行不会出现在结果中,因为该 ON 子句中 的条件 失败。

  • 由于 name 列出现在两个表中,因此您必须具体说明引用该列时的表。 这是通过将表名添加到列名称来完成的。

您不需要两个不同的表来执行连接。 如果要将表中的记录与同一表中的其他记录进行比较,有时将表连接到自身会很有用。 例如,要在您的宠物中找到繁殖对,您可以 pet 自己 加入 表格,以生成候选对的活体雄性和类似物种的雌性:

MySQL的> SELECT p1.name, p1.sex, p2.name, p2.sex, p1.species
       FROM pet AS p1 INNER JOIN pet AS p2
         ON p1.species = p2.species
         AND p1.sex = 'f' AND p1.death IS NULL
         AND p2.sex = 'm' AND p2.death IS NULL;
+ -------- + ------ + ------- + ------ + --------- +
| 名字| 性别 名字| 性别 物种|
+ -------- + ------ + ------- + ------ + --------- +
| 蓬松| f | 爪子| m | 猫|
| 巴菲| f | 方| m | 狗|
+ -------- + ------ + ------- + ------ + --------- +

在此查询中,我们为表名指定别名以引用列,并保持每个列引用与表关联的表的实例。

3.4获取有关数据库和表的信息

如果您忘记了数据库或表的名称,或者给定表的结构是什么(例如,它的列被调用),该怎么办? MySQL通过几个语句来解决这个问题,这些语句提供有关它支持的数据库和表的信息。

您之前看到过 SHOW DATABASES ,它列出了服务器管理的数据库。 要找出当前选择的数据库,请使用以下 DATABASE() 函数:

MySQL的> SELECT DATABASE();
+ ------------ +
| 数据库()|
+ ------------ +
| 动物园|
+ ------------ +

如果您尚未选择任何数据库,则结果为 NULL 。

要找出默认数据库包含的表(例如,当您不确定表的名称时),请使用以下语句:

MySQL的> SHOW TABLES;
+ --------------------- +
| Tables_in_menagerie |
+ --------------------- +
| 事件|
| 宠物|
+ --------------------- +

此语句生成的输出中的列名称始终为 ,其中 是数据库的名称。 有关 更多信息 , 请参见 第13.7.6.37节“SHOW TABLES语法” 。 Tables_in_db_name db_name

如果要查找表的结构,该 DESCRIBE 语句很有用; 它显示有关每个表列的信息:

MySQL的> DESCRIBE pet;
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +
| 领域| 输入| 空| 钥匙| 默认| 额外的|
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +
| 名字| varchar(20)| 是的| | NULL | |
| 老板| varchar(20)| 是的| | NULL | |
| 物种| varchar(20)| 是的| | NULL | |
| 性别 char(1)| 是的| | NULL | |
| 出生| 日期| 是的| | NULL | |
| 死亡| 日期| 是的| | NULL | |
+ --------- + ------------- + ------ + ------ + --------- +  - ----- +

Field 表示列名称, Type 是 列 的数据类型, NULL 指示 列 是否可以包含 NULL 值, Key 指示列是否已编制索引,并 Default 指定列的默认值。 Extra 显示有关列的特殊信息:如果使用该 AUTO_INCREMENT 选项 创建了列 ,则该值将为 auto_increment 空而不是空。

DESC 是一种简短的形式 DESCRIBE 。 有关 更多信息 , 请参见 第13.8.1节“DESCRIBE语法” 。

您可以 CREATE TABLE 使用该 SHOW CREATE TABLE 语句 获取 创建现有表所需的 语句。 请参见 第13.7.6.10节“SHOW CREATE TABLE语法” 。

如果表上有索引,则 生成有关它们的信息。 有关此语句的更多信息 , 请参见 第13.7.6.22节“SHOW INDEX语法” 。 SHOW INDEX FROM tbl_name

3.5在批处理模式下使用mysql

在前面的部分中,您以 交互方式 使用 mysql 输入语句并查看结果。 您也可以 在批处理模式下 运行 mysql 。 为此,将要运行的语句放在文件中,然后告诉 mysql 从文件中读取其输入:

外壳> mysql < batch-file

如果您 在Windows下 运行 mysql 并在文件中有一些导致问题的特殊字符,您可以这样做:

C:\> mysql -e "source batch-file"

如果需要在命令行上指定连接参数,则命令可能如下所示:

shell> 
输入密码:mysql -h host -u user -p < batch-file********

当您以 这种方式 使用 mysql时 ,您将创建一个脚本文件,然后执行该脚本。

如果您希望脚本继续运行,即使其中的某些语句产生错误,您也应该使用 --force 命令行选项。

为什么要使用脚本? 原因如下:

  • 如果您反复运行查询(例如,每天或每周),使其成为脚本使您可以避免每次执行它时重新输入它。

  • 您可以通过复制和编辑脚本文件从现有的查询生成新查询。

  • 在开发查询时,批处理模式也很有用,特别是对于多行语句或多语句序列。 如果你犯了一个错误,你不必重新输入所有内容。 只需编辑脚本以更正错误,然后告诉 mysql 再次执行它。

  • 如果您有一个产生大量输出的查询,您可以通过寻呼机运行输出,而不是看着它从屏幕顶部滚动:

    外壳> mysql < batch-file | more
    
  • 您可以捕获文件中的输出以进行进一步处理:

    外壳> mysql < batch-file > mysql.out
    
  • 您可以将脚本分发给其他人,以便他们也可以运行语句。

  • 某些情况不允许交互式使用,例如,当您从 cron 作业 运行查询时 。 在这种情况下,您必须使用批处理模式。

在批处理模式下 运行 mysql 时,默认输出格式与 交互使用时 的默认输出格式不同(更简洁) 。 例如, SELECT DISTINCT species FROM pet 当 mysql 以交互方式运行 时 ,输出 看起来像这样 :

+ --------- +
| 物种|
+ --------- +
| 鸟|
| 猫|
| 狗|
| 仓鼠|
| 蛇|
+ --------- +

在批处理模式下,输出看起来像这样:

种类
鸟
猫
狗
仓鼠
蛇

如果要以批处理模式获取交互式输出格式,请使用 mysql -t 。 要回显输出执行的语句,请使用 mysql -v 。

您还可以使用 命令或 命令 从 mysql 提示符中 使用脚本 : source \.

mysql> 
mysql>source filename;\. filename

有关 更多信息 , 请参见 第4.5.1.5节“从文本文件执行SQL语句” 。

3.6常见查询示例

以下是如何解决MySQL的一些常见问题的示例。

一些示例使用该表 shop 来保存某些交易商(交易商)的每个商品(商品编号)的价格。 假设每个交易者每篇文章只有一个固定价格,则( article , dealer )是记录的主键。

启动命令行工具 mysql 并选择一个数据库:

外壳> mysql your-database-name

要创建和填充示例表,请使用以下语句:

CREATE TABLE商店(
    文章INT UNSIGNED DEFAULT'0000'非空,
    经销商CHAR(20)DEFAULT''NOT NULL,
    价格DECIMAL(16,2)DEFAULT'0.00'非空,
    PRIMARY KEY(文章,经销商));
插入商店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 ORDER BY文章;

+ --------- + -------- + ------- +
| 文章| 经销商| 价格|
+ --------- + -------- + ------- +
| 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 |
+ --------- + -------- + ------- +

3.6.1列的最大值

“ 什么是最高的项目编号? ”

SELECT MAX(文章)AS article FROM shop;

+ --------- +
| 文章|
+ --------- +
| 4 |
+ --------- +

3.6.2保持某一列最大值的行

任务:查找最昂贵文章的编号,经销商和价格。

使用子查询可以轻松完成此操作:

SELECT文章,经销商,价格
从商店
价格=(SELECT MAX(价格)FROM shop);

+ --------- + -------- + ------- +
| 文章| 经销商| 价格|
+ --------- + -------- + ------- +
| 0004 | D | 19.95 |
+ --------- + -------- + ------- +

其他解决方案是使用a LEFT JOIN 或按价格降序排序所有行,并使用特定于MySQL的 LIMIT 子句 获取第一行 :

SELECT s1.article,s1.dealer,s1.price
从商店s1
LEFT JOIN shop s2 ON s1.price <s2.price
WHERE.s2.icle是IS NULL;

SELECT文章,经销商,价格
从商店
按订单价格DESC
限制1;
注意

如果有几个最昂贵的文章,每个价格为19.95, LIMIT 解决方案只会显示其中一个。

3.6.3每组最大列数

任务:找到每篇文章的最高价格。

SELECT文章,MAX(价格)AS价格
从商店
GROUP BY文章
ORDER BY文章;

+ --------- + ------- +
| 文章| 价格|
+ --------- + ------- +
| 0001 | 3.99 |
| 0002 | 10.99 |
| 0003 | 1.69 |
| 0004 | 19.95 |
+ --------- + ------- +

3.6.4保持某一列的分组最大值的行

任务:对于每篇文章,找到价格最贵的经销商或经销商。

这个问题可以通过像这样的子查询来解决:

SELECT文章,经销商,价格
从商店s1
价格=(SELECT MAX(s2.price)
              从商店s2
              在哪里s1.article = s2.article)
ORDER BY文章;

+ --------- + -------- + ------- +
| 文章| 经销商| 价格|
+ --------- + -------- + ------- +
| 0001 | B | 3.99 |
| 0002 | A | 10.99 |
| 0003 | C | 1.69 |
| 0004 | D | 19.95 |
+ --------- + -------- + ------- +

前面的示例使用相关子查询,这可能是低效的(请参见 第13.2.11.7节“相关子查询” )。 解决问题的其他可能性是在 FROM 子句中 使用不相关的子查询 ,a LEFT JOIN 或具有窗口函数的公用表表达式。

不相关的子查询:

SELECT s1.article,dealer,s1.price
从商店s1
加入(
  SELECT文章,MAX(价格)AS价格
  从商店
  GROUP BY文章)AS s2
  ON s1.article = s2.article AND s1.price = s2.price
ORDER BY文章;

LEFT JOIN :

SELECT s1.article,s1.dealer,s1.price
从商店s1
LEFT JOIN shop s2 ON s1.article = s2.article AND s1.price <s2.price
在哪里s2.article IS NULL
ORDER BY s1.article;

的 LEFT JOIN 基础上的作品,当 s1.price 是在其最大价值,不存在 s2.price 具有更大价值,从而相应的 s2.article 值 NULL 。 请参见 第13.2.10.2节“JOIN语法” 。

带窗口函数的公用表表达式:

与s1 AS(
   选择文章,经销商,价格,
          RANK()(文章分区)
                           按订单价格DESC
                      )AS`排名`
     从商店
)
SELECT文章,经销商,价格
  从s1
  在哪里`Rank` = 1
ORDER BY文章;

3.6.5使用用户定义的变量

您可以使用MySQL用户变量来记住结果,而无需将它们存储在客户端的临时变量中。 (参见 第9.4节“用户定义的变量” 。)

例如,要查找价格最高和最低的文章,您可以执行以下操作:

mysql> SELECT @min_price:=MIN(price),@max_price:=MAX(price) FROM shop;
mysql>SELECT * FROM shop WHERE price=@min_price OR price=@max_price;
+ --------- + -------- + ------- +
| 文章| 经销商| 价格|
+ --------- + -------- + ------- +
| 0003 | D | 1.25 |
| 0004 | D | 19.95 |
+ --------- + -------- + ------- +
注意

也可以在用户变量中存储数据库对象(如表或列)的名称,然后在SQL语句中使用此变量; 但是,这需要使用准备好的声明。 有关 更多信息 , 请参见 第13.5节“准备好的SQL语句语法” 。

3.6.6使用外键

在MySQL中, InnoDB 表支持检查外键约束。 请参见 第15章, InnoDB存储引擎 和 第1.8.2.3节“外键差异” 。

仅仅为了连接两个表,不需要外键约束。 对于除以外的存储引擎 InnoDB ,可以在定义列时使用 没有实际效果 的 子句,并且 仅作为备忘录或注释,您当前定义的列旨在引用列中的列。另一张桌子 。 在使用以下语法时要意识到: REFERENCES tbl_name(col_name)

  • MySQL不执行任何类型的检查以确保 col_name 实际存在 tbl_name (或甚至 tbl_name 本身存在)。

  • MySQL不执行任何类型的操作, tbl_name 例如删除行以响应对您定义的表中的行所采取的操作; 换句话说,这种语法不会引起任何 行为 ON DELETE 或 ON UPDATE 行为。 (虽然您可以将 ON DELETE or或 ON UPDATE 子句 写为 子句的一部分 REFERENCES ,但也会被忽略。)

  • 此语法创建一个 列 ; 它并 不会 创建任何类型的索引或关键的。

您可以使用如此创建的列作为连接列,如下所示:

CREATE TABLE人(
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    name CHAR(60)NOT NULL,
    PRIMARY KEY(id)
);

CREATE TABLE衬衫(
    id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
    风格ENUM('t恤','polo','连衣裙')NOT NULL,
    颜色ENUM('红色','蓝色','橙色','白色','黑色')NOT NULL,
    所有者SMALLINT UNSIGNED NOT NULL参考人(id),
    PRIMARY KEY(id)
);

INSERT INTO VALUES(NULL,'Antonio Paz');

SELECT @last:= LAST_INSERT_ID();

插入衬衫VALUES
(NULL,'polo','blue',@ are),
(NULL,'dress','white',@ are),
(NULL,'t-shirt','blue',@ pop);

INSERT INTO VALUES(NULL,'Lilliana Angelovska');

SELECT @last:= LAST_INSERT_ID();

插入衬衫VALUES
(NULL,'dress','orange',@ are),
(NULL,'polo','red',@ are),
(NULL,'dress','blue',@ are),
(NULL,'t-shirt','white',@ are);

SELECT * FROM person;
+ ---- + --------------------- +
| id | 名字|
+ ---- + --------------------- +
| 1 | 安东尼奥帕兹|
| 2 | Lilliana Angelovska |
+ ---- + --------------------- +

SELECT * FROM shirt;
+ ---- + --------- + -------- + ------- +
| id | 风格| 颜色| 老板|
+ ---- + --------- + -------- + ------- +
| 1 | polo | 蓝色| 1 |
| 2 | 连衣裙| 白色| 1 |
| 3 | T恤| 蓝色| 1 |
| 4 | 连衣裙| 橙色| 2 |
| 5 | polo | 红色| 2 |
| 6 | 连衣裙| 蓝色| 2 |
| 7 | T恤| 白色| 2 |
+ ---- + --------- + -------- + ------- +


SELECT s。* FROM person p INNER JOIN衬衫s
   ON s.owner = p.id.
 在哪里p.name LIKE'Lilliana%'
   AND s.color <>'white';

+ ---- + ------- + -------- + ------- +
| id | 风格| 颜色| 老板|
+ ---- + ------- + -------- + ------- +
| 4 | 连衣裙| 橙色| 2 |
| 5 | polo | 红色| 2 |
| 6 | 连衣裙| 蓝色| 2 |
+ ---- + ------- + -------- + ------- +

当以这种方式使用时, REFERENCES 子句中不显示的输出 SHOW CREATE TABLE 或 DESCRIBE :

SHOW CREATE TABLE衬衫\ G
*************************** 1。排******************** *******
表:衬衫
创建表:CREATE TABLE`衬衣`(
`id`minintint(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 = utf8mb4

REFERENCES 以这种方式 使用 列定义中 的注释或 “ 提醒 ” 可以使用 MyISAM 表。

3.6.7搜索两个密钥

一个 OR 使用单个密钥被很好地优化,因为是的处理 AND 。

一个棘手的案例是搜索两个不同的键结合 OR :

SELECT field1_index,field2_index FROM test_table
WHERE field1_index ='1'或field2_index ='1'

这种情况已经过优化。 请参见 第8.2.1.3节“索引合并优化” 。

您还可以使用 UNION 结合两个单独 SELECT 语句 的输出的 有效方法来解决问题 。 请参见 第13.2.10.3节“UNION语法” 。

每个 SELECT 只搜索一个键,可以进行优化:

SELECT field1_index,field2_index
    FROM test_table WHERE field1_index ='1'
联盟
SELECT field1_index,field2_index
    FROM test_table WHERE field2_index ='1';

3.6.8计算每日访问量

以下示例显示如何使用位组功能计算用户访问网页的每月天数。

CREATE TABLE t1(年份(4),月份INT UNSIGNED,
             当天INT UNSIGNED);
插入t1 VALUES(2000,1,1),(2000,1,20),(2000,1,30),(2000,2,2),
            (2000,2,23),(2000,2,23);

示例表包含表示用户对页面的访问的年 - 月 - 日值。 要确定这些访问每月发生的天数,请使用以下查询:

选择年,月,BIT_COUNT(BIT_OR(1 <<天))AS天从t1开始
       GROUP BY年,月;

哪个回报:

+ ------ + ------- + ------ +
| 一年| 月| 天|
+ ------ + ------- + ------ +
| 2000年| 1 | 3 |
| 2000年| 2 | 2 |
+ ------ + ------- + ------ +

该查询计算每个年/月组合在表中显示的天数,并自动删除重复的条目。

3.6.9使用AUTO_INCREMENT

该 AUTO_INCREMENT 属性可用于为新行生成唯一标识:

CREATE TABLE动物(
     id MEDIUMINT NOT NULL AUTO_INCREMENT,
     name CHAR(30)NOT NULL,
     PRIMARY KEY(id)
);

插入动物(名称)VALUES
    ( '狗'),( '猫'),( '企鹅'),
    ( '松懈'),( '鲸'),( '鸵鸟');

SELECT * FROM animals;

哪个回报:

+ ---- + --------- +
| id | 名字|
+ ---- + --------- +
| 1 | 狗|
| 2 | 猫|
| 3 | 企鹅|
| 4 | 松懈|
| 5 | 鲸鱼|
| 6 | 鸵鸟|
+ ---- + --------- +

没有为 AUTO_INCREMENT 列 指定值 ,因此MySQL会自动分配序列号。 除非 NO_AUTO_VALUE_ON_ZERO 启用 了 SQL模式 ,否则您还可以为列显式指定0以生成序列号 。 例如:

INSERT INTO animals(id,name)VALUES(0,'groundhog');

如果声明了列 NOT NULL ,则还可以分配 NULL 给列以生成序列号。 例如:

INSERT INTO animals(id,name)VALUES(NULL,'squirrel');

当您将任何其他值插入 AUTO_INCREMENT 列时,该列将设置为该值并重置序列,以便下一个自动生成的值按顺序从最大列值开始。 例如:

INSERT INTO animals (id,name) VALUES(100,'rabbit');
INSERT INTO animals (id,name) VALUES(NULL,'mouse');
SELECT * FROM animals;
+ ----- + ----------- +
| id | 名字|
+ ----- + ----------- +
| 1 | 狗|
| 2 | 猫|
| 3 | 企鹅|
| 4 | 松懈|
| 5 | 鲸鱼|
| 6 | 鸵鸟|
| 7 | 土拨鼠|
| 8 | 松鼠|
| 100 | 兔子|
| 101 | 小鼠|
+ ----- + ----------- +

更新现有 AUTO_INCREMENT 列值也会重置 AUTO_INCREMENT 序列。

您可以 AUTO_INCREMENT 使用 LAST_INSERT_ID() SQL函数或者 检索最近自动生成的 值 mysql_insert_id() C API函数 。 这些函数是特定于连接的,因此它们的返回值不受另一个也执行插入的连接的影响。

对于 AUTO_INCREMENT 足够大 的 列, 请使用最小的整数数据类型, 以保存所需的最大序列值。 当列达到数据类型的上限时,下一次生成序列号的尝试将失败。 UNSIGNED 如果可能, 请使用该 属性以允许更大的范围。 例如,如果你使用 TINYINT ,允许的最大序列号为127的 TINYINT UNSIGNED ,最大值为255见 第11.2.1节“整型(精确值) - INTEGER,INT,SMALLINT,TINYINT,MEDIUMINT,BIGINT” 为所有整数类型的范围。

注意

对于多行插入, LAST_INSERT_ID() 而 mysql_insert_id() 实际返回 AUTO_INCREMENT 从钥匙 第一 插入行的。 这使得多行插入可以在复制设置中的其他服务器上正确再现。

要以 AUTO_INCREMENT 1以外 的 值 开头,请 使用 CREATE TABLE 或 设置该值 ALTER TABLE ,如下所示:

MySQL的> ALTER TABLE tbl AUTO_INCREMENT = 100;

InnoDB备注

有关 AUTO_INCREMENT 特定用途的 信息 InnoDB ,请参见 第15.6.1.4节“InnoDB中的AUTO_INCREMENT处理” 。

MyISAM笔记

  • 对于 MyISAM 表,您可以 AUTO_INCREMENT 在多列索引中的辅助列上 指定 。 在这种情况下, AUTO_INCREMENT 列 的生成值 计算为 。 当您想要将数据放入有序组时,这非常有用。 MAX(auto_increment_column) + 1 WHERE prefix=given-prefix

    CREATE TABLE动物(
        grp ENUM('fish','哺乳动物','鸟')NOT NULL,
        id MEDIUMINT NOT NULL AUTO_INCREMENT,
        name CHAR(30)NOT NULL,
        PRIMARY KEY(grp,id)
    )ENGINE = MyISAM;
    
    插入动物(grp,名称)VALUES
        ( '哺乳动物', '狗'),( '哺乳动物', '猫'),
        ( '鸟', '企鹅'),( '鱼', '宽松'),( '哺乳动物', '鲸鱼'),
        ( '鸟', '鸵鸟');
    
    SELECT * FROM animals ORDER BY grp,id;
    

    哪个回报:

    + -------- + ---- + --------- +
    | grp | id | 名字|
    + -------- + ---- + --------- +
    | 鱼| 1 | 松懈|
    | 哺乳动物| 1 | 狗|
    | 哺乳动物| 2 | 猫|
    | 哺乳动物| 3 | 鲸鱼|
    | 鸟| 1 | 企鹅|
    | 鸟| 2 | 鸵鸟|
    + -------- + ---- + --------- +
    

    在这种情况下(当 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 请访问:

3.7在Apache中使用MySQL

有些程序可以让您从MySQL数据库中验证用户身份,还可以将日志文件写入MySQL表。

您可以通过将以下内容添加到Apache配置文件中来更改Apache日志记录格式,以便MySQL可以轻松读取:

LogFormat \
        “\”%h \“,%{%Y%m%d%H%M%S} t,%> s,\”%b \“,\”%{Content-Type} o \“,\
        \ “%U \”,\ “%{Referer的} I \”,\ “%{用户代理} I \””

要将该格式的日志文件加载到MySQL中,您可以使用如下语句:

LOAD DATA INFILE''INTO /local/access_logTABLEtbl_name
字段被',''''''''''''''''''''''''''''''''''''''''''

应创建指定的表,以使列与 LogFormat 行写入日志文件的 列相对应 。

原文