一、SQL概述
1、概念
对数据库进行查询和修改操作的语言叫做 SQL(Structured Query Language,结构化查询语言)。SQL 语言是目前广泛使用的关系数据库标准语言,是各种数据库交互方式的基础。
著名的大型商用数据库 Oracle、DB2、Sybase、SQL Server,开源的数据库 PostgreSQL、MySQL,甚至一些小型的数据库 Access 等都支持 SQL。近些年蓬勃发展的 NoSQL 系统最初是宣称不再需要 SQL 的,后来也不得不修正为 Not Only SQL,来拥抱 SQL。
SQL 是一种数据库查询和程序设计语言,用于存取数据以及查询、更新和管理关系数据库系统。与其他程序设计语言(如 C语言、Java 等)不同的是,SQL 由很少的关键字组成,每个 SQL 语句通过一个或多个关键字构成。
2、优点
-
一体化:SQL 集数据定义、数据操作和数据控制于一体,可以完成数据库中的全部工作。
-
使用方式灵活:SQL 具有两种使用方式,可以直接以命令方式交互使用;也可以嵌入使用,嵌入C、C++、Fortran、COBOL、Java 等语言中使用。
-
非过程化:只提操作要求,不必描述操作步骤,也不需要导航。使用时只需要告诉计算机“做什么”,而不需要告诉它“怎么做”,存储路径的选择和操作的执行由数据库管理系统自动完成。
-
语言简洁、语法简单:该语言的语句都是由描述性很强的英语单词组成,而且这些单词的数目不多。
3、常见的SQL语句
(1)数据定义语言(Data Definition Language,DDL)
用来创建或删除数据库以及表等对象,主要包含以下几种命令:
- DROP:删除数据库和表等对象
- CREATE:创建数据库和表等对象
- ALTER:修改数据库和表等对象的结构
(2)数据操作语言(Data Manipulation Language,DML)
用来变更表中的记录,主要包含以下几种命令:
- INSERT:向表中插入新数据
- UPDATE:更新表中的数据
- DELETE:删除表中的数据
(3)数据查询语言(Data Query Language,DQL)
用来查询表中的记录,主要包含 SELECT 命令,来查询表中的数据。
(4)数据控制语言(Data Control Language,DCL)
用来确认或者取消对数据库中的数据进行的变更。除此之外,还可以对数据库中的用户设定权限。主要包含以下几种命令:
- GRANT:赋予用户操作权限
- REVOKE:取消用户的操作权限
- COMMIT:确认对数据库中的数据进行的变更
- ROLLBACK:取消对数据库中的数据进行的变更
注意:SQL 语句不区分大小写,许多 SQL 开发人员习惯对 SQL 本身的关键字进行大写,而对表或者列的名称使用小写,这样可以提高代码的可阅读性和可维护性。本教程也按照这种方式组织 SQL 语句。大多数数据库都支持通用的 SQL 语句,同时不同的数据库具有各自特有的 SQL 语言特性。
4、标准SQL
-
标准 SQL 是指符合国际标准的 SQL,而非某个数据库厂商的 SQL 语法(如:Microsoft SQL Server 的 T-SQL,Oracle 的 PL/SQL,MySQL)。
-
标准 SQL 可以在任何数据库中使用,而数据库厂商的 SQL 只适合它们对应的数据库,如 T-SQL 只适合 Microsoft SQL Server。
-
本教程讲解的 SQL 是专门针对 MySQL 的,虽然多数语法也适用于其它 DBMS,但不是所有 SQL 语法都是完全可移植的。
5、SQL的书写规则
- SQL 语句要以分号
;结尾 - SQL语句不区分大小写,但是插入到表中的数据是区分大小写的;下表总结了 SQL 元素在 Windows 和 Linux 系统是否区分大小写。
- 正常情况下,每个公司都有一套自己的标准,这里建议:
表名、字段名使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
二、MySql数据库操作
1、查看数据库
数据库可以看作是一个专门存储数据对象的容器,每一个数据库都有唯一的名称,并且数据库的名称都是有实际意义的,这样就可以清晰的看出每个数据库用来存放什么数据。在 MySQL 数据库中存在系统数据库和自定义数据库,系统数据库是在安装 MySQL 后系统自带的数据库,自定义数据库是由用户定义创建的数据库。在 MySQL 中,可使用 SHOW DATABASES 语句来查看或显示当前用户权限范围以内的数据库。查看数据库的语法格式为:
SHOW DATABASES [LIKE \'数据库名\'];
语法说明如下:
- LIKE 从句是可选项,用于匹配指定的数据库名称。LIKE 从句可以部分匹配,也可以完全匹配。
- 数据库名由单引号
\' \'包围。
实例1:查看所有数据库
列出当前用户可查看的所有数据库:
mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sakila | | sys | | world | +--------------------+ 6 row in set (0.22 sec)
可以发现,在上面的列表中有 6 个数据库,它们都是安装 MySQL 时系统自动创建的,其各自功能如下:
- information_schema:主要存储了系统中的一些数据库对象信息,比如用户表信息、列信息、权限信息、字符集信息和分区信息等。
- mysql:MySQL 的核心数据库,类似于 SQL Server 中的 master 表,主要负责存储数据库用户、用户访问权限等 MySQL 自己需要使用的控制和管理信息。常用的比如在 mysql 数据库的 user 表中修改 root 用户密码。
- performance_schema:主要用于收集数据库服务器性能参数。
- sakila:MySQL 提供的样例数据库,该数据库共有 16 张表,这些数据表都是比较常见的,在设计数据库时,可以参照这些样例数据表来快速完成所需的数据表。
- sys:MySQL 5.7 安装完成后会多一个 sys 数据库。sys 数据库主要提供了一些视图,数据都来自于 performation_schema,主要是让开发者和使用者更方便地查看性能问题。
- world:world 数据库是 MySQL 自动创建的数据库,该数据库中只包括 3 张数据表,分别保存城市,国家和国家使用的语言等内容。
实例2:创建并查看数据库
先创建一个名为 test_db 的数据库:
mysql> CREATE DATABASE test_db; Query OK, 1 row affected (0.12 sec)
再使用 SHOW DATABASES 语句显示权限范围内的所有数据库名,如下所示:
mysql> SHOW DATABASES; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sakila | | sys | | test_db | | world | +--------------------+ 7 row in set (0.22 sec)
你看,刚才创建的数据库已经被显示出来了。
实例3:使用 LIKE 从句
先创建三个数据库,名字分别为 test_db、db_test、db_test_db。
1) 使用 LIKE 从句,查看与 test_db 完全匹配的数据库:
mysql> SHOW DATABASES LIKE \'test_db\'; +--------------------+ | Database (test_db) | +--------------------+ | test_db | +--------------------+ 1 row in set (0.03 sec)
2) 使用 LIKE 从句,查看名字中包含 test 的数据库:
mysql> SHOW DATABASES LIKE \'%test%\'; +--------------------+ | Database (%test%) | +--------------------+ | db_test | +--------------------+ | db_test_db | +--------------------+ | test_db | +--------------------+ 3 row in set (0.03 sec)
3) 使用 LIKE 从句,查看名字以 db 开头的数据库:
mysql> SHOW DATABASES LIKE \'db%\'; +----------------+ | Database (db%) | +----------------+ | db_test | +----------------+ | db_test_db | +----------------+ 2 row in set (0.03 sec)
4) 使用 LIKE 从句,查看名字以 db 结尾的数据库:
mysql> SHOW DATABASES LIKE \'%db\'; +----------------+ | Database (%db) | +----------------+ | db_test_db | +----------------+ | test_db | +----------------+ 2 row in set (0.03 sec)
2、创建数据库
在 MySQL 中,可以使用 CREATE DATABASE 语句创建数据库,语法格式如下:
CREATE DATABASE [IF NOT EXISTS] <数据库名> [[DEFAULT] CHARACTER SET <字符集名>] [[DEFAULT] COLLATE <校对规则名>];
[ ]中的内容是可选的。语法说明如下:
- <数据库名>:创建数据库的名称。MySQL 的数据存储区将以目录方式表示 MySQL 数据库,因此数据库名称必须符合操作系统的文件夹命名规则,不能以数字开头,尽量要有实际意义。注意在 MySQL 中不区分大小写。
- IF NOT EXISTS:在创建数据库之前进行判断,只有该数据库目前尚不存在时才能执行操作。此选项可以用来避免数据库已经存在而重复创建的错误。
- [DEFAULT] CHARACTER SET:指定数据库的字符集。指定字符集的目的是为了避免在数据库中存储的数据出现乱码的情况。如果在创建数据库时不指定字符集,那么就使用系统的默认字符集。
- [DEFAULT] COLLATE:指定字符集的默认校对规则。
MySQL 的字符集(CHARACTER)和校对规则(COLLATION)是两个不同的概念。字符集是用来定义 MySQL 存储字符串的方式,校对规则定义了比较字符串的方式。可参考https://www.cnblogs.com/qtiger/p/13039875.html
实例:创建 MySQL 数据库时指定字符集和校对规则
mysql> CREATE DATABASE IF NOT EXISTS test_db_char -> DEFAULT CHARACTER SET utf8 -> DEFAULT COLLATE utf8_chinese_ci; Query OK, 1 row affected (0.03 sec)
可以使用SHOW CREATE DATABASE查看 test_db_char 数据库的定义声明,发现该数据库的指定字符集为 utf8,运行结果如下所示:
mysql> SHOW CREATE DATABASE test_db_char; +--------------+-----------------------------------------------------+ | Database | Create Database | +--------------+-----------------------------------------------------+ | test_db_char | CREATE DATABASE `test_db_char` /*!40100 DEFAULT CHARACTER SET utf8 */ | +--------------+-----------------------------------------------------+ 1 row in set (0.00 sec)
3、修改数据库
在 MySQL 数据库中只能对数据库使用的字符集和校对规则进行修改,数据库的这些特性都储存在 db.opt 文件中。下面我们来介绍一下修改数据库的基本操作。在 MySQL 中,可以使用 ALTER DATABASE 来修改已经被创建或者存在的数据库的相关参数。修改数据库的语法格式为:
ALTER DATABASE [数据库名] { [ DEFAULT ] CHARACTER SET <字符集名> | [ DEFAULT ] COLLATE <校对规则名>}
语法说明如下:
- ALTER DATABASE 用于更改数据库的全局特性。
- 使用 ALTER DATABASE 需要获得数据库 ALTER 权限。
- 数据库名称可以忽略,此时语句对应于默认数据库。
- CHARACTER SET 子句用于更改默认的数据库字符集。
实例:
使用命令行工具将数据库 test_db 的指定字符集修改为 gb2312,默认校对规则修改为 gb2312_unicode_ci,输入 SQL 语句与执行结果如下所示:
mysql> ALTER DATABASE test_db -> DEFAULT CHARACTER SET gb2312 -> DEFAULT COLLATE gb2312_chinese_ci; mysql> SHOW CREATE DATABASE test_db; +----------+--------------------------------------------------------+ | Database | ALTER Database | +----------+--------------------------------------------------------+ | test_db | ALTER DATABASE `test_db` /*!40100 DEFAULT CHARACTER SET gb2312 */| +----------+--------------------------------------------------------+ 1 row in set (0.00 sec)
4、删除数据库
当数据库不再使用时应该将其删除,以确保数据库存储空间中存放的是有效数据。删除数据库是将已经存在的数据库从磁盘空间上清除,清除之后,数据库中的所有数据也将一同被删除。在 MySQL 中,当需要删除已创建的数据库时,可以使用 DROP DATABASE 语句。其语法格式为:
DROP DATABASE [ IF EXISTS ] <数据库名>
语法说明如下:
- <数据库名>:指定要删除的数据库名。
- IF EXISTS:用于防止当数据库不存在时发生错误。
- DROP DATABASE:删除数据库中的所有表格并同时删除数据库。使用此语句时要非常小心,以免错误删除。如果要使用 DROP DATABASE,需要获得数据库 DROP 权限。
注意:MySQL 安装后,系统会自动创建名为 information_schema 和 mysql 的两个系统数据库,系统数据库存放一些和数据库相关的信息,如果删除了这两个数据库,MySQL 将不能正常工作。使用 DROP DATABASE 命令时要非常谨慎,在执行该命令后,MySQL 不会给出任何提示确认信息。DROP DATABASE 删除数据库后,数据库中存储的所有数据表和数据也将一同被删除,而且不能恢复。因此最好在删除数据库之前先将数据库进行备份。
5、选择数据库
在 MySQL 中,USE 语句用来完成一个数据库到另一个数据库的跳转。当用 CREATE DATABASE 语句创建数据库之后,该数据库不会自动成为当前数据库,需要用 USE 来指定当前数据库。其语法格式为:
USE <数据库名>
该语句可以通知 MySQL 把<数据库名>所指示的数据库作为当前数据库。该数据库保持为默认数据库,直到语段的结尾,或者直到遇见一个不同的 USE 语句。 只有使用 USE 语句来指定某个数据库作为当前数据库之后,才能对该数据库及其存储的数据对象执行操作。
例 1
使用命令行工具将数据库 test_db 设置为默认数据库,输入的 SQL 语句与执行结果如下所示:
mysql> USE test_db;
Database changed
6、注释
下面主要介绍 MySQL 中 SQL 语句的注释方法。注释在 SQL 语句中用来标识说明或者注意事项的部分,对 SQL 的执行没有任何影响。因此,注释内容中无论是英文字母还是汉字都可以随意使用。MySQL 注释分为单行注释和多行注释,下面分别来介绍这两种注释。
(1)MySQL 单行注释
- 单行注释可以使用
#注释符,#注释符后直接加注释内容。格式如下:
#注释内容
单行注释使用注释符#的示例如下:
#从结果中删除重复行
SELECT DISTINCT product_id, purchase_price FROM Product;
- 单行注释可以使用
--注释符,--注释符后需要加一个空格,注释才能生效。格式如下:
-- 注释内容
单行注释使用注释符--的示例如下:
-- 从结果中删除重复行
SELECT DISTINCT product_id, purchase_price FROM Product;
#和--的区别就是:#后面直接加注释内容,而--的第 2 个破折号后需要跟一个空格符在加注释内容。
(2)MySQL 多行注释
多行注释使用/* */注释符。/*用于注释内容的开头,*/用于注释内容的结尾。多行注释格式如下:
/* 第一行注释内容 第二行注释内容 */
注释内容写在/*和*/之间,可以跨多行。多行注释的使用示例如下
/*这条SELECT语句, 会从结果中删除重复行*/ SELECT DISTINCT product_id, purchase_price FROM Product;
任何注释(单行注释和多行注释)都可以插在 SQL 语句中,且注释可以放在 SQL 语句中的任意位置。在 SQL 语句中插入单行注释,如下所示:
SELECT DISTINCT product_id, purchase_price -- 从结果中删除重复行。 FROM Product; SELECT DISTINCT product_id, purchase_price #从结果中删除重复行。 FROM Product;
在 SQL 语句中插入多行注释,如下所示:
SELECT DISTINCT product_id, purchase_price /* 这条SELECT语句, 会从结果中删除重复行。*/ FROM Product;
7、MySql帮助手册
在 MySQL 中,查看帮助的命令是 HELP,语法格式如下:
HELP 查询内容
其中,查询内容为要查询的关键字。
- 查询内容中不区分大小写。
- 查询内容中可以包含通配符“%”和“_”,效果与 LIKE 运算符执行的模式匹配操作含义相同。例如,
HELP \'rep%\'用来返回以 rep 开头的主题列表。 - 查询内容可以使单引号引起来,也可以不使用单引号,为避免歧义,最好使用单引号引起来。
使用 HELP 查询信息的具体示例如下。
1)查询帮助文档目录列表
可以通过 HELP contents 命令查看帮助文档的目录列表,运行结果如下:
mysql> HELP\'contents\'; You asked for help about help category: "Contents" For more information, type \'help <item>\', where <item> is one of the following categories: Account Management Administration Compound Statements Contents Data Definition Data Manipulation Data Types Functions Geographic Features Help Metadata Language Structure Plugins Procedures Storage Engines Table Maintenance Transactions User-Defined Functions Utility
2)查看具体内容
根据上面运行结果列出的目录,可以选择某一项进行查询。例如使用 HELP Data Types; 命令查看所支持的数据类型,运行结果如下:
mysql> HELP \'Data Types\'; You asked for help about help category: "Data Types" For more information, type \'help <item>\', where <item> is one of the following topics: AUTO_INCREMENT BIGINT BINARY BIT BLOB BLOB DATA TYPE BOOLEAN CHAR CHAR BYTE DATE DATETIME DEC DECIMAL DOUBLE DOUBLE PRECISION ENUM FLOAT INT INTEGER LONGBLOB LONGTEXT MEDIUMBLOB MEDIUMINT MEDIUMTEXT SET DATA TYPE SMALLINT TEXT TIME TIMESTAMP TINYBLOB TINYINT TINYTEXT VARBINARY VARCHAR YEAR DATA TYPE
如果还想进一步查看某一数据类型,如 INT 类型,可以使用 HELP INT;命令,运行结果如下:
mysql> HELP \'INT\'; Name: \'INT\' Description: INT[(M)] [UNSIGNED] [ZEROFILL] A normal-size integer. The signed range is -2147483648 to 2147483647. The unsigned range is 0 to 4294967295. URL: https://dev.mysql.com/doc/refman/5.7/en/numeric-type-overview.html
运行结果中可以看到 INT 类型的帮助信息,包含类型描述、取值范围和官方手册中 INT 类型说明的 URL。
另外,还可以查询某命令,例如使用 HELP CREATE TABLE 命令查询创建数据表的语法,运行结果如下所示:
mysql> HELP \'CREATE TABLE\' Name: \'CREATE TABLE\' Description: Syntax: CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name (create_definition,...) [table_options] [partition_options] CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name [(create_definition,...)] [table_options] [partition_options] [IGNORE | REPLACE] [AS] query_expression
3)拓展
MySQL 提供了 4 张数据表来保存服务端的帮助信息,即使用 HELP 语法查看的帮助信息。执行语句就是从这些表中获取数据并返回给客户端的,MySQL 提供的 4 张数据表如下:
- help_category:关于帮助主题类别的信息
- help_keyword:与帮助主题相关的关键字信息
- help_relation:帮助关键字信息和主题信息之间的映射
- help_topic:帮助主题的详细内容
三、数据库设计
按照规范设计,将数据库的设计过程分为六个阶段:
-
系统需求分析阶段
-
概念结构设计阶段
-
逻辑结构设计阶段
-
物理结构设计阶段
-
数据库实施阶段
-
数据库运行与维护阶段
需求分析和概念结构设计独立于任何数据库管理系统。下面通过一个实例(报销类别管理)具体分析。
1、系统需求分析
需求分析是数据库设计的第一步,是最困难、最耗费时间的一步,也是整个设计过程的基础。本阶段的主要任务是对现实世界中要处理的对象(公司、部门及企业,也可以理解成客户)进行详细调查,然后通过分析,逐步明确客户/用户对系统的需求,包括数据需求和业务处理需求。需求分析是否做的充分和准确,直接决定了在其上构建数据库大厦的速度与质量。需求分析做的不好,会导致整个数据库设计返工重做。
实例
需求:报销类别管理
详细需求:公司有很多报销信息,但是报销的类别都不相同,很杂乱,单独把报销的类别抽离出来,对报销的类别进行管理。
2、概念结构设计
概要设计是数据库设计的关键,通过综合、归纳与抽象用户需求,形成一个具体 DBMS 的概念模型,也就是绘制数据库的 E-R 图。E-R 图主要用于在项目团队内部,设计人员和客户之间进行沟通,确认需求信息的正确性和完整性。
实例
需求:报销类别管理的E-R图
实体:报销类别
实体表要记录的属性:报销类别的名称、报销类别的备注、报销类别的创建人、报销类别的更新人、报销类别的更新时间、报销类别的创建时间。
3、逻辑结构设计
将 E-R 图转换为多张表,进行逻辑设计,确认各表的主外键,并应用数据库设计的三大范式进行审核,对其优化。在这阶段,E-R 图非常重要。大家要学会根据各个实体定义的属性来画出总体的 E-R 图。
实例
需求:报销类别管理模型
由ER模型转化为的关系模型:
报销类别(类别编号、类别名称、类别备注、创建人、创建时间、更新人、更新时间)实体表
4、物理结构设计
经项目组开会讨论确定 E-R 图后,根据项目的技术实现,团队开发能力及项目的成本预算,选择具体的数据库 MySQL 进行物理实现。
实例
| 字段名 | 类型 | 宽度 | 小数 | 主键/索引 | 约束 | Null值 |
| 类别编号 | int | 11 | 主键 | |||
| 类别名称 | 文本型 | 25 | ||||
| 备注 | 文本型 | 50 | 是 | |||
| 创建人 | 文本型 | 50 | ||||
| 创建时间 | datetime | |||||
| 更新人 | 文本型 | 50 | ||||
| 更新时间 | datetime |
5、数据库实施
运用 DBMS 提供的数据语言(例如 SQL)、工具及宿主语言(例如 Java),根据逻辑设计和物理设计的结果建立数据库,编制与调试应用程序,组织数据入库,并进行试运行。
实例
报销类别表:
6、数据库运行与维护
数据库应用系统经过试运行后即可投入正式运行。在运行过程中必须不断地对其进行评价、调整与修改。
四、MySQL数据类型
MySQL 的数据类型有大概可以分为 5 种,分别是整数类型、浮点数类型和定点数类型、日期和时间类型、字符串类型、二进制类型等。注意:整数类型和浮点数类型可以统称为数值数据类型。
1、数值类型-整数类型
整数类型包括 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT不同的数据类型提供不同的取值范围,可以存储的值范围越大,所需的存储空间也会越大。
| 类型名称 | 说明 | 存储需求 |
|---|---|---|
| TINYINT | 很小的整数 | 1个字节 |
| SMALLINT | 小的整数 | 2个宇节 |
| MEDIUMINT | 中等大小的整数 | 3个字节 |
| INT (INTEGHR) | 普通大小的整数 | 4个字节 |
| BIGINT | 大整数 | 8个字节 |
从上表中可以看到,不同类型的整数存储所需的字节数不相同,占用字节数最小的是 TINYINT 类型,占用字节最大的是 BIGINT 类型,占用的字节越多的类型所能表示的数值范围越大。根据占用字节数可以求出每一种数据类型的取值范围。例如,TINYINT 需要 1 个字节(8bit)来存储,那么 TINYINT 无符号数的最大值为 28-1,即 255;TINYINT 有符号数的最大值为 27-1,即 127。其他类型的整数的取值范围计算方法相同,如下表所示。
| 类型名称 | 说明 | 存储需求 |
|---|---|---|
| TINYINT | -128〜127 | 0 〜255 |
| SMALLINT | -32768〜32767 | 0〜65535 |
| MEDIUMINT | -8388608〜8388607 | 0〜16777215 |
| INT (INTEGER) | -2147483648〜2147483647 | 0〜4294967295 |
| BIGINT | -9223372036854775808〜9223372036854775807 | 0〜18446744073709551615 |
提示:显示宽度和数据类型的取值范围是无关的。显示宽度只是指明 MySQL 最大可能显示的数字个数,数值的位数小于指定的宽度时会由空格填充。如果插入了大于显示宽度的值,只要该值不超过该类型整数的取值范围,数值依然可以插入,而且能够显示出来。例如,year 字段插入 19999,当使用 SELECT 查询该列值的时候,MySQL 显示的将是完整的带有 5 位数字的 19999,而不是 4 位数字的值。
其他整型数据类型也可以在定义表结构时指定所需的显示宽度,如果不指定,则系统为每一种类型指定默认的宽度值。
2、数值类型-小数类型
MySQL 中使用浮点数和定点数来表示小数。浮点类型有两种,分别是单精度浮点数(FLOAT)和双精度浮点数(DOUBLE);定点类型只有一种,就是 DECIMAL。浮点类型和定点类型都可以用(M, D)来表示,其中M称为精度,表示总共的位数;D称为标度,表示小数的位数。浮点数类型的取值范围为 M(1~255)和 D(1~30,且不能大于 M-2),分别表示显示宽度和小数位数。M 和 D 在 FLOAT 和DOUBLE 中是可选的,FLOAT 和 DOUBLE 类型将被保存为硬件所支持的最大精度。DECIMAL 的默认 D 值为 0、M 值为 10。下表中列出了 MySQL 中的小数类型和存储需求。
| 类型名称 | 说明 | 存储需求 |
|---|---|---|
| FLOAT | 单精度浮点数 | 4 个字节 |
| DOUBLE | 双精度浮点数 | 8 个字节 |
| DECIMAL (M, D),DEC | 压缩的“严格”定点数 | M+2 个字节 |
DECIMAL 类型不同于 FLOAT 和 DOUBLE。DOUBLE 实际上是以字符串的形式存放的,DECIMAL 可能的最大取值范围与 DOUBLE 相同,但是有效的取值范围由 M 和 D 决定。如果改变 M 而固定 D,则取值范围将随 M 的变大而变大。从上表中可以看到,DECIMAL 的存储空间并不是固定的,而由精度值 M 决定,占用 M+2 个字节。
FLOAT 类型的取值范围如下:
- 有符号的取值范围:-3.402823466E+38~-1.175494351E-38。
- 无符号的取值范围:0 和 -1.175494351E-38~-3.402823466E+38。
DOUBLE 类型的取值范围如下:
- 有符号的取值范围:-1.7976931348623157E+308~-2.2250738585072014E-308。
- 无符号的取值范围:0 和 -2.2250738585072014E-308~-1.7976931348623157E+308。
提示:不论是定点还是浮点类型,如果用户指定的精度超出精度范围,则会四舍五入进行处理。
FLOAT 和 DOUBLE 在不指定精度时,默认会按照实际的精度(由计算机硬件和操作系统决定),DECIMAL 如果不指定精度,默认为(10,0)。浮点数相对于定点数的优点是在长度一定的情况下,浮点数能够表示更大的范围;缺点是会引起精度问题。
3、日期/时间类型
包括 YEAR、TIME、DATE、DATETIME 和 TIMESTAMP。当只记录年信息的时候,可以只使用 YEAR 类型。每一个类型都有合法的取值范围,当指定确定不合法的值时,系统将“零”值插入数据库中。下表中列出了 MySQL 中的日期与时间类型。
| 类型名称 | 日期格式 | 日期范围 | 存储需求 |
|---|---|---|---|
| YEAR | YYYY | 1901 ~ 2155 | 1 个字节 |
| TIME | HH:MM:SS | -838:59:59 ~ 838:59:59 | 3 个字节 |
| DATE | YYYY-MM-DD | 1000-01-01 ~ 9999-12-3 | 3 个字节 |
| DATETIME | YYYY-MM-DD HH:MM:SS | 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 | 8 个字节 |
| TIMESTAMP | YYYY-MM-DD HH:MM:SS | 1980-01-01 00:00:01 UTC ~ 2040-01-19 03:14:07 UTC | 4 个字节 |
(1)YEAR 类型
YEAR 类型是一个单字节类型,用于表示年,在存储时只需要 1 个字节。可以使用各种格式指定 YEAR,如下所示:
- 以 4 位字符串或者 4 位数字格式表示的 YEAR,范围为 \'1901\'~\'2155\'。输入格式为 \'YYYY\' 或者 YYYY,例如,输入 \'2010\' 或 2010,插入数据库的值均为 2010。
- 以 2 位字符串格式表示的 YEAR,范围为 \'00\' 到 \'99\'。\'00\'~\'69\' 和 \'70\'~\'99\' 范围的值分别被转换为 2000~2069 和 1970~1999 范围的 YEAR 值。\'0\' 与 \'00\' 的作用相同。插入超过取值范围的值将被转换为 2000。
- 以 2 位数字表示的 YEAR,范围为 1~99。1~99 和 70~99 范围的值分别被转换为 2001~2069 和 1970~1999 范围的 YEAR 值。注意,在这里 0 值将被转换为 0000,而不是 2000。
提示:两位整数范围与两位字符串范围稍有不同。例如,插入 3000 年,读者可能会使用数字格式的 0 表示 YEAR,实际上,插入数据库的值为 0000,而不是所希望的 3000。只有使用字符串格式的 \'0\' 或 \'00\',才可以被正确解释为 3000,非法 YEAR值将被转换为 0000。
(2)TIME 类型
TIME 类型用于只需要时间信息的值,在存储时需要 3 个字节。格式为 HH:MM:SS。HH 表示小时,MM 表示分钟,SS 表示秒。TIME 类型的取值范围为 -838:59:59~838:59:59,小时部分如此大的原因是 TIME 类型不仅可以用于表示一天的时间(必须小于 24 小时),还可能是某个事件过去的时间或两个事件之间的时间间隔(可大于 24 小时,或者甚至为负)。可以使用各种格式指定 TIME 值,如下所示。
- \'D HH:MM:SS\' 格式的字符串。还可以使用这些“非严格”的语法:\'HH:MM:SS\'、\'HH:MM\'、\'D HH\' 或 \'SS\'。这里的 D 表示日,可以取 0~34 之间的值。在插入数据库时,D 被转换为小时保存,格式为 “D*24+HH”。
- \'HHMMSS\' 格式、没有间隔符的字符串或者 HHMMSS 格式的数值,假定是有意义的时间。例如,\'101112\' 被理解为\'10:11:12\',但是 \'106112\' 是不合法的(它有一个没有意义的分钟部分),在存储时将变为 00:00:00。
提示:为 TIME 列分配简写值时应注意:如果没有冒号,MySQL 解释值时,假定最右边的两位表示秒。(MySQL 解释 TIME 值为过去的时间而不是当前的时间)。例如,读者可能认为 \'1112\' 和 1112 表示 11:12:00(即 11 点过 12 分钟),但MySQL 将它们解释为 00:11:12(即 11 分 12 秒)。同样 \'12\' 和 12 被解释为00:00:12。相反,TIME 值中如果使用冒号则肯定被看作当天的时间,也就是说,\'11:12\' 表示 11:12:00,而不是 00:11:12。
(3)DATE 类型
DATE 类型用于仅需要日期值时,没有时间部分,在存储时需要 3 个字节。日期格式为 \'YYYY-MM-DD\',其中 YYYY 表示年,MM 表示月,DD 表示日。在给 DATE 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合 DATE 的日期格式即可。如下所示:
- 以 \'YYYY-MM-DD\' 或者 \'YYYYMMDD\' 字符中格式表示的日期,取值范围为 \'1000-01-01\'~\'9999-12-3\'。例如,输入 \'2015-12-31\' 或者 \'20151231\',插入数据库的日期为2015-12-31。
- 以 \'YY-MM-DD\' 或者 \'YYMMDD\' 字符串格式表示日期,在这里YY表示两位的年值。MySQL 解释两位年值的规则:\'00~69\' 范围的年值转换为 \'2000~2069\',\'70~99\' 范围的年值转换为 \'1970~1999\'。例如,输入 \'15-12-31\',插入数据库的日期为 2015-12-31;输入 \'991231\',插入数据库的日期为 1999-12-31。
- 以 YYMMDD 数字格式表示的日期,与前面相似,00~69 范围的年值转换为 2000~2069,80~99 范围的年值转换为 1980~1999。例如,输入 151231,插入数据库的日期为 2015-12-31,输入 991231,插入数据库的日期为 1999-12-31。
- 使用 CURRENT_DATE 或者 NOW(),插入当前系统日期。
提示:MySQL 允许“不严格”语法:任何标点符号都可以用作日期部分之间的间隔符。例如,\'98-11-31\'、\'98.11.31\'、\'98/11/31\'和\'98@11@31\' 是等价的,这些值也可以正确地插入数据库。
(4)DATETIME 类型
DATETIME 类型用于需要同时包含日期和时间信息的值,在存储时需要 8 个字节。日期格式为 \'YYYY-MM-DD HH:MM:SS\',其中 YYYY 表示年,MM 表示月,DD 表示日,HH 表示小时,MM 表示分钟,SS 表示秒。在给 DATETIME 类型的字段赋值时,可以使用字符串类型或者数字类型的数据插入,只要符合 DATETIME 的日期格式即可,如下所示。
- 以 \'YYYY-MM-DD HH:MM:SS\' 或者 \'YYYYMMDDHHMMSS\' 字符串格式表示的日期,取值范围为 \'1000-01-01 00:00:00\'~\'9999-12-3 23:59:59\'。例如,输入 \'2014-12-31 05:05:05\' 或者 \'20141231050505’,插入数据库的 DATETIME 值都为 2014-12-31 05:05:05。
- 以 \'YY-MM-DD HH:MM:SS\' 或者 \'YYMMDDHHMMSS\' 字符串格式表示的日期,在这里 YY 表示两位的年值。与前面相同,\'00~79\' 范围的年值转换为 \'2000~2079\',\'80~99\' 范围的年值转换为 \'1980~1999\'。例如,输入 \'14-12-31 05:05:05\',插入数据库的 DATETIME 为 2014-12-31 05:05:05;输入 141231050505,插入数据库的 DATETIME 为 2014-12-31 05:05:05。
- 以 YYYYMMDDHHMMSS 或者 YYMMDDHHMMSS 数字格式表示的日期和时间。例如,输入 20141231050505,插入数据库的 DATETIME 为 2014-12-31 05:05:05;输入 140505050505,插入数据库的 DATETIME 为 2014-12-31 05:05:05。
提示:MySQL 允许“不严格”语法:任何标点符号都可用作日期部分或时间部分之间的间隔符。例如,\'98-12-31 11:30:45\'、\'98.12.31 11+30+35\'、\'98/12/31 11*30*45\' 和 \'98@12@31 11^30^45\' 是等价的,这些值都可以正确地插入数据库。
(5)TIMESTAMP 类型
TIMESTAMP 的显示格式与 DATETIME 相同,显示宽度固定在 19 个字符,日期格式为 YYYY-MM-DD HH:MM:SS,在存储时需要 4 个字节。但是 TIMESTAMP 列的取值范围小于 DATETIME 的取值范围,为 \'1970-01-01 00:00:01\'UTC~\'2038-01-19 03:14:07\'UTC。在插入数据时,要保证在合法的取值范围内。
提示:协调世界时(英:Coordinated Universal Time,法:Temps Universel Coordonné)又称为世界统一时间、世界标准时间、国际协调时间。英文(CUT)和法文(TUC)的缩写不同,作为妥协,简称 UTC。
TIMESTAMP 与 DATETIME 除了存储字节和支持的范围不同外,还有一个最大的区别是:
- DATETIME 在存储日期数据时,按实际输入的格式存储,即输入什么就存储什么,与时区无关;
- 而 TIMESTAMP 值的存储是以 UTC(世界标准时间)格式保存的,存储时对当前时区进行转换,检索时再转换回当前时区。即查询时,根据当前时区的不同,显示的时间值是不同的。
提示:如果为一个 DATETIME 或 TIMESTAMP 对象分配一个 DATE 值,结果值的时间部分被设置为 \'00:00:00\',因此 DATE 值未包含时间信息。如果为一个 DATE 对象分配一个 DATETIME 或 TIMESTAMP 值,结果值的时间部分被删除,因此DATE 值未包含时间信息。
4、字符串类型
MySQL 中的字符串类型有 CHAR、VARCHAR、TINYTEXT、TEXT、MEDIUMTEXT、LONGTEXT、ENUM、SET 等。下表中列出了 MySQL 中的字符串数据类型,括号中的M表示可以为其指定长度。
| 类型名称 | 说明 | 存储需求 |
|---|---|---|
| CHAR(M) | 固定长度非二进制字符串 | M 字节,1<=M<=255 |
| VARCHAR(M) | 变长非二进制字符串 | L+1字节,在此,L< = M和 1<=M<=255 |
| TINYTEXT | 非常小的非二进制字符串 | L+1字节,在此,L<2^8 |
| TEXT | 小的非二进制字符串 | L+2字节,在此,L<2^16 |
| MEDIUMTEXT | 中等大小的非二进制字符串 | L+3字节,在此,L<2^24 |
| LONGTEXT | 大的非二进制字符串 | L+4字节,在此,L<2^32 |
| ENUM | 枚举类型,只能有一个枚举字符串值 | 1或2个字节,取决于枚举值的数目 (最大值为65535) |
| SET | 一个设置,字符串对象可以有零个或 多个SET成员 | 1、2、3、4或8个字节,取决于集合 成员的数量(最多64个成员) |
VARCHAR 和 TEXT 类型是变长类型,其存储需求取决于列值的实际长度(在前面的表格中用 L 表示),而不是取决于类型的最大可能尺寸。例如,一个 VARCHAR(10) 列能保存一个最大长度为 10 个字符的字符串,实际的存储需要字符串的长度 L 加上一个字节以记录字符串的长度。对于字符 “abcd”,L 是 4,而存储要求 5 个字节。
(1)CHAR 和 VARCHAR 类型
CHAR(M) 为固定长度字符串,在定义时指定字符串列长。当保存时,在右侧填充空格以达到指定的长度。M 表示列的长度,范围是 0~255 个字符。例如,CHAR(4) 定义了一个固定长度的字符串列,包含的字符个数最大为 4。当检索到 CHAR 值时,尾部的空格将被删除。VARCHAR(M) 是长度可变的字符串,M 表示最大列的长度,M 的范围是 0~65535。VARCHAR 的最大实际长度由最长的行的大小和使用的字符集确定,而实际占用的空间为字符串的实际长度加 1。例如,VARCHAR(50) 定义了一个最大长度为 50 的字符串,如果插入的字符串只有 10 个字符,则实际存储的字符串为 10 个字符和一个字符串结束字符。VARCHAR 在值保存和检索时尾部的空格仍保留。
【实例】下面将不同的字符串保存到 CHAR(4) 和 VARCHAR(4) 列,说明 CHAR 和 VARCHAR 之间的差别,如下表所示。
| 插入值 | CHAR(4) | 存储需求 | VARCHAR(4) | 存储需求 |
|---|---|---|---|---|
| \' \' | \' \' | 4字节 | \'\' | 1字节 |
| \'ab\' | \'ab \' | 4字节 | \'ab\' | 3字节 |
| \'abc\' | \'abc \' | 4字节 | \'abc\' | 4字节 |
| \'abcd\' | \'abcd\' | 4字节 | \'abcd\' | 5字节 |
| \'abcdef\' | \'abcd\' | 4字节 | \'abcd\' | 5字节 |
对比结果可以看到,CHAR(4) 定义了固定长度为 4 的列,无论存入的数据长度为多少,所占用的空间均为 4 个字节。VARCHAR(4) 定义的列所占的字节数为实际长度加 1。
(2)TEXT 类型
TEXT 列保存非二进制字符串,如文章内容、评论等。当保存或查询 TEXT 列的值时,不删除尾部空格。TEXT 类型分为 4 种:TINYTEXT、TEXT、MEDIUMTEXT 和 LONGTEXT。不同的 TEXT 类型的存储空间和数据长度不同。
- TINYTEXT 表示长度为 255(28-1)字符的 TEXT 列。
- TEXT 表示长度为 65535(216-1)字符的 TEXT 列。
- MEDIUMTEXT 表示长度为 16777215(224-1)字符的 TEXT 列。
- LONGTEXT 表示长度为 4294967295 或 4GB(232-1)字符的 TEXT 列。
(3)ENUM 类型
ENUM 是一个字符串对象,值为表创建时列规定中枚举的一列值。其语法格式如下:
<字段名> ENUM( \'值1\', \'值1\', …, \'值n\' )
字段名指将要定义的字段,值 n 指枚举列表中第 n 个值。ENUM 类型的字段在取值时,能在指定的枚举列表中获取,而且一次只能取一个。如果创建的成员中有空格,尾部的空格将自动被删除。ENUM 值在内部用整数表示,每个枚举值均有一个索引值;列表值所允许的成员值从 1 开始编号,MySQL 存储的就是这个索引编号,枚举最多可以有 65535 个元素。
例如,定义 ENUM 类型的列(\'first\',\'second\',\'third\'),该列可以取的值和每个值的索引如下表所示。
| 值 | 索引 |
|---|---|
| NULL | NULL |
| \'\' | 0 |
| ’first | 1 |
| second | 2 |
| third | 3 |
ENUM 值依照列索引顺序排列,并且空字符串排在非空字符串前,NULL 值排在其他所有枚举值前。
提示:ENUM 列总有一个默认值。如果将 ENUM 列声明为 NULL,NULL 值则为该列的一个有效值,并且默认值为 NULL。如果 ENUM 列被声明为 NOT NULL,其默认值为允许的值列表的第 1 个元素。
(4)SET 类型
SET 是一个字符串的对象,可以有零或多个值,SET 列最多可以有 64 个成员,值为表创建时规定的一列值。指定包括多个 SET 成员的 SET 列值时,各成员之间用逗号,隔开,语法格式如下:
SET( \'值1\', \'值2\', …, \'值n\' )
与 ENUM 类型相同,SET 值在内部用整数表示,列表中每个值都有一个索引编号。当创建表时,SET 成员值的尾部空格将自动删除。但与 ENUM 类型不同的是,ENUM 类型的字段只能从定义的列值中选择一个值插入,而 SET 类型的列可从定义的列值中选择多个字符的联合。
提示:如果插入 SET 字段中的列值有重复,则 MySQL 自动删除重复的值;插入 SET 字段的值的顺序并不重要,MySQL 会在存入数据库时,按照定义的顺序显示;如果插入了不正确的值,默认情况下,MySQL 将忽视这些值,给出警告。
5、二进制类型
MySQL 中的二进制字符串有 BIT、BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB。下表中列出了 MySQL 中的二进制数据类型,括号中的M表示可以为其指定长度。
| 类型名称 | 说明 | 存储需求 |
|---|---|---|
| BIT(M) | 位字段类型 | 大约 (M+7)/8 字节 |
| BINARY(M) | 固定长度二进制字符串 | M 字节 |
| VARBINARY (M) | 可变长度二进制字符串 | M+1 字节 |
| TINYBLOB (M) | 非常小的BLOB | L+1 字节,在此,L<2^8 |
| BLOB (M) | 小 BLOB | L+2 字节,在此,L<2^16 |
| MEDIUMBLOB (M) | 中等大小的BLOB | L+3 字节,在此,L<2^24 |
| LONGBLOB (M) | 非常大的BLOB | L+4 字节,在此,L<2^32 |
(1)BIT 类型
位字段类型。M 表示每个值的位数,范围为 1~64。如果 M 被省略,默认值为 1。如果为 BIT(M) 列分配的值的长度小于 M 位,在值的左边用 0 填充。例如,为 BIT(6) 列分配一个值 b\'101\',其效果与分配 b\'000101\' 相同。BIT 数据类型用来保存位字段值,例如以二进制的形式保存数据 13,13 的二进制形式为 1101,在这里需要位数至少为 4 位的 BIT 类型,即可以定义列类型为 BIT(4)。大于二进制 1111 的数据是不能插入 BIT(4) 类型的字段中的。
提示:默认情况下,MySQL 不可以插入超出该列允许范围的值,因而插入数据时要确保插入的值在指定的范围内。
(2)BINARY 和 VARBINARY 类型
BINARY 和 VARBINARY 类型类似于 CHAR 和 VARCHAR,不同的是它们包含二进制字节字符串。使用的语法格式如下:
列名称 BINARY(M) 或者 VARBINARY(M)
BINARY 类型的长度是固定的,指定长度后,不足最大长度的,将在它们右边填充 “\0” 补齐,以达到指定长度。例如,指定列数据类型为 BINARY(3),当插入 a 时,存储的内容实际为 “\a0\0”,当插入 ab 时,实际存储的内容为“ab\0”,无论存储的内容是否达到指定的长度,存储空间均为指定的值 M。VARBINARY 类型的长度是可变的,指定好长度之后,长度可以在 0 到最大值之间。例如,指定列数据类型为 VARBINARY(20),如果插入的值长度只有 10,则实际存储空间为 10 加 1,实际占用的空间为字符串的实际长度加 1。
(3)BLOB 类型
BLOB 是一个二进制的对象,用来存储可变数量的数据。BLOB 类型分为 4 种:TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB,它们可容纳值的最大长度不同,如下表所示。
| 数据类型 | 存储范围 |
|---|---|
| TINYBLOB | 最大长度为255 (28-1)字节 |
| BLOB | 最大长度为65535 (216-1)字节 |
| MEDIUMBLOB | 最大长度为16777215 (224-1)字节 |
| LONGBLOB | 最大长度为4294967295或4GB (231-1)字节 |
BLOB 列存储的是二进制字符串(字节字符串),TEXT 列存储的是非进制字符串(字符字符串)。BLOB 列是字符集,并且排序和比较基于列值字节的数值;TEXT 列有一个字符集,并且根据字符集对值进行排序和比较。
6、MySQL数据类型选择
在选择数据类型时,首先要考虑这个列存放的值是什么类型的。一般来说,用数值类型列存储数字、用字符类型列存储字符串、用时态类型列存储日期和时间。
(1)数值类型
对于数值类型列,如果要存储的数字是整数(没有小数部分),则使用整数类型;如果要存储的数字是小数(带有小数部分),则可以选用 DECIMAL 或浮点类型,但是一般选择 FLOAT 类型(浮点类型的一种)。例如,如果列的取值范围是 1~99999 之间的整数,则 MEDIUMINT UNSIGNED 类型是最好的选择。MEDIUMINT 是整数类型,UNSIGNED 用来将数字类型无符号化。比如 INT 类型的取值范围是 -2 147 483 648 ~ 2 147 483 647,那么 INT UNSIGNED 类型的取值范围就是 0 ~ 4 294 967 295。如果需要存储某些整数值,则值的范围决定了可选用的数据类型。如果取值范围是 0~1000,那么可以选择 SMALLINT~BIGINT 之间的任何一种类型。如果取值范围超过了 200 万,则不能使用 SMALLINT,可以选择的类型变为从 MEDIUMINT 到 BIGINT 之间的某一种。 当然,完全可以为要存储的值选择一种最“大”的数据类型。但是,如果正确选择数据类型,不仅可以使表的存储空间变小,也会提高性能。因为与较长的列相比,较短的列的处理速度更快。当读取较短的值时,所需的磁盘读写操作会更少,并且可以把更多的键值放入内存索引缓冲区里。
如果无法获知各种可能值的范围,则只能靠猜测,或者使用 BIGINT 以满足最坏情况的需要。如果猜测的类型偏小,那么也不是就无药可救。将来,还可以使用 ALTER TABLE 让该列变得更大些。如果数值类型需要存储的数据为货币,如人民币。在计算时,使用到的值常带有元和分两个部分。它们看起来像是浮点值,但 FLOAT 和 DOUBLE 类型都存在四舍五入的误差问题,因此不太适合。因为人们对自己的金钱都很敏感,所以需要一个可以提供完美精度的数据类型。可以把货币表示成 DECIMAL(M,2) 类型,其中 M 为所需取值范围的最大宽度。这种类型的数值可以精确到小数点后 2 位。DECIMAL 的优点在于不存在舍入误差,计算是精确的。
对于电话号码、信用卡号和社会保险号都会使用非数字字符。因为空格和短划线不能直接存储到数字类型列里,除非去掉其中的非数字字符。但即使去掉了其中的非数字字符,也不能把它们存储成数值类型,以避免丢失开头的“零”。
(2)日期和时间类型
MySQL 对于不同种类的日期和时间都提供了数据类型,比如 YEAR 和 TIME。如果只需要记录年份,则使用 YEAR 类型即可;如果只记录时间,可以使用 TIME 类型。如果同时需要记录日期和时间,则可以使用 TIMESTAMP 或者 DATETIME 类型。由于TIMESTAMP 列的取值范围小于 DATETIME 的取值范围,因此存储较大的日期最好使用 DATETIME。TIMESTAMP 也有一个 DATETIME 不具备的属性。默认情况下,当插入一条记录但并没有指定 TIMESTAMP 这个列值时,MySQL 会把 TIMESTAMP 列设为当前的时间。因此当需要插入记录和当前时间时,使用 TIMESTAMP 是方便的,另外 TIMESTAMP 在空间上比 DATETIME 更有效。
MySQL 没有提供时间部分为可选的日期类型。DATE 没有时间部分,DATETIME 必须有时间部分。如果时间部分是可选的,那么可以使用 DATE 列来记录日期,再用一个单独的 TIME 列来记录时间。然后,设置 TIME 列可以为 NULL。SQL 语句如下:
CREATE TABLE mytb1 (
date DATE NOT NULL, #日期是必需的
time TIME NULL #时间可选(可能为NULL)
);
(3)字符串类型
字符串类型没有像数字类型列那样的“取值范围",但它们都有长度的概念。如果需要存储的字符串短于 256 个字符,那么可以使用 CHAR、VARCHAR 或 TINYTEXT。如果需要存储更长一点的字符串,则可以选用 VARCHAR 或某种更长的 TEXT 类型。如果某个字符串列用于表示某种固定集合的值,那么可以考虑使用数据类型 ENUM 或 SET。
(4)CHAR 和 VARCHAR 之间的特点和选择
CHAR 和 VARCHAR 的区别如下:
- CHAR 是固定长度字符,VARCHAR 是可变长度字符。
- CHAR 会自动删除插入数据的尾部空格,VARCHAR 不会删除尾部空格。
CHAR 是固定长度,所以它的处理速度比 VARCHAR 的速度要快,但是它的缺点就是浪费存储空间。所以对存储不大,但在速度上有要求的可以使用 CHAR 类型,反之可以使用 VARCHAR类型来实现。
存储引擎对于选择 CHAR 和 VARCHAR 的影响:
- 对于 MyISAM 存储引擎,最好使用固定长度的数据列代替可变长度的数据列。这样可以使整个表静态化,从而使数据检索更快,用空间换时间。
- 对于InnoDB存储引擎,最好使用可变长度的数据列,因为 InnoDB 数据表的存储格式不分固定长度和可变长度,因此使用 CHAR 不一定比使用 VARCHAR 更好,但由于 VARCHAR 是按照实际的长度存储,比较节省空间,所以对磁盘 I/O 和数据存储总量比较好。
(5)ENUM 和 SET
ENUM 只能取单值,它的数据列表是一个枚举集合。它的合法取值列表最多允许有 65 535个成员。因此,在需要从多个值中选取一个时,可以使用 ENUM。比如,性别字段适合定义,为 ENUM 类型,每次只能从‘男’或‘女’中取一个值。SET 可取多值。它的合法取值列表最多允许有 64 个成员。空字符串也是一个合法的 SET值。在需要取多个值的时候,适合使用 SET 类型,比如,要存储一个人兴趣爱好,最好使用SET类型。
ENUM 和 SET 的值是以字符串形式出现的,但在内部,MySQL 以数值的形式存储它们。
(6)二进制类型
BLOB 是二进制字符串,TEXT 是非二进制字符串,两者均可存放大容量的信息。BLOB 主要存储图片、音频信息等,而 TEXT 只能存储纯文本文件。
五、MySQL的基本操作
1、创建数据表
在 MySQL 中,可以使用 CREATE TABLE 语句创建表。其语法格式为:
CREATE TABLE <表名> ([表定义选项])[表选项][分区选项];
其中,[表定义选项]的格式为:
<列名1> <类型1> [,…] <列名n> <类型n>
CREATE TABLE 语句的主要语法及使用说明如下:
- CREATE TABLE:用于创建给定名称的表,必须拥有表CREATE的权限。
- <表名>:指定要创建表的名称,在 CREATE TABLE 之后给出,必须符合标识符命名规则。表名称被指定为 db_name.tbl_name,以便在特定的数据库中创建表。无论是否有当前数据库,都可以通过这种方式创建。在当前数据库中创建表时,可以省略 db-name。如果使用加引号的识别名,则应对数据库和表名称分别加引号。例如,\'mydb\'.\'mytbl\' 是合法的,但 \'mydb.mytbl\' 不合法。
- <表定义选项>:表创建定义,由列名(col_name)、列的定义(column_definition)以及可能的空值说明、完整性约束或表索引组成。
- 默认的情况是,表被创建到当前的数据库中。若表已存在、没有当前数据库或者数据库不存在,则会出现错误。
提示:使用 CREATE TABLE 创建表时,必须指定以下信息:
- 要创建的表的名称不区分大小写,不能使用SQL语言中的关键字,如DROP、ALTER、INSERT等。
- 数据表中每个列(字段)的名称和数据类型,如果创建多个列,要用逗号隔开。
实例
CREATE TABLE IF NOT EXISTS `runoob_tbl`(
`runoob_id` INT UNSIGNED AUTO_INCREMENT,
`runoob_title` VARCHAR(100) NOT NULL,
`runoob_author` VARCHAR(40) NOT NULL,
`submission_date` DATE,
PRIMARY KEY ( `runoob_id` )
)ENGINE=InnoDB DEFAULT CHARSET=utf8;
2、修改数据表
在 MySQL 中可以使用 ALTER TABLE 语句来改变原有表的结构,例如增加或删减列、更改原有列类型、重新命名列或表等。其语法格式如下:
ALTER TABLE <表名> [修改选项]
修改选项的语法格式如下:
{ ADD COLUMN <列名> <类型>
| CHANGE COLUMN <旧列名> <新列名> <新列类型>
| ALTER COLUMN <列名> { SET DEFAULT <默认值> | DROP DEFAULT }
| MODIFY COLUMN <列名> <类型>
| DROP COLUMN <列名>
| RENAME TO <新表名>
| CHARACTER SET <字符集名>
| COLLATE <校对规则名> }
实例
- 修改表名
ALTER TABLE <旧表名> RENAME [TO] <新表名>;
- 修改表字符集
ALTER TABLE 表名 [DEFAULT] CHARACTER SET <字符集名> [DEFAULT] COLLATE <校对规则名>;
3、修改字段名称
MySQL 中修改表字段名的语法规则如下:
ALTER TABLE <表名> CHANGE <旧字段名> <新字段名> <新数据类型>;
其中:
- 旧字段名:指修改前的字段名;
- 新字段名:指修改后的字段名;
- 新数据类型:指修改后的数据类型,如果不需要修改字段的数据类型,可以将新数据类型设置成与原来一样,但数据类型不能为空。
实例
mysql> ALTER TABLE tb_emp1 CHANGE col1 col3 CHAR(30); Query OK, 0 rows affected (0.76 sec)
4、修改字段数据类型
修改字段的数据类型就是把字段的数据类型转换成另一种数据类型。在 MySQL 中修改字段数据类型的语法规则如下:
ALTER TABLE <表名> MODIFY <字段名> <数据类型>
其中:
- 表名:指要修改数据类型的字段所在表的名称;
- 字段名:指需要修改的字段;
- 数据类型:指修改后字段的新数据类型。
实例
ALTER TABLE tb_emp1 MODIFY name VARCHAR(30); Query OK, 0 rows affected (0.15 sec)
4、删除字段
删除字段是将数据表中的某个字段从表中移除,语法格式如下:
ALTER TABLE <表名> DROP <字段名>;
其中,“字段名”指需要从表中删除的字段的名称。
实例
mysql> ALTER TABLE tb_emp1 DROP col2; Query OK, 0 rows affected (0.53 sec)
5、删除数据表
使用 DROP TABLE 语句可以删除一个或多个数据表,语法格式如下:
DROP TABLE [IF EXISTS] 表名1 [ ,表名2, 表名3 ...]
对语法格式的说明如下:
-
表名1, 表名2, 表名3 ...表示要被删除的数据表的名称。DROP TABLE 可以同时删除多个表,只要将表名依次写在后面,相互之间用逗号隔开即可。 - IF EXISTS 用于在删除数据表之前判断该表是否存在。如果不加 IF EXISTS,当数据表不存在时 MySQL 将提示错误,中断 SQL 语句的执行;加上 IF EXISTS 后,当数据表不存在时 SQL 语句可以顺利执行,但是会发出警告(warning)。
两点注意:
- 用户必须拥有执行 DROP TABLE 命令的权限,否则数据表不会被删除。
- 表被删除时,用户在该表上的权限不会自动删除。
6、查看表结构
(1)以表格的形式展示表结构
DESCRIBE/DESC 语句会以表格的形式来展示表的字段信息,包括字段名、字段数据类型、是否为主键、是否有默认值等,语法格式如下:
DESCRIBE <表名>;
或简写成:
DESC <表名>;
(2)以SQL语句的形式展示表结构
SHOW CREATE TABLE 的语法格式如下:
SHOW CREATE TABLE <表名>;
在 SHOW CREATE TABLE 语句的结尾处(分号前面)添加\g或者\G参数可以改变展示形式。
7、在末尾添加字段
一个完整的字段包括字段名、数据类型和约束条件。MySQL 添加字段的语法格式如下:
ALTER TABLE <表名> ADD <新字段名><数据类型>[约束条件];
对语法格式的说明如下:
- <表名> 为数据表的名字;
- <新字段名> 为所要添加的字段的名字;
- <数据类型> 为所要添加的字段能存储数据的数据类型;
- [约束条件] 是可选的,用来对添加的字段进行约束。
这种语法格式默认在表的最后位置(最后一列的后面)添加新字段。
实例
mysql> ALTER TABLE student ADD age INT(4) DEFAULT \'0\' COMMENT \'年龄\';
8、在开头添加字段
MySQL 默认在表的最后位置添加新字段,如果希望在开头位置(第一列的前面)添加新字段,那么可以使用 FIRST 关键字,语法格式如下:
ALTER TABLE <表名> ADD <新字段名> <数据类型> [约束条件] FIRST;
FIRST 关键字一般放在语句的末尾。
实例
mysql> ALTER TABLE student ADD stuId INT(4)DEFAULT \'0\' COMMENT \'编号\' FIRST;
9、在中间位置添加字段
MySQL 除了允许在表的开头位置和末尾位置添加字段外,还允许在中间位置(指定的字段之后)添加字段,此时需要使用 AFTER 关键字,语法格式如下:
ALTER TABLE <表名> ADD <新字段名> <数据类型> [约束条件] AFTER <已经存在的字段名>;
AFTER 的作用是将新字段添加到某个已有字段后面。注意,只能在某个已有字段的后面添加新字段,不能在它的前面添加新字段。
实例
mysql> ALTER TABLE student ADD stuno INT(11) AFTER name;
六、MySql约束
在 MySQL 中,约束是指对表中数据的一种约束,能够帮助数据库管理员更好地管理数据库,并且能够确保数据库中数据的正确性和有效性。例如,在数据表中存放年龄的值时,如果存入 200、300 这些无效的值就毫无意义了。因此,使用约束来限定表中的数据范围是很有必要的。主要支持以下 6 种约束:
-
主键约束:主键约束是使用最频繁的约束。在设计数据表时,一般情况下,都会要求表中设置一个主键。主键是表的一个特殊字段,该字段能唯一标识该表中的每条信息。例如,学生信息表中的学号是唯一的。
-
外键约束:外键约束经常和主键约束一起使用,用来确保数据的一致性。例如,一个水果摊,只有苹果、桃子、李子、西瓜 4 种水果,那么,你来到水果摊要买水果只能选择苹果、桃子、李子和西瓜,不能购买其它的水果。
-
唯一约束:唯一约束与主键约束有一个相似的地方,就是它们都能够确保列的唯一性。与主键约束不同的是,唯一约束在一个表中可以有多个,并且设置唯一约束的列是允许有空值的,虽然只能有一个空值。例如,在用户信息表中,要避免表中的用户名重名,就可以把用户名列设置为唯一约束。
-
检查约束:检查约束是用来检查数据表中,字段值是否有效的一个手段。例如,学生信息表中的年龄字段是没有负数的,并且数值也是有限制的。如果是大学生,年龄一般应该在 18~30 岁之间。在设置字段的检查约束时要根据实际情况进行设置,这样能够减少无效数据的输入。
-
非空约束:非空约束用来约束表中的字段不能为空。例如,在学生信息表中,如果不添加学生姓名,那么这条记录是没有用的。
-
默认值约束:默认值约束用来约束当数据表中某个字段不输入值时,自动为其添加一个已经设置好的值。
以上 6 种约束中,一个数据表中只能有一个主键约束,其它约束可以有多个。
1、主键约束
使用主键应注意以下几点:
- 每个表只能定义一个主键。
- 主键值必须唯一标识表中的每一行,且不能为 NULL,即表中不可能存在有相同主键值的两行数据。这是唯一性原则。
- 一个字段名只能在联合主键字段表中出现一次。
- 联合主键不能包含不必要的多余字段。当把联合主键的某一字段删除后,如果剩下的字段构成的主键仍然满足唯一性原则,那么这个联合主键是不正确的。这是最小化原则
(1)在创建表时设置主键约束
在创建数据表时设置主键约束,既可以为表中的一个字段设置主键,也可以为表中多个字段设置联合主键。但是不论使用哪种方法,在一个表中主键只能有一个。下面分别讲解设置单字段主键和多字段联合主键的方法。
-
设置单字段主键
在 CREATE TABLE 语句中,通过 PRIMARY KEY 关键字来指定主键。在定义字段的同时指定主键,语法格式如下:
<字段名> <数据类型> PRIMARY KEY [默认值]
实例
在 test_db 数据库中创建 tb_emp3 数据表,其主键为 id,SQL 语句和运行结果如下。
mysql> CREATE TABLE tb_emp3 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(25), -> deptId INT(11), -> salary FLOAT -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_emp3; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(25) | YES | | NULL | | | deptId | int(11) | YES | | NULL | | | salary | float | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.14 sec)
或者是在定义完所有字段之后指定主键,语法格式如下:
[CONSTRAINT <约束名>] PRIMARY KEY [字段名]
实例
在 test_db 数据库中创建 tb_emp4 数据表,其主键为 id,SQL 语句和运行结果如下。
mysql> CREATE TABLE tb_emp4 -> ( -> id INT(11), -> name VARCHAR(25), -> deptId INT(11), -> salary FLOAT, -> PRIMARY KEY(id) -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_emp4; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(25) | YES | | NULL | | | deptId | int(11) | YES | | NULL | | | salary | float | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (0.14 sec)
-
在创建表时设置联合主键
所谓的联合主键,就是这个主键是由一张表中多个字段组成的。比如,设置学生选课数据表时,使用学生编号做主键还是用课程编号做主键呢?如果用学生编号做主键,那么一个学生就只能选择一门课程。如果用课程编号做主键,那么一门课程只能有一个学生来选。显然,这两种情况都是不符合实际情况的。实际上设计学生选课表,要限定的是一个学生只能选择同一课程一次。因此,学生编号和课程编号可以放在一起共同作为主键,这也就是联合主键了。主键由多个字段联合组成,语法格式如下:
PRIMARY KEY [字段1,字段2,…,字段n]
注意:当主键是由多个字段组成时,不能直接在字段名后面声明主键约束。
实例
创建数据表 tb_emp5,假设表中没有主键 id,为了唯一确定一个员工,可以把 name、deptId 联合起来作为主键,SQL 语句和运行结果如下。
mysql> CREATE TABLE tb_emp5 -> ( -> name VARCHAR(25), -> deptId INT(11), -> salary FLOAT, -> PRIMARY KEY(name,deptId) -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_emp5; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | name | varchar(25) | NO | PRI | NULL | | | deptId | int(11) | NO | PRI | NULL | | | salary | float | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 3 rows in set (0.14 sec)
(2)在修改表时添加主键约束
主键约束不仅可以在创建表的同时创建,也可以在修改表时添加。但是需要注意的是,设置成主键约束的字段中不允许有空值。在修改数据表时添加主键约束的语法格式如下:
ALTER TABLE <数据表名> ADD PRIMARY KEY(<字段名>);
实例
修改数据表 tb_emp2,将字段 id 设置为主键,SQL 语句和运行结果如下。
mysql> ALTER TABLE tb_emp2 ADD PRIMARY KEY(id); Query OK, 0 rows affected (0.94 sec)
通常情况下,当在修改表时要设置表中某个字段的主键约束时,要确保设置成主键约束的字段中值不能够有重复的,并且要保证是非空的。否则,无法设置主键约束。
(3)删除主键约束
当一个表中不需要主键约束时,就需要从表中将其删除。删除主键约束的方法要比创建主键约束容易的多。删除主键约束的语法格式如下所示:
ALTER TABLE <数据表名> DROP PRIMARY KEY;
实例
mysql> ALTER TABLE tb_emp2 DROP PRIMARY KEY; Query OK, 0 rows affected (0.94 sec) Records: 0 Duplicates: 0 Warnings: 0
由于主键约束在一个表中只能有一个,因此不需要指定主键名就可以删除一个表中的主键约束。
2、主键自增长
通过给字段添加 AUTO_INCREMENT 属性来实现主键自增长。语法格式如下:
字段名 数据类型 AUTO_INCREMENT
- 默认情况下,AUTO_INCREMENT 的初始值是 1,每新增一条记录,字段值自动加 1。
- 一个表中只能有一个字段使用 AUTO_INCREMENT 约束,且该字段必须有唯一索引,以避免序号重复(即为主键或主键的一部分)。
- AUTO_INCREMENT 约束的字段必须具备 NOT NULL 属性。
- AUTO_INCREMENT 约束的字段只能是整数类型(TINYINT、SMALLINT、INT、BIGINT 等)。
- AUTO_INCREMENT 约束字段的最大值受该字段的数据类型约束,如果达到上限,AUTO_INCREMENT 就会失效。
实例
定义数据表 tb_student,指定表中 id 字段递增,SQL 语句和运行结果如下:
mysql> CREATE TABLE tb_student( -> id INT(4) PRIMARY KEY AUTO_INCREMENT, -> name VARCHAR(25) NOT NULL -> ); Query OK, 0 rows affected (0.07 sec)
上述语句执行成功后,会创建名为 tb_student 的数据表。其中,id 为主键,每插入一条新记录,id 的值就会在前一条记录的基础上自动加 1。name 为非空字段,该字段的值不能为空值(NULL)。加上 AUTO_INCREMENT 约束条件后,字段中的每个值都是自动增加的。因此,这个字段不可能出现相同的值。通常情况下,AUTO_INCREMENT 都是作为 id 字段的约束条件,并且将 id 字段作为表的主键。
(1)指定自增字段初始值
如果第一条记录设置了该字段的初始值,那么新增加的记录就从这个初始值开始自增。例如,如果表中插入的第一条记录的 id 值设置为 5,那么再插入记录时,id 值就会从 5 开始往上增加。
实例
下面创建表 tb_student2,指定主键从 100 开始自增长。SQL 语句和运行结果如下:
mysql> CREATE TABLE tb_student2 ( -> id INT NOT NULL AUTO_INCREMENT, -> name VARCHAR(20) NOT NULL, -> PRIMARY KEY(ID) -> )AUTO_INCREMENT=100; Query OK, 0 rows affected (0.03 sec)
(2)自增字段值不连续的情况分析
下面我们通过一个实例分析自增字段的值为什么不连续。
实例
创建表 tb_student3,其中 id 是自增主键字段,name 是唯一索引,SQL 语句和执行结果语句如下:
mysql> CREATE TABLE tb_student3( -> id INT PRIMARY KEY AUTO_INCREMENT, -> name VARCHAR(20) UNIQUE KEY, -> age INT DEFAULT NULL -> ); Query OK, 0 rows affected (0.04 sec)
向 tb_student3 表中插入数据,SQL 语句如下:
INSERT INTO tb_student3 VALUES(1,1,1);
此时,表 tb_student3 中已经有了(1,1,1)这条记录,这时再执行一条插入数据命令:
mysql> INSERT INTO tb_student3 VALUES(null,1,1); ERROR 1062 (23000): Duplicate entry \'1\' for key \'name\'
由于表中已经存在 name=1 的记录,所以报 Duplicate key error(唯一键冲突)。在这之后,再插入新的数据时,自增 id 就是 3,这样就出现了自增字段值不连续的情况。
3、外键约束
MySQL 外键约束(FOREIGN KEY)是表的一个特殊字段,经常与主键约束一起使用。对于两个具有关联关系的表而言,相关联字段中主键所在的表就是主表(父表),外键所在的表就是从表(子表)。外键用来建立主表与从表的关联关系,为两个表的数据建立连接,约束两个表中数据的一致性和完整性。比如,一个水果摊,只有苹果、桃子、李子、西瓜等 4 种水果,那么,你来到水果摊要买水果就只能选择苹果、桃子、李子和西瓜,其它的水果都是不能购买的。主表删除某条记录时,从表中与之对应的记录也必须有相应的改变。一个表可以有一个或多个外键,外键可以为空值,若不为空值,则每一个外键的值必须等于主表中主键的某个值。定义外键时,需要遵守下列规则:
- 主表必须已经存在于数据库中,或者是当前正在创建的表。如果是后一种情况,则主表与从表是同一个表,这样的表称为自参照表,这种结构称为自参照完整性。
- 必须为主表定义主键。
- 主键不能包含空值,但允许在外键中出现空值。也就是说,只要外键的每个非空值出现在指定的主键中,这个外键的内容就是正确的。
- 在主表的表名后面指定列名或列名的组合。这个列或列的组合必须是主表的主键或候选键。
- 外键中列的数目必须和主表的主键中列的数目相同。
- 外键中列的数据类型必须和主表主键中对应列的数据类型相同。
(1)在创建表时设置外键约束
在 CREATE TABLE 语句中,通过 FOREIGN KEY 关键字来指定外键,具体的语法格式如下:
[CONSTRAINT <外键名>] FOREIGN KEY 字段名 [,字段名2,…]
REFERENCES <主表名> 主键列1 [,主键列2,…]
实例
为了展现表与表之间的外键关系,本例在 test_db 数据库中创建一个部门表 tb_dept1,表结构如下表所示。创建 tb_dept1 的 SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_dept1 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(22) NOT NULL, -> location VARCHAR(50) -> ); Query OK, 0 rows affected (0.37 sec)
创建数据表 tb_emp6,并在表 tb_emp6 上创建外键约束,让它的键 deptId 作为外键关联到表 tb_dept1 的主键 id,SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_emp6 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(25), -> deptId INT(11), -> salary FLOAT, -> CONSTRAINT fk_emp_dept1 -> FOREIGN KEY(deptId) REFERENCES tb_dept1(id) -> ); Query OK, 0 rows affected (0.37 sec)
mysql> DESC tb_emp6; +--------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +--------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(25) | YES | | NULL | | | deptId | int(11) | YES | MUL | NULL | | | salary | float | YES | | NULL | | +--------+-------------+------+-----+---------+-------+ 4 rows in set (1.33 sec)
以上语句执行成功之后,在表 tb_emp6 上添加了名称为 fk_emp_dept1 的外键约束,外键名称为 deptId,其依赖于表 tb_dept1 的主键 id。
注意:从表的外键关联的必须是主表的主键,且主键和外键的数据类型必须一致。例如,两者都是 INT 类型,或者都是 CHAR 类型。如果不满足这样的要求,在创建从表时,就会出现“ERROR 1005(HY000): Can\'t create table”错误。
(2)在修改表时添加外键约束
外键约束也可以在修改表时添加,但是添加外键约束的前提是:从表中外键列中的数据必须与主表中主键列中的数据一致或者是没有数据。在修改数据表时添加外键约束的语法格式如下:
ALTER TABLE <数据表名> ADD CONSTRAINT <外键名>
FOREIGN KEY(<列名>) REFERENCES <主表名> (<列名>);
实例
修改数据表 tb_emp2,将字段 deptId 设置为外键,与数据表 tb_dept1 的主键 id 进行关联,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_emp2 -> ADD CONSTRAINT fk_tb_dept1 -> FOREIGN KEY(deptId) -> REFERENCES tb_dept1(id); Query OK, 0 rows affected (1.38 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW CREATE TABLE tb_emp2\G *************************** 1. row *************************** Table: tb_emp2 Create Table: CREATE TABLE `tb_emp2` ( `id` int(11) NOT NULL, `name` varchar(30) DEFAULT NULL, `deptId` int(11) DEFAULT NULL, `salary` float DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_tb_dept1` (`deptId`), CONSTRAINT `fk_tb_dept1` FOREIGN KEY (`deptId`) REFERENCES `tb_dept1` (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=gb2312 1 row in set (0.12 sec)
注意:在为已经创建好的数据表添加外键约束时,要确保添加外键约束的列的值全部来源于主键列,并且外键列不能为空。
(3)删除外键约束
当一个表中不需要外键约束时,就需要从表中将其删除。外键一旦删除,就会解除主表和从表间的关联关系。删除外键约束的语法格式如下所示:
ALTER TABLE <表名> DROP FOREIGN KEY <外键约束名>;
实例
mysql> ALTER TABLE tb_emp2
-> DROP FOREIGN KEY fk_tb_dept1;
4、唯一约束
MySQL 唯一约束(Unique Key)是指所有记录中字段的值不能重复出现。例如,为 id 字段加上唯一性约束后,每条记录的 id 值都是唯一的,不能出现重复的情况。如果其中一条记录的 id 值为‘0001’,那么该表中就不能出现另一条记录的 id 值也为‘0001’。唯一约束与主键约束相似的是它们都可以确保列的唯一性。不同的是,唯一约束在一个表中可有多个,并且设置唯一约束的列允许有空值,但是只能有一个空值。而主键约束在一个表中只能有一个,且不允许有空值。比如,在用户信息表中,为了避免表中用户名重名,可以把用户名设置为唯一约束。
(1)在创建表时设置唯一约束
唯一约束可以在创建表时直接设置,通常设置在除了主键以外的其它列上。在定义完列之后直接使用 UNIQUE 关键字指定唯一约束,语法格式如下:
<字段名> <数据类型> UNIQUE
实例
创建数据表 tb_dept2,指定部门的名称唯一,SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_dept2 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(22) UNIQUE, -> location VARCHAR(50) -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_dept2; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(40) | YES | UNI | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.08 sec)
(2)在修改表时添加唯一约束
在修改表时添加唯一约束的语法格式为:
ALTER TABLE <数据表名> ADD CONSTRAINT <唯一约束名> UNIQUE(<列名>);
实例
修改数据表 tb_dept1,指定部门的名称唯一,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept1 -> ADD CONSTRAINT unique_name UNIQUE(name); Query OK, 0 rows affected (0.63 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept1; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | NO | UNI | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
(3)删除唯一约束
在 MySQL 中删除唯一约束的语法格式如下:
ALTER TABLE <表名> DROP INDEX <唯一约束名>;
实例
删除数据表 tb_dept1 中的唯一约束 unique_name,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept1 -> DROP INDEX unique_name; Query OK, 0 rows affected (0.20 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept1; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | NO | | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
5、检查约束
MySQL 检查约束(CHECK)是用来检查数据表中字段值有效性的一种手段,可以通过 CREATE TABLE 或 ALTER TABLE 语句实现。设置检查约束时要根据实际情况进行设置,这样能够减少无效数据的输入。
(1)选取设置检查约束的字段
检查约束使用 CHECK 关键字,具体的语法格式如下:
CHECK <表达式>
其中,“表达式”指的就是 SQL 表达式,用于指定需要检查的限定条件。若将 CHECK 约束子句置于表中某个列的定义之后,则这种约束也称为基于列的 CHECK 约束。在更新表数据的时候,系统会检查更新后的数据行是否满足 CHECK 约束中的限定条件。MySQL 可以使用简单的表达式来实现 CHECK 约束,也允许使用复杂的表达式作为限定条件,例如在限定条件中加入子查询。
注意:若将 CHECK 约束子句置于所有列的定义以及主键约束和外键定义之后,则这种约束也称为基于表的 CHECK 约束。该约束可以同时对表中多个列设置限定条件。
(2)在创建表时设置检查约束
一般情况下,如果系统的表结构已经设计完成,那么在创建表时就可以为字段设置检查约束了。创建表时设置检查约束的语法格式如下:
CHECK(<检查约束>)
实例
在 test_db 数据库中创建 tb_emp7 数据表,要求 salary 字段值大于 0 且小于 10000,SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_emp7 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(25), -> deptId INT(11), -> salary FLOAT, -> CHECK(salary>0 AND salary<100), -> FOREIGN KEY(deptId) REFERENCES tb_dept1(id) -> ); Query OK, 0 rows affected (0.37 sec)
(3)在修改表时添加检查约束
如果一个表创建完成,可以通过修改表的方式为表添加检查约束。修改表时设置检查约束的语法格式如下:
ALTER TABLE tb_emp7 ADD CONSTRAINT <检查约束名> CHECK(<检查约束>)
实例
修改 tb_emp7 数据表,要求 id 字段值大于 0,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_emp7 -> ADD CONSTRAINT check_id -> CHECK(id>0); Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0
(4)删除检查约束
修改表时删除检查约束的语法格式如下:
ALTER TABLE <数据表名> DROP CONSTRAINT <检查约束名>;
实例
删除 tb_emp7 表中的 check_id 检查约束,SQL 语句和运行结果如下所示:
mysql> ALTER TABLE tb_emp7 -> DROP CONSTRAINT check_id; Query OK, 0 rows affected (0.19 sec) Records: 0 Duplicates: 0 Warnings: 0
6、默认值
默认值(Default)的完整称呼是“默认值约束(Default Constraint)”,用来指定某列的默认值。在表中插入一条新记录时,如果没有为某个字段赋值,系统就会自动为这个字段插入默认值。例如,员工信息表中,部门位置在北京的较多,那么部门位置就可以默认为“北京”,系统就会自动为这个字段赋值为“北京”。
默认值约束通常用在已经设置了非空约束的列,这样能够防止数据表在录入数据时出现错误。
(1)在创建表时设置默认值约束
创建表时可以使用 DEFAULT 关键字设置默认值约束,具体的语法格式如下:
<字段名> <数据类型> DEFAULT <默认值>;
其中,“默认值”为该字段设置的默认值,如果是字符类型的,要用单引号括起来。
实例
创建数据表 tb_dept3,指定部门位置默认为 Beijing,SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_dept3 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(22), -> location VARCHAR(50) DEFAULT \'Beijing\' -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_dept3; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | YES | | NULL | | | location | varchar(50) | YES | | Beijing | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.06 sec)
以上语句执行成功之后,表 tb_dept3 上的字段 location 拥有了一个默认值 Beijing,新插入的记录如果没有指定部门位置,则默认都为 Beijing。注意:在创建表时为列添加默认值,可以一次为多个列添加默认值,需要注意不同列的数据类型。
(2)在修改表时添加默认值约束
修改表时添加默认值约束的语法格式如下:
ALTER TABLE <数据表名>
CHANGE COLUMN <字段名> <数据类型> DEFAULT <默认值>;
实例
修改数据表 tb_dept3,将部门位置的默认值修改为 Shanghai,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept3 -> CHANGE COLUMN location -> location VARCHAR(50) DEFAULT \'Shanghai\'; Query OK, 0 rows affected (0.15 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept3; +----------+-------------+------+-----+----------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+----------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | YES | | NULL | | | location | varchar(50) | YES | | Shanghai | | +----------+-------------+------+-----+----------+-------+ 3 rows in set (0.00 sec)
(3)删除默认值约束
当一个表中的列不需要设置默认值时,就需要从表中将其删除。修改表时删除默认值约束的语法格式如下:
ALTER TABLE <数据表名>
CHANGE COLUMN <字段名> <字段名> <数据类型> DEFAULT NULL;
实例
修改数据表 tb_dept3,将部门位置的默认值约束删除,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept3 -> CHANGE COLUMN location -> location VARCHAR(50) DEFAULT NULL; Query OK, 0 rows affected (0.15 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept3; +----------+-------------+------+-----+----------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+----------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | YES | | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+----------+-------+ 3 rows in set (0.00 sec)
7、MySQL非空约束(NOT NULL)
MySQL 非空约束(NOT NULL)指字段的值不能为空。对于使用了非空约束的字段,如果用户在添加数据时没有指定值,数据库系统就会报错。可以通过 CREATE TABLE 或 ALTER TABLE 语句实现。在表中某个列的定义后加上关键字 NOT NULL 作为限定词,来约束该列的取值不能为空。比如,在用户信息表中,如果不添加用户名,那么这条用户信息就是无效的,这时就可以为用户名字段设置非空约束。
(1)在创建表时设置非空约束
创建表时可以使用 NOT NULL 关键字设置非空约束,具体的语法格式如下:
<字段名> <数据类型> NOT NULL;
实例
创建数据表 tb_dept4,指定部门名称不能为空,SQL 语句和运行结果如下所示。
mysql> CREATE TABLE tb_dept4 -> ( -> id INT(11) PRIMARY KEY, -> name VARCHAR(22) NOT NULL, -> location VARCHAR(50) -> ); Query OK, 0 rows affected (0.37 sec) mysql> DESC tb_dept3; +----------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+---------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | NO | | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+---------+-------+ 3 rows in set (0.06 sec)
(2)在修改表时添加非空约束
如果在创建表时忘记了为字段设置非空约束,也可以通过修改表进行非空约束的添加。修改表时设置非空约束的语法格式如下:
ALTER TABLE <数据表名>
CHANGE COLUMN <字段名>
<字段名> <数据类型> NOT NULL;
实例
修改数据表 tb_dept4,指定部门位置不能为空,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept4 -> CHANGE COLUMN location -> location VARCHAR(50) NOT NULL; Query OK, 0 rows affected (0.15 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept4; +----------+-------------+------+-----+----------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+----------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | NO | | NULL | | | location | varchar(50) | NO | | NULL | | +----------+-------------+------+-----+----------+-------+ 3 rows in set (0.00 sec)
(3)删除非空约束
修改表时删除非空约束的语法规则如下:
ALTER TABLE <数据表名>
CHANGE COLUMN <字段名> <字段名> <数据类型> NULL;
实例
修改数据表 tb_dept4,将部门位置的非空约束删除,SQL 语句和运行结果如下所示。
mysql> ALTER TABLE tb_dept4 -> CHANGE COLUMN location -> location VARCHAR(50) NULL; Query OK, 0 rows affected (0.15 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC tb_dept4; +----------+-------------+------+-----+----------+-------+ | Field | Type | Null | Key | Default | Extra | +----------+-------------+------+-----+----------+-------+ | id | int(11) | NO | PRI | NULL | | | name | varchar(22) | NO | | NULL | | | location | varchar(50) | YES | | NULL | | +----------+-------------+------+-----+----------+-------+ 3 rows in set (0.00 sec)
8、MySQL查看表中的约束
在 MySQL 中可以使用 SHOW CREATE TABLE 语句来查看表中的约束。查看数据表中的约束语法格式如下:
SHOW CREATE TABLE <数据表名>;
七、MySql操作表中数据
1、MySQL SELECT:数据表查询语句
在 MySQL 中,可以使用 SELECT 语句来查询数据。查询数据是指从数据库中根据需求,使用不同的查询方式来获取不同的数据,是使用频率最高、最重要的操作。SELECT 的语法格式如下:
SELECT {* | <字段列名>} [ FROM <表 1>, <表 2>… [WHERE <表达式> [GROUP BY <group by definition> [HAVING <expression> [{<operator> <expression>}…]] [ORDER BY <order by definition>] [LIMIT[<offset>,] <row count>] ]
其中,各条子句的含义如下:
-
{*|<字段列名>}包含星号通配符的字段列表,表示所要查询字段的名称。 -
<表 1>,<表 2>…,表 1 和表 2 表示查询数据的来源,可以是单个或多个。 -
WHERE <表达式>是可选项,如果选择该项,将限定查询数据必须满足该查询条件。 -
GROUP BY< 字段 >,该子句告诉 MySQL 如何显示查询出来的数据,并按照指定的字段分组。 -
[ORDER BY< 字段 >],该子句告诉 MySQL 按什么样的顺序显示查询出来的数据,可以进行的排序有升序(ASC)和降序(DESC),默认情况下是升序。 -
[LIMIT[<offset>,]<row count>],该子句告诉 MySQL 每次显示查询出来的数据条数。
2、MySQL使用DISTINCT过滤重复数据
DISTINCT 关键字的主要作用就是对数据表中一个或多个字段重复的数据进行过滤,只返回其中的一条数据给用户。DISTINCT 关键字的语法格式为:
SELECT DISTINCT <字段名> FROM <表名>;
其中,“字段名”为需要消除重复记录的字段名称,多个字段时用逗号隔开。使用 DISTINCT 关键字时需要注意以下几点:
- DISTINCT 关键字只能在 SELECT 语句中使用。
- 在对一个或多个字段去重时,DISTINCT 关键字必须在所有字段的最前面。
- 如果 DISTINCT 关键字后有多个字段,则会对多个字段进行组合去重,也就是说,只有多个字段组合起来完全是一样的情况下才会被去重。
实例
下面对 student 表的 age 字段进行去重,SQL 语句和运行结果如下:
mysql> SELECT DISTINCT age FROM student;
对 student 表的 name 和 age 字段进行去重,SQL 语句和运行结果如下:
mysql> SELECT DISTINCT name,age FROM student;
对 student 表中的所有字段进行去重,SQL 语句和运行结果如下:
mysql> SELECT DISTINCT * FROM student;
因为 DISTINCT 只能返回它的目标字段,而无法返回其它字段,所以在实际情况中,我们经常使用 DISTINCT 关键字来返回不重复字段的条数。查询 student 表中对 name 和 age 字段去重之后记录的条数,SQL 语句和运行结果如下:
mysql> SELECT COUNT(DISTINCT name,age) FROM student;
3、MySQL AS:设置别名
(1)为表指定别名
为表指定别名的基本语法格式为:
<表名> [AS] <别名>
其中各子句的含义如下:
-
<表名>:数据库中存储的数据表的名称。 -
<别名>:查询时指定的表的新名称。 -
AS关键字可以省略,省略后需要将表名和别名用空格隔开。
注意:表的别名不能与该数据库的其它表同名。字段的别名不能与该表的其它字段同名。在条件表达式中不能使用字段的别名,否则会出现“ERROR 1054 (42S22): Unknown column”这样的错误提示信息。
实例
mysql> SELECT stu.name,stu.height FROM tb_students_info AS stu;
(2)为字段指定别名
为字段指定别名的基本语法格式为:
<字段名> [AS] <别名>
其中,各子句的语法含义如下:
-
<字段名>:为数据表中字段定义的名称。 -
<字段别名>:字段新的名称。 -
AS关键字可以省略,省略后需要将字段名和别名用空格隔开。
实例
mysql> SELECT name AS student_name, age AS student_age FROM tb_students_info;
表别名只在执行查询时使用,并不在返回结果中显示。而字段定义别名之后,会返回给客户端显示,显示的字段为字段的别名。
4、MySQL LIMIT:限制查询结果的条数
LIMIT 是 MySQL 中的一个特殊关键字,用于指定查询结果从哪条记录开始显示,一共显示多少条记录。LIMIT 关键字有 3 种使用方式,即指定初始位置、不指定初始位置以及与 OFFSET 组合使用。
(1)指定初始位置
LIMIT 指定初始位置的基本语法格式如下:
LIMIT 初始位置,记录数
其中,“初始位置”表示从哪条记录开始显示;“记录数”表示显示记录的条数。第一条记录的位置是 0,第二条记录的位置是 1。后面的记录依次类推。注意:LIMIT 后的两个参数必须都是正整数。
实例
在 tb_students_info 表中,使用 LIMIT 子句返回从第 4 条记录开始的行数为 5 的记录,SQL 语句和运行结果如下。
SELECT * FROM tb_students_info LIMIT 3,5;
LIMIT 关键字后的第一个数字“3”表示从第 4 行开始(记录的位置从 0 开始,第 4 行的位置为 3),第二个数字 5 表示返回的行数。
(2)不指定初始位置
LIMIT 关键字不指定初始位置时,记录从第一条记录开始显示。显示记录的条数由 LIMIT 关键字指定。LIMIT 不指定初始位置的基本语法格式如下:
LIMIT 记录数
其中,“记录数”表示显示记录的条数。如果“记录数”的值小于查询结果的总数,则会从第一条记录开始,显示指定条数的记录。如果“记录数”的值大于查询结果的总数,则会直接显示查询出来的所有记录。
实例
显示 tb_students_info 表查询结果的前 4 行,SQL 语句和运行结果如下。
mysql> SELECT * FROM tb_students_info LIMIT 4;
结果中只显示了 4 条记录,说明“LIMIT 4”限制了显示条数为 4。
实例
显示 tb_students_info 表查询结果的前 15 行,SQL 语句和运行结果如下。
mysql> SELECT * FROM tb_students_info LIMIT 15; +----+--------+---------+------+------+--------+------------+ | id | name | dept_id | age | sex | height | login_date | +----+--------+---------+------+------+--------+------------+ | 1 | Dany | 1 | 25 | F | 160 | 2015-09-10 | | 2 | Green | 3 | 23 | F | 158 | 2016-10-22 | | 3 | Henry | 2 | 23 | M | 185 | 2015-05-31 | | 4 | Jane | 1 | 22 | F | 162 | 2016-12-20 | | 5 | Jim | 1 | 24 | M | 175 | 2016-01-15 | | 6 | John | 2 | 21 | M | 172 | 2015-11-11 | | 7 | Lily | 6 | 22 | F | 165 | 2016-02-26 | | 8 | Susan | 4 | 23 | F | 170 | 2015-10-01 | | 9 | Thomas | 3 | 22 | M | 178 | 2016-06-07 | | 10 | Tom | 4 | 23 | M | 165 | 2016-08-05 | +----+--------+---------+------+------+--------+------------+ 10 rows in set (0.26 sec)
结果中只显示了 10 条记录。虽然 LIMIT 关键字指定了显示 15 条记录,但是查询结果中只有 10 条记录。因此,数据库系统就将这 10 条记录全部显示出来。带一个参数的 LIMIT 指定从查询结果的首行开始,唯一的参数表示返回的行数,即“LIMIT n”与“LIMIT 0,n”返回结果相同。带两个参数的 LIMIT 可返回从任何位置开始指定行数的数据。
(3)LIMIT和OFFSET组合使用
LIMIT 可以和 OFFSET 组合使用,语法格式如下:
LIMIT 记录数 OFFSET 初始位置
参数和 LIMIT 语法中参数含义相同,“初始位置”指定从哪条记录开始显示;“记录数”表示显示记录的条数。
实例
在 tb_students_info 表中,使用 LIMIT OFFSET 返回从第 4 条记录开始的行数为 5 的记录,SQL 语句和运行结果如下。
mysql> SELECT * FROM tb_students_info LIMIT 5 OFFSET 3;
由结果可以看到,该语句返回的是从第 4 条记录开始的之后的 5 条记录。即“LIMIT 5 OFFSET 3”意思是获取从第 4 条记录开始的后面的 5 条记录,和“LIMIT 3,5”返回的结果相同。
5、MySQL ORDER BY:对查询结果排序
ORDER BY 关键字主要用来将查询结果中的数据按照一定的顺序进行排序。其语法格式如下:
ORDER BY <字段名> [ASC|DESC]
语法说明如下。
- 字段名:表示需要排序的字段名称,多个字段时用逗号隔开。
- ASC|DESC:
ASC表示字段按升序排序;DESC表示字段按降序排序。其中ASC为默认值。
使用 ORDER BY 关键字应该注意以下几个方面:
- ORDER BY 关键字后可以跟子查询
- 当排序的字段中存在空值时,ORDER BY 会将该空值作为最小值来对待。
- ORDER BY 指定多个字段进行排序时,MySQL 会按照字段的顺序从左到右依次进行排序。
(1)单字段排序
mysql> SELECT * FROM tb_students_info ORDER BY height;
(2)多字段排序
下面通过一个具体的实例来说明当 ORDER BY 指定多个字段时,MySQL 如何对查询结果进行排序。
例 2
mysql> SELECT name,height FROM tb_students_info ORDER BY height,name Desc;
注意:
- 在对多个字段进行排序时,排序的第一个字段必须有相同的值,才会对第二个字段进行排序。如果第一个字段数据中所有的值都是唯一的,MySQL 将不再对第二个字段进行排序。
- 多个字段时,各自定义升降序。比如上边语句,是先按照height升序(因为没有指定ASC或者DESC,所以默认升序),然后按照name降序。
实例
查询 tb_students_info 表,先按 height 降序排序,再按 name 升序排序,SQL 语句和运行结果如下。
mysql> SELECT name,height FROM tb_student_info ORDER BY height DESC,name ASC;
DESC 关键字只对前面的列进行降序排列,在这里只对 height 字段进行降序。因此,height 按降序排序,而 name 仍按升序排序。如果想在多个列上进行降序排序,必须对每个列指定 DESC 关键字。
6、MySQL WHERE:条件查询数据
使用 WHERE 关键字的语法格式如下:
WHERE 查询条件
查询条件可以是:
- 带比较运算符和逻辑运算符的查询条件
- 带 BETWEEN AND 关键字的查询条件
- 带 IS NULL 关键字的查询条件
- 带 IN 关键字的查询条件
- 带 LIKE 关键字的查询条件
在 WHERE 关键词后可以有多个查询条件,这样能够使查询结果更加精确。多个查询条件时用逻辑运算符 AND(&&)、OR(||)或 XOR 隔开。
- AND:记录满足所有查询条件时,才会被查询出来。
- OR:记录满足任意一个查询条件时,才会被查询出来。
- XOR:记录满足其中一个条件,并且不满足另一个条件时,才会被查询出来。
实例
在 tb_students_info 表中查询 age 大于 21,并且 height 小于 175 的学生信息和 age 小于 21,并且 height 大于等于 175 的学生信息,SQL 语句和运行结果如下。
mysql> SELECT name,age,height FROM tb_students_info -> WHERE age>21 XOR height>=175; +-------+------+--------+ | name | age | height | +-------+------+--------+ | Dany | 25 | 160 | | Green | 23 | 158 | | Jane | 22 | 162 | | Lily | 22 | 165 | | Susan | 23 | 170 | | Tom | 23 | 165 | +-------+------+--------+ 7 rows in set (0.00 sec)
可以看到,查询结果中所有记录的 age 字段都大于 21 且 height 字段都小于 175。tb_students_info 数据表中没有 age 字段小于 21 且 height 字段大于等于 175 的记录。
7、MySQL LIKE:模糊查询
在 MySQL 中,LIKE 关键字主要用于搜索匹配字段中的指定内容。其语法格式如下:
[NOT] LIKE \'字符串\'
其中:
- NOT :可选参数,字段中的内容与指定的字符串不匹配时满足条件。
- 字符串:指定用来匹配的字符串。“字符串”可以是一个很完整的字符串,也可以包含通配符。
LIKE 关键字支持百分号“%”和下划线“_”通配符。
通配符是一种特殊语句,主要用来模糊查询。当不知道真正字符或者懒得输入完整名称时,可以使用通配符来代替一个或多个真正的字符。
(1)带有“%”通配符的查询
“%”是 MySQL 中最常用的通配符,它能代表任何长度的字符串,字符串的长度可以为 0。例如,a%b表示以字母 a 开头,以字母 b 结尾的任意长度的字符串。该字符串可以代表 ab、acb、accb、accrb 等字符串。注意:匹配的字符串必须加单引号或双引号。NOT LIKE 表示字符串不匹配时满足条件。
(2)带有“_”通配符的查询
“_”只能代表单个字符,字符的长度不能为 0。例如,a_b可以代表 acb、adb、aub 等字符串。
实例
mysql> SELECT name FROM tb_students_info -> WHERE name LIKE \'____y\';
(3)LIKE 区分大小写
默认情况下,LIKE 关键字匹配字符的时候是不区分大小写的。如果需要区分大小写,可以加入 BINARY 关键字。
例 5
在 tb_students_info 表中,查找所有以字母“t”开头的学生姓名,区分大小写和不区分大小写的 SQL 语句和运行结果如下。
mysql> SELECT name FROM tb_students_info WHERE name LIKE \'t%\'; +--------+ | name | +--------+ | Thomas | | Tom | +--------+ 2 rows in set (0.00 sec) mysql> SELECT name FROM tb_students_info WHERE name LIKE BINARY \'t%\'; Empty set (0.01 sec)
由结果可以看到,区分大小写后,“Tom”和“Thomas”等记录就不会被匹配到了。
(4)使用通配符的注意事项和技巧
下面是使用通配符的一些注意事项:
- 注意大小写。MySQL 默认是不区分大小写的。如果区分大小写,像“Tom”这样的数据就不能被“t%”所匹配到。
- 注意尾部空格,尾部空格会干扰通配符的匹配。例如,“T% ”就不能匹配到“Tom”。
- 注意 NULL。“%”通配符可以到匹配任意字符,但是不能匹配 NULL。也就是说 “%”匹配不到 tb_students_info 数据表中值为 NULL 的记录。
下面是一些使用通配符要记住的技巧。
- 不要过度使用通配符,如果其它操作符能达到相同的目的,应该使用其它操作符。因为 MySQL 对通配符的处理一般会比其他操作符花费更长的时间。
- 在确定使用通配符后,除非绝对有必要,否则不要把它们用在字符串的开始处。把通配符置于搜索模式的开始处,搜索起来是最慢的。
- 仔细注意通配符的位置。如果放错地方,可能不会返回想要的数据。
(5)拓展
如果查询内容中包含通配符,可以使用“\”转义符。例如,在 tb_students_info 表中,将学生姓名“Dany”修改为“Dany%”后,查询以“%”结尾的学生姓名,SQL 语句和运行结果如下:
mysql> SELECT NAME FROM test.`tb_students_info` WHERE NAME LIKE \'%\%\';
8、MySQL BETWEEN AND:范围查询
BETWEEN AND 需要两个参数,即范围的起始值和终止值。如果字段值在指定的范围内,则这些记录被返回。如果不在指定范围内,则不会被返回。使用 BETWEEN AND 的基本语法格式如下:
[NOT] BETWEEN 取值1 AND 取值2
其中:
- NOT:可选参数,表示指定范围之外的值。如果字段值不满足指定范围内的值,则这些记录被返回。
- 取值1:表示范围的起始值。
- 取值2:表示范围的终止值。
BETWEEN AND 和 NOT BETWEEN AND 关键字在查询指定范围内的记录时很有用。例如,查询学生的年龄段、出生日期,员工的工资水平等。
实例
在表 tb_students_info 中查询年龄在 20 到 23 之间的学生姓名和年龄,SQL 语句和运行结果如下。
mysql> SELECT name,age FROM tb_students_info -> WHERE age BETWEEN 20 AND 23; +--------+------+ | name | age | +--------+------+ | Green | 23 | | Henry | 23 | | Jane | 22 | | John | 21 | | Lily | 22 | | Susan | 23 | | Thomas | 22 | | Tom | 23 | +--------+------+ 8 rows in set (0.00 sec)
查询结果中包含学生年龄为 20 和 23 的记录,这就说明,在 MySQL 中,BETWEEN AND 能匹配指定范围内的所有值,包括起始值和终止值。
实例
在表 tb_students_info 中查询年龄不在 20 到 23 之间的学生姓名和年龄,SQL 语句和运行结果如下。
mysql> SELECT name,age FROM tb_students_info -> WHERE age NOT BETWEEN 20 AND 23; +------+------+ | name | age | +------+------+ | Dany | 25 | | Jim | 24 | +------+------+ 2 rows in set (0.00 sec)
9、MySQL IS NULL:空值查询
MySQL 提供了 IS NULL 关键字,用来判断字段的值是否为空值(NULL)。空值不同于 0,也不同于空字符串。使用 IS NULL 的基本语法格式如下:
IS [NOT] NULL
其中,“NOT”是可选参数,表示字段值不是空值时满足条件。
实例
mysql> SELECT `name`,`login_date` FROM tb_students_info -> WHERE login_date IS NULL; +--------+------------+ | NAME | login_date | +--------+------------+ | Dany | NULL | | Green | NULL | | Henry | NULL | | Jane | NULL | | Thomas | NULL | | Tom | NULL | +--------+------------+ 6 rows in set (0.01 sec)
注意:IS NULL 是一个整体,不能将 IS 换成“=”。如果将 IS 换成“=”将不能查询出任何结果,数据库系统会出现“Empty set(0.00 sec)”这样的提示。同理,IS NOT NULL 中的 IS NOT 不能换成“!=”或“<>”。IS NOT NULL 表示查询字段值不为空的记录。
实例
下面使用 IS NOT NULL 关键字来查询 tb_students_info 表中 login_date 字段不为空的记录。
mysql> SELECT `name`,login_date FROM tb_students_info
-> WHERE login_date IS NOT NULL;
10、MySQL使用GROUP BY分组查询
使用 GROUP BY 关键字的语法格式如下:
GROUP BY <字段名>
其中,“字段名”表示需要分组的字段名称,多个字段时用逗号隔开。
(1)GROUP BY单独使用
单独使用 GROUP BY 关键字时,查询结果会只显示每个分组的第一条记录。
实例
下面根据 tb_students_info 表中的 sex 字段进行分组查询,SQL 语句和运行结果如下:
mysql> SELECT `name`,`sex` FROM tb_students_info -> GROUP BY sex; +-------+------+ | name | sex | +-------+------+ | Henry | 女 | | Dany | 男 | +-------+------+ 2 rows in set (0.01 sec)
结果中只显示了两条记录,这两条记录的 sex 字段的值分别为“女”和“男”。
(2)GROUP BY 与 GROUP_CONCAT()
GROUP BY 关键字可以和 GROUP_CONCAT() 函数一起使用。GROUP_CONCAT() 函数会把每个分组的字段值都显示出来。
实例
下面根据 tb_students_info 表中的 sex 字段进行分组查询,使用 GROUP_CONCAT() 函数将每个分组的 name 字段的值都显示出来。SQL 语句和运行结果如下:
mysql> SELECT `sex`, GROUP_CONCAT(name) FROM tb_students_info -> GROUP BY sex; +------+----------------------------+ | sex | GROUP_CONCAT(name) | +------+----------------------------+ | 女 | Henry,Jim,John,Thomas,Tom | | 男 | Dany,Green,Jane,Lily,Susan | +------+----------------------------+ 2 rows in set (0.00 sec)
由结果可以看到,查询结果分为两组,sex 字段值为“女”的是一组,值为“男”的是一组,且每组的学生姓名都显示出来了。
实例
下面根据 tb_students_info 表中的 age 和 sex 字段进行分组查询。SQL 语句和运行结果如下:
mysql> SELECT age,sex,GROUP_CONCAT(name) FROM tb_students_info
-> GROUP BY age,sex;
+------+------+--------------------+
| age | sex | GROUP_CONCAT(name) |
+------+------+--------------------+
| 21 | 女 | John |
| 22 | 女 | Thomas |
| 22 | 男 | Jane,Lily |
| 23 | 女 | Henry,Tom |
| 23 | 男 | Green,Susan |
| 24 | 女 | Jim |
| 25 | 男 | Dany |
+------+------+--------------------+
7 rows in set (0.00 sec)
上面实例在分组过程中,先按照 age 字段进行分组,当 age 字段值相等时,再把 age 字段值相等的记录按照 sex 字段进行分组。多个字段分组查询时,会先按照第一个字段进行分组。如果第一个字段中有相同的值,MySQL 才会按照第二个字段进行分组。如果第一个字段中的数据都是唯一的,那么 MySQL 将不再对第二个字段进行分组。
(3)GROUP BY 与聚合函数
在数据统计时,GROUP BY 关键字经常和聚合函数一起使用。聚合函数包括 COUNT(),SUM(),AVG(),MAX() 和 MIN()。其中,COUNT() 用来统计记录的条数;SUM() 用来计算字段值的总和;AVG() 用来计算字段值的平均值;MAX() 用来查询字段的最大值;MIN() 用来查询字段的最小值。
实例
下面根据 tb_students_info 表的 sex 字段进行分组查询,使用 COUNT() 函数计算每一组的记录数。SQL 语句和运行结果如下:
mysql> SELECT sex,COUNT(sex) FROM tb_students_info -> GROUP BY sex; +------+------------+ | sex | COUNT(sex) | +------+------------+ | 女 | 5 | | 男 | 5 | +------+------------+ 2 rows in set (0.00 sec)
结果显示,sex 字段值为“女”的记录是一组,有 5 条记录;sex 字段值为“男”的记录是一组,有 5 条记录。
(4)GROUP BY 与 WITH ROLLUP
WITH POLLUP 关键字用来在所有记录的最后加上一条记录,这条记录是上面所有记录的总和,即统计记录数量。
例 5
下面根据 tb_students_info 表中的 sex 字段进行分组查询,并使用 WITH ROLLUP 显示记录的总和。
mysql> SELECT sex,GROUP_CONCAT(name) FROM tb_students_info ->GROUP BY sex WITH ROLLUP; +------+------------------------------------------------------+ | sex | GROUP_CONCAT(name) | +------+------------------------------------------------------+ | 女 | Henry,Jim,John,Thomas,Tom | | 男 | Dany,Green,Jane,Lily,Susan | | NULL | Henry,Jim,John,Thomas,Tom,Dany,Green,Jane,Lily,Susan | +------+------------------------------------------------------+ 3 rows in set (0.00 sec)
查询结果显示,GROUP_CONCAT(name) 显示了每个分组的 name 字段值。同时,最后一条记录的 GROUP_CONCAT(name) 字段的值刚好是上面分组 name 字段值的总和。
11、MySQL HAVING:过滤分组
在 MySQL 中,可以使用 HAVING 关键字对分组后的数据进行过滤。使用 HAVING 关键字的语法格式如下:
HAVING <查询条件>
HAVING 关键字和 WHERE 关键字都可以用来过滤数据,且 HAVING 支持 WHERE 关键字中所有的操作符和语法。但是 WHERE 和 HAVING 关键字也存在以下几点差异:
- 一般情况下,WHERE 用于过滤数据行,而 HAVING 用于过滤分组。
- WHERE 查询条件中不可以使用聚合函数,而 HAVING 查询条件中可以使用聚合函数。
- WHERE 在数据分组前进行过滤,而 HAVING 在数据分组后进行过滤 。
- WHERE 针对数据库文件进行过滤,而 HAVING 针对查询结果进行过滤。也就是说,WHERE 根据数据表中的字段直接进行过滤,而 HAVING 是根据前面已经查询出的字段进行过滤。
- WHERE 查询条件中不可以使用字段别名,而 HAVING 查询条件中可以使用字段别名。
下面通过实例让大家更直观的了解 WHERE 和 HAVING 关键字的相同点和不同点。
实例
分别使用 HAVING 和 WHERE 关键字查询出 tb_students_info 表中身高大于 150 的学生姓名,性别和身高。SQL 语句和运行结果如下。
mysql> SELECT name,sex,height FROM tb_students_info -> HAVING height>150; +--------+------+--------+ | name | sex | height | +--------+------+--------+ | Dany | 男 | 160 | | Green | 男 | 158 | | Henry | 女 | 185 | | Jane | 男 | 162 | | Jim | 女 | 175 | | John | 女 | 172 | | Lily | 男 | 165 | | Susan | 男 | 170 | | Thomas | 女 | 178 | | Tom | 女 | 165 | +--------+------+--------+ 10 rows in set (0.00 sec) mysql> SELECT name,sex,height FROM tb_students_info -> WHERE height>150; +--------+------+--------+ | name | sex | height | +--------+------+--------+ | Dany | 男 | 160 | | Green | 男 | 158 | | Henry | 女 | 185 | | Jane | 男 | 162 | | Jim | 女 | 175 | | John | 女 | 172 | | Lily | 男 | 165 | | Susan | 男 | 170 | | Thomas | 女 | 178 | | Tom | 女 | 165 | +--------+------+--------+ 10 rows in set (0.00 sec)
上述实例中,因为在 SELECT 关键字后已经查询出了 height 字段,所以 HAVING 和 WHERE 都可以使用。但是如果 SELECT 关键字后没有查询出 height 字段,MySQL 就会报错。
实例
使用 HAVING 和 WHERE 关键字分别查询出 tb_students_info 表中身高大于 150 的学生姓名和性别(与例 1 相比,这次没有查询 height 字段)。SQL 语句和运行结果如下。
mysql> SELECT name,sex FROM tb_students_info -> WHERE height>150; +--------+------+ | name | sex | +--------+------+ | Dany | 男 | | Green | 男 | | Henry | 女 | | Jane | 男 | | Jim | 女 | | John | 女 | | Lily | 男 | | Susan | 男 | | Thomas | 女 | | Tom | 女 | +--------+------+ 10 rows in set (0.00 sec) mysql> SELECT name,sex FROM tb_students_info HAVING height>150; ERROR 1054 (42S22): Unknown column \'height\' in \'having clause\'
由结果可以看出,如果 SELECT 关键字后没有查询出 HAVING 查询条件中使用的 height 字段,MySQL 会提示错误信息:“having子句”中的列“height”未知”。
实例
根据 height 字段对 tb_students_info 表中的数据进行分组,并使用 HAVING 和 WHERE 关键字分别查询出分组后平均身高大于 170 的学生姓名、性别和身高。SQL 语句和运行结果如下。
mysql> SELECT GROUP_CONCAT(name),sex,height FROM tb_students_info -> GROUP BY height -> HAVING AVG(height)>170; +--------------------+------+--------+ | GROUP_CONCAT(name) | sex | height | +--------------------+------+--------+ | John | 女 | 172 | | Jim | 女 | 175 | | Thomas | 女 | 178 | | Henry | 女 | 185 | +--------------------+------+--------+ 4 rows in set (0.00 sec) mysql> SELECT GROUP_CONCAT(name),sex,height FROM tb_students_info WHERE AVG(height)>170 GROUP BY height; ERROR 1111 (HY000): Invalid use of group function
由结果可以看出,如果在 WHERE 查询条件中使用聚合函数,MySQL 会提示错误信息:无效使用组函数。
12、MySQL CROSS JOIN:交叉连接
交叉连接(CROSS JOIN)一般用来返回连接表的笛卡尔积。
笛卡尔积(Cartesian product)是指两个集合 X 和 Y 的乘积。例如,有 A 和 B 两个集合,它们的值如下:
A = {1,2}
B = {3,4,5}集合 A×B 和 B×A 的结果集分别表示为:
A×B={(1,3), (1,4), (1,5), (2,3), (2,4), (2,5) };
B×A={(3,1), (3,2), (4,1), (4,2), (5,1), (5,2) };以上 A×B 和 B×A 的结果就叫做两个集合的笛卡尔积。
并且,从以上结果我们可以看出:
- 两个集合相乘,不满足交换率,即 A×B≠B×A。
- A 集合和 B 集合的笛卡尔积是 A 集合的元素个数 × B 集合的元素个数。
多表查询遵循的算法就是以上提到的笛卡尔积,表与表之间的连接可以看成是在做乘法运算。在实际应用中,应避免使用笛卡尔积,因为笛卡尔积中容易存在大量的不合理数据,简单来说就是容易导致查询结果重复、混乱。
交叉连接的语法格式如下:
SELECT <字段名> FROM <表1> CROSS JOIN <表2> [WHERE子句]
或
SELECT <字段名> FROM <表1>, <表2> [WHERE子句]
语法说明如下:
- 字段名:需要查询的字段名称。
- <表1><表2>:需要交叉连接的表名。
- WHERE 子句:用来设置交叉连接的查询条件。
注意:多个表交叉连接时,在 FROM 后连续使用 CROSS JOIN 或,即可。以上两种语法的返回结果是相同的,但是第一种语法才是官方建议的标准写法。当连接的表之间没有关系时,我们会省略掉 WHERE 子句,这时返回结果就是两个表的笛卡尔积,返回结果数量就是两个表的数据行相乘。需要注意的是,如果每个表有 1000 行,那么返回结果的数量就有 1000×1000 = 1000000 行,数据量是非常巨大的。
实例
查询学生信息表和科目信息表,并得到一个笛卡尔积。为了方便观察学生信息表和科目表交叉连接后的运行结果,我们先分别查询出这两个表的数据,再进行交叉连接查询。
1)查询 tb_students_info 表中的数据,SQL 语句和运行结果如下:
mysql> SELECT * FROM tb_students_info; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 1 | Dany | 25 | 男 | 160 | 1 | | 2 | Green | 23 | 男 | 158 | 2 | | 3 | Henry | 23 | 女 | 185 | 1 | | 4 | Jane | 22 | 男 | 162 | 3 | | 5 | Jim | 24 | 女 | 175 | 2 | | 6 | John | 21 | 女 | 172 | 4 | | 7 | Lily | 22 | 男 | 165 | 4 | | 8 | Susan | 23 | 男 | 170 | 5 | | 9 | Thomas | 22 | 女 | 178 | 5 | | 10 | Tom | 23 | 女 | 165 | 5 | +----+--------+------+------+--------+-----------+ 10 rows in set (0.00 sec)
2)查询 tb_course 表中的数据,SQL 语句和运行结果如下:
mysql> SELECT * FROM tb_course; +----+-------------+ | id | course_name | +----+-------------+ | 1 | Java | | 2 | MySQL | | 3 | Python | | 4 | Go | | 5 | C++ | +----+-------------+ 5 rows in set (0.00 sec)
3)使用 CROSS JOIN 查询出两张表中的笛卡尔积,SQL 语句和运行结果如下:
mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info; +----+-------------+----+--------+------+------+--------+-----------+ | id | course_name | id | name | age | sex | height | course_id | +----+-------------+----+--------+------+------+--------+-----------+ | 1 | Java | 1 | Dany | 25 | 男 | 160 | 1 | | 2 | MySQL | 1 | Dany | 25 | 男 | 160 | 1 | | 3 | Python | 1 | Dany | 25 | 男 | 160 | 1 | | 4 | Go | 1 | Dany | 25 | 男 | 160 | 1 | | 5 | C++ | 1 | Dany | 25 | 男 | 160 | 1 | | 1 | Java | 2 | Green | 23 | 男 | 158 | 2 | | 2 | MySQL | 2 | Green | 23 | 男 | 158 | 2 | | 3 | Python | 2 | Green | 23 | 男 | 158 | 2 | | 4 | Go | 2 | Green | 23 | 男 | 158 | 2 | | 5 | C++ | 2 | Green | 23 | 男 | 158 | 2 | | 1 | Java | 3 | Henry | 23 | 女 | 185 | 1 | | 2 | MySQL | 3 | Henry | 23 | 女 | 185 | 1 | | 3 | Python | 3 | Henry | 23 | 女 | 185 | 1 | | 4 | Go | 3 | Henry | 23 | 女 | 185 | 1 | | 5 | C++ | 3 | Henry | 23 | 女 | 185 | 1 | | 1 | Java | 4 | Jane | 22 | 男 | 162 | 3 | | 2 | MySQL | 4 | Jane | 22 | 男 | 162 | 3 | | 3 | Python | 4 | Jane | 22 | 男 | 162 | 3 | | 4 | Go | 4 | Jane | 22 | 男 | 162 | 3 | | 5 | C++ | 4 | Jane | 22 | 男 | 162 | 3 | | 1 | Java | 5 | Jim | 24 | 女 | 175 | 2 | | 2 | MySQL | 5 | Jim | 24 | 女 | 175 | 2 | | 3 | Python | 5 | Jim | 24 | 女 | 175 | 2 | | 4 | Go | 5 | Jim | 24 | 女 | 175 | 2 | | 5 | C++ | 5 | Jim | 24 | 女 | 175 | 2 | | 1 | Java | 6 | John | 21 | 女 | 172 | 4 | | 2 | MySQL | 6 | John | 21 | 女 | 172 | 4 | | 3 | Python | 6 | John | 21 | 女 | 172 | 4 | | 4 | Go | 6 | John | 21 | 女 | 172 | 4 | | 5 | C++ | 6 | John | 21 | 女 | 172 | 4 | | 1 | Java | 7 | Lily | 22 | 男 | 165 | 4 | | 2 | MySQL | 7 | Lily | 22 | 男 | 165 | 4 | | 3 | Python | 7 | Lily | 22 | 男 | 165 | 4 | | 4 | Go | 7 | Lily | 22 | 男 | 165 | 4 | | 5 | C++ | 7 | Lily | 22 | 男 | 165 | 4 | | 1 | Java | 8 | Susan | 23 | 男 | 170 | 5 | | 2 | MySQL | 8 | Susan | 23 | 男 | 170 | 5 | | 3 | Python | 8 | Susan | 23 | 男 | 170 | 5 | | 4 | Go | 8 | Susan | 23 | 男 | 170 | 5 | | 5 | C++ | 8 | Susan | 23 | 男 | 170 | 5 | | 1 | Java | 9 | Thomas | 22 | 女 | 178 | 5 | | 2 | MySQL | 9 | Thomas | 22 | 女 | 178 | 5 | | 3 | Python | 9 | Thomas | 22 | 女 | 178 | 5 | | 4 | Go | 9 | Thomas | 22 | 女 | 178 | 5 | | 5 | C++ | 9 | Thomas | 22 | 女 | 178 | 5 | | 1 | Java | 10 | Tom | 23 | 女 | 165 | 5 | | 2 | MySQL | 10 | Tom | 23 | 女 | 165 | 5 | | 3 | Python | 10 | Tom | 23 | 女 | 165 | 5 | | 4 | Go | 10 | Tom | 23 | 女 | 165 | 5 | | 5 | C++ | 10 | Tom | 23 | 女 | 165 | 5 | +----+-------------+----+--------+------+------+--------+-----------+ 50 rows in set (0.00 sec)
由运行结果可以看出,tb_course 和 tb_students_info 表交叉连接查询后,返回了 50 条记录。可以想象,当表中的数据较多时,得到的运行结果会非常长,而且得到的运行结果也没太大的意义。所以,通过交叉连接的方式进行多表查询的这种方法并不常用,我们应该尽量避免这种查询。
实例
查询 tb_course 表中的 id 字段和 tb_students_info 表中的 course_id 字段相等的内容, SQL 语句和运行结果如下:
mysql> SELECT * FROM tb_course CROSS JOIN tb_students_info -> WHERE tb_students_info.course_id = tb_course.id; +----+-------------+----+--------+------+------+--------+-----------+ | id | course_name | id | name | age | sex | height | course_id | +----+-------------+----+--------+------+------+--------+-----------+ | 1 | Java | 1 | Dany | 25 | 男 | 160 | 1 | | 2 | MySQL | 2 | Green | 23 | 男 | 158 | 2 | | 1 | Java | 3 | Henry | 23 | 女 | 185 | 1 | | 3 | Python | 4 | Jane | 22 | 男 | 162 | 3 | | 2 | MySQL | 5 | Jim | 24 | 女 | 175 | 2 | | 4 | Go | 6 | John | 21 | 女 | 172 | 4 | | 4 | Go | 7 | Lily | 22 | 男 | 165 | 4 | | 5 | C++ | 8 | Susan | 23 | 男 | 170 | 5 | | 5 | C++ | 9 | Thomas | 22 | 女 | 178 | 5 | | 5 | C++ | 10 | Tom | 23 | 女 | 165 | 5 | +----+-------------+----+--------+------+------+--------+-----------+ 10 rows in set (0.01 sec)
如果在交叉连接时使用 WHERE 子句,MySQL 会先生成两个表的笛卡尔积,然后再选择满足 WHERE 条件的记录。因此,表的数量较多时,交叉连接会非常非常慢。一般情况下不建议使用交叉连接。
13、MySQL INNER JOIN:内连接
内连接(INNER JOIN)主要通过设置连接条件的方式,来移除查询结果中某些数据行的交叉连接。简单来说,就是利用条件表达式来消除交叉连接的某些数据行。内连接使用 INNER JOIN 关键字连接两张表,并使用 ON 子句来设置连接条件。如果没有连接条件,INNER JOIN 和 CROSS JOIN 在语法上是等同的,两者可以互换。内连接的语法格式如下:
SELECT <字段名> FROM <表1> INNER JOIN <表2> [ON子句]
语法说明如下。
- 字段名:需要查询的字段名称。
- <表1><表2>:需要内连接的表名。
- INNER JOIN :内连接中可以省略 INNER 关键字,只用关键字 JOIN。
- ON 子句:用来设置内连接的连接条件。
INNER JOIN 也可以使用 WHERE 子句指定连接条件,但是 INNER JOIN ... ON 语法是官方的标准写法,而且 WHERE 子句在某些时候会影响查询的性能。
多个表内连接时,在 FROM 后连续使用 INNER JOIN 或 JOIN 即可。内连接可以查询两个或两个以上的表。
实例
在 tb_students_info 表和 tb_course 表之间,使用内连接查询学生姓名和相对应的课程名称,SQL 语句和运行结果如下。
mysql> SELECT s.name,c.course_name FROM tb_students_info s INNER JOIN tb_course c -> ON s.course_id = c.id; +--------+-------------+ | name | course_name | +--------+-------------+ | Dany | Java | | Green | MySQL | | Henry | Java | | Jane | Python | | Jim | MySQL | | John | Go | | Lily | Go | | Susan | C++ | | Thomas | C++ | | Tom | C++ | +--------+-------------+ 10 rows in set (0.00 sec)
在这里的查询语句中,两个表之间的关系通过 INNER JOIN 指定,连接的条件使用 ON 子句给出。注意:当对多个表进行查询时,要在 SELECT 语句后面指定字段是来源于哪一张表。因此,在多表查询时,SELECT 语句后面的写法是表名.列名。另外,如果表名非常长的话,也可以给表设置别名,这样就可以直接在 SELECT 语句后面写上表的别名.列名。
14、MySQL LEFT/RIGHT JOIN:外连接
外连接会先将连接的表分为基表和参考表,再以基表为依据返回满足和不满足条件的记录。外连接可以分为左外连接和右外连接
(1)左连接
左外连接又称为左连接,使用 LEFT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。左连接的语法格式如下:
SELECT <字段名> FROM <表1> LEFT OUTER JOIN <表2> <ON子句>
语法说明如下。
- 字段名:需要查询的字段名称。
- <表1><表2>:需要左连接的表名。
- LEFT OUTER JOIN:左连接中可以省略 OUTER 关键字,只使用关键字 LEFT JOIN。
- ON 子句:用来设置左连接的连接条件,不能省略。
上述语法中,“表1”为基表,“表2”为参考表。左连接查询时,可以查询出“表1”中的所有记录和“表2”中匹配连接条件的记录。如果“表1”的某行在“表2”中没有匹配行,那么在返回结果中,“表2”的字段值均为空值(NULL)。
实例
在进行左连接查询之前,我们先查看 tb_course 和 tb_students_info 两张表中的数据。SQL 语句和运行结果如下。
mysql> SELECT * FROM tb_course; +----+-------------+ | id | course_name | +----+-------------+ | 1 | Java | | 2 | MySQL | | 3 | Python | | 4 | Go | | 5 | C++ | | 6 | HTML | +----+-------------+ 6 rows in set (0.00 sec) mysql> SELECT * FROM tb_students_info; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 1 | Dany | 25 | 男 | 160 | 1 | | 2 | Green | 23 | 男 | 158 | 2 | | 3 | Henry | 23 | 女 | 185 | 1 | | 4 | Jane | 22 | 男 | 162 | 3 | | 5 | Jim | 24 | 女 | 175 | 2 | | 6 | John | 21 | 女 | 172 | 4 | | 7 | Lily | 22 | 男 | 165 | 4 | | 8 | Susan | 23 | 男 | 170 | 5 | | 9 | Thomas | 22 | 女 | 178 | 5 | | 10 | Tom | 23 | 女 | 165 | 5 | | 11 | LiMing | 22 | 男 | 180 | 7 | +----+--------+------+------+--------+-----------+ 11 rows in set (0.00 sec)
在 tb_students_info 表和 tb_course 表中查询所有学生姓名和相对应的课程名称,包括没有课程的学生,SQL 语句和运行结果如下。
mysql> SELECT s.name,c.course_name FROM tb_students_info s LEFT OUTER JOIN tb_course c -> ON s.`course_id`=c.`id`; +--------+-------------+ | name | course_name | +--------+-------------+ | Dany | Java | | Henry | Java | | NULL | Java | | Green | MySQL | | Jim | MySQL | | Jane | Python | | John | Go | | Lily | Go | | Susan | C++ | | Thomas | C++ | | Tom | C++ | | LiMing | NULL | +--------+-------------+ 12 rows in set (0.00 sec)
可以看到,运行结果显示了 12 条记录,name 为 LiMing 的学生目前没有课程,因为对应的 tb_course 表中没有该学生的课程信息,所以该条记录只取出了 tb_students_info 表中相应的值,而从 tb_course 表中取出的值为 NULL。
(2)右连接
右外连接又称为右连接,右连接是左连接的反向连接。使用 RIGHT OUTER JOIN 关键字连接两个表,并使用 ON 子句来设置连接条件。右连接的语法格式如下:
SELECT <字段名> FROM <表1> RIGHT OUTER JOIN <表2> <ON子句>
语法说明如下。
- 字段名:需要查询的字段名称。
- <表1><表2>:需要右连接的表名。
- RIGHT OUTER JOIN:右连接中可以省略 OUTER 关键字,只使用关键字 RIGHT JOIN。
- ON 子句:用来设置右连接的连接条件,不能省略。
与左连接相反,右连接以“表2”为基表,“表1”为参考表。右连接查询时,可以查询出“表2”中的所有记录和“表1”中匹配连接条件的记录。如果“表2”的某行在“表1”中没有匹配行,那么在返回结果中,“表1”的字段值均为空值(NULL)。
实例
在 tb_students_info 表和 tb_course 表中查询所有课程,包括没有学生的课程,SQL 语句和运行结果如下。
mysql> SELECT s.name,c.course_name FROM tb_students_info s RIGHT OUTER JOIN tb_course c -> ON s.`course_id`=c.`id`; +--------+-------------+ | name | course_name | +--------+-------------+ | Dany | Java | | Green | MySQL | | Henry | Java | | Jane | Python | | Jim | MySQL | | John | Go | | Lily | Go | | Susan | C++ | | Thomas | C++ | | Tom | C++ | | NULL | HTML | +--------+-------------+ 11 rows in set (0.00 sec)
可以看到,结果显示了 11 条记录,名称为 HTML 的课程目前没有学生,因为对应的 tb_students_info 表中并没有该学生的信息,所以该条记录只取出了 tb_course 表中相应的值,而从 tb_students_info 表中取出的值为 NULL。
多个表左/右连接时,在 ON 子句后连续使用 LEFT/RIGHT OUTER JOIN 或 LEFT/RIGHT JOIN 即可。
使用外连接查询时,一定要分清需要查询的结果,是需要显示左表的全部记录还是右表的全部记录,然后选择相应的左连接和右连接。
15、MySQL子查询
子查询指将一个查询语句嵌套在另一个查询语句中。子查询可以在 SELECT、UPDATE 和 DELETE 语句中使用,而且可以进行多层嵌套。在实际开发时,子查询经常出现在 WHERE 子句中。子查询在 WHERE 中的语法格式如下:
WHERE <表达式> <操作符> (子查询)
其中,操作符可以是比较运算符和 IN、NOT IN、EXISTS、NOT EXISTS 等关键字。
-
IN | NOT IN
当表达式与子查询返回的结果集中的某个值相等时,返回 TRUE,否则返回 FALSE;若使用关键字 NOT,则返回值正好相反。
-
EXISTS | NOT EXISTS
用于判断子查询的结果集是否为空,若子查询的结果集不为空,返回 TRUE,否则返回 FALSE;若使用关键字 NOT,则返回的值正好相反。
实例
使用子查询在 tb_students_info 表和 tb_course 表中查询学习 Java 课程的学生姓名,SQL 语句和运行结果如下。
mysql> SELECT name FROM tb_students_info WHERE course_id IN (SELECT id FROM tb_course WHERE course_name = \'Java\');
习惯上,外层的 SELECT 查询称为父查询,圆括号中嵌入的查询称为子查询(子查询必须放在圆括号内)。MySQL 在处理上例的 SELECT 语句时,执行流程为:先执行子查询,再执行父查询。
实例
在 SELECT 语句中使用 NOT IN 关键字,查询没有学习 Java 课程的学生姓名,SQL 语句和运行结果如下。
mysql> SELECT name FROM tb_students_info WHERE course_id NOT IN (SELECT id FROM tb_course WHERE course_name = \'Java\');
实例
使用=运算符,在 tb_course 表和 tb_students_info 表中查询出所有学习 Python 课程的学生姓名,SQL 语句和运行结果如下。
mysql> SELECT name FROM tb_students_info WHERE course_id = (SELECT id FROM tb_course WHERE course_name = \'Python\');
实例
使用<>运算符,在 tb_course 表和 tb_students_info 表中查询出没有学习 Python 课程的学生姓名,SQL 语句和运行结果如下。
mysql> SELECT name FROM tb_students_info WHERE course_id <> (SELECT id FROM tb_course WHERE course_name = \'Python\');
实例
查询 tb_course 表中是否存在 id=1 的课程,如果存在,就查询出 tb_students_info 表中的记录,SQL 语句和运行结果如下。
mysql> SELECT * FROM tb_students_info WHERE EXISTS(SELECT course_name FROM tb_course WHERE id=1);
如果tb_course 表中存在 id=1 的记录, EXISTS 表达式返回 TRUE,外层查询语句接收 TRUE 之后对表 tb_students_info 进行查询,返回所有的记录。EXISTS 关键字可以和其它查询条件一起使用,条件表达式与 EXISTS 关键字之间用 AND 和 OR 连接。
实例
查询 tb_course 表中是否存在 id=1 的课程,如果存在,就查询出 tb_students_info 表中 age 字段大于 24 的记录,SQL 语句和运行结果如下。
SELECT * FROM tb_students_infoWHERE age>24 AND EXISTS(SELECT course_name FROM tb_course WHERE id=1);
内层查询语句从 tb_course 表中查询到记录,返回 TRUE。外层查询语句开始进行查询。根据查询条件,从 tb_students_info 表中查询 age 大于 24 的记录。
子查询注意事项
- 子查询的功能也可以通过表连接完成,但是子查询会使 SQL 语句更容易阅读和编写。一般来说,表连接(内连接和外连接等)都可以用子查询替换,但反过来却不一定,有的子查询不能用表连接来替换。子查询比较灵活、方便、形式多样,适合作为查询的筛选条件,而表连接更适合于查看连接表的数据。
-
子查询语句可以嵌套在 SQL 语句中任何表达式出现的位置
在 SELECT 语句中,子查询可以被嵌套在 SELECT 语句的列、表和查询条件中,即 SELECT 子句,FROM 子句、WHERE 子句、GROUP BY 子句和 HAVING 子句。前面已经介绍了 WHERE 子句中嵌套子查询的使用方法,下面是子查询在 SELECT 子句和 FROM 子句中的使用语法。
-
- 嵌套在 SELECT 语句的 SELECT 子句中的子查询语法格式如下。提示:子查询结果为单行单列,但不必指定列别名。
SELECT (子查询) FROM 表名;
-
- 嵌套在 SELECT 语句的 FROM 子句中的子查询语法格式如下。注意:必须为表指定别名。一般返回多行多列数据记录,可以当作一张临时表。
SELECT * FROM (子查询) AS 表的别名;
-
只出现在子查询中而没有出现在父查询中的表不能包含在输出列中
多层嵌套子查询的最终数据集只包含父查询(即最外层的查询)的 SELECT 子句中出现的字段,而子查询的输出结果通常会作为其外层子查询数据源或用于数据判断匹配。常见错误如下:
SELECT * FROM (SELECT * FROM result);
这个子查询语句产生语法错误的原因在于主查询语句的 FROM 子句是一个子查询语句,因此应该为子查询结果集指定别名。正确代码如下。
SELECT * FROM (SELECT * FROM result) AS Temp;
16、正则表达式
主要用来查询和替换符合某个模式(规则)的文本内容。例如,从一个文件中提取电话号码,查找一篇文章中重复的单词、替换文章中的敏感语汇等,这些地方都可以使用正则表达式。正则表达式强大且灵活,常用于非常复杂的查询。MySQL 中,使用 REGEXP 关键字指定正则表达式的字符匹配模式,其基本语法格式如下:
属性名 REGEXP \'匹配方式\'
其中,“属性名”表示需要查询的字段名称;“匹配方式”表示以哪种方式来匹配查询。“匹配方式”中有很多的模式匹配字符,它们分别表示不同的意思。下表列出了 REGEXP 操作符中常用的匹配方式。
| 选项 | 说明 | 例子 | 匹配值示例 |
|---|---|---|---|
| ^ | 匹配文本的开始字符 | \'^b\' 匹配以字母 b 开头的字符串 | book、big、banana、bike |
| $ | 匹配文本的结束字符 | \'st$\' 匹配以 st 结尾的字符串 | test、resist、persist |
| . | 匹配任何单个字符 | \'b.t\' 匹配任何 b 和 t 之间有一个字符 | bit、bat、but、bite |
| * | 匹配零个或多个在它前面的字符 | \'f*n\' 匹配字符 n 前面有任意个字符 f | fn、fan、faan、abcn |
| + | 匹配前面的字符 1 次或多次 | \'ba+\' 匹配以 b 开头,后面至少紧跟一个 a | ba、bay、bare、battle |
| <字符串> | 匹配包含指定字符的文本 | \'fa\' 匹配包含‘fa’的文本 | fan、afa、faad |
| [字符集合] | 匹配字符集合中的任何一个字符 | \'[xz]\' 匹配 x 或者 z | dizzy、zebra、x-ray、extra |
| [^] | 匹配不在括号中的任何字符 | \'[^abc]\' 匹配任何不包含 a、b 或 c 的字符串 | desk、fox、f8ke |
| 字符串{n,} | 匹配前面的字符串至少 n 次 | \'b{2}\' 匹配 2 个或更多的 b | bbb、bbbb、bbbbbbb |
| 字符串 {n,m} |
匹配前面的字符串至少 n 次, 至多 m 次 | \'b{2,4}\' 匹配最少 2 个,最多 4 个 b | bbb、bbbb |
(1)查询以特定字符或字符串开头的记录
字符^用来匹配以特定字符或字符串开头的记录。
实例
在 tb_students_info 表中,查询 name 字段以“J”开头的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'^J\';
实例
在 tb_students_info 表中,查询 name 字段以“Ji”开头的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'^Ji\';
(2)查询以特定字符或字符串结尾的记录
字符$用来匹配以特定字符或字符串结尾的记录。
实例
在 tb_students_info 表中,查询 name 字段以“y”结尾的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'y$\';
实例
在 tb_students_info 表中,查询 name 字段以“ry”结尾的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'ry$\';
(3)替代字符串中的任意一个字符
字符.用来替代字符串中的任意一个字符。
实例
在 tb_students_info 表中,查询 name 字段值包含“a”和“y”,且两个字母之间只有一个字母的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'a.y\';
(4)匹配多个字符
字符*和+都可以匹配多个该符号之前的字符。不同的是,+表示至少一个字符,而*可以表示 0 个字符。
实例
在 tb_students_info 表中,查询 name 字段值包含字母“T”,且“T”后面出现字母“h”的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'^Th*\'; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 9 | Thomas | 22 | 女 | 178 | 5 | | 10 | Tom | 23 | 女 | 165 | 5 | +----+--------+------+------+--------+-----------+ 2 rows in set (0.00 sec)
实例
在 tb_students_info 表中,查询 name 字段值包含字母“T”,且“T”后面至少出现“h”一次的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'^Th+\'; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 9 | Thomas | 22 | 女 | 178 | 5 | +----+--------+------+------+--------+-----------+ 1 row in set (0.00 sec)
(5)匹配指定字符串
正则表达式可以匹配字符串。当表中的记录包含这个字符串时,就可以将该记录查询出来。指定多个字符串时,需要用|隔开。只要匹配这些字符串中的任意一个即可。
实例
在 tb_students_info 表中,查询 name 字段值包含字符串“an”的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'an\'; +----+-------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+------+--------+-----------+ | 1 | Dany | 25 | 男 | 160 | 1 | | 4 | Jane | 22 | 男 | 162 | 3 | | 8 | Susan | 23 | 男 | 170 | 5 | +----+-------+------+------+--------+-----------+ 3 rows in set (0.00 sec)
实例
在 tb_students_info 表中,查询 name 字段值包含字符串“an”或“en”的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'an|en\'; +----+-------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+------+--------+-----------+ | 1 | Dany | 25 | 男 | 160 | 1 | | 2 | Green | 23 | 男 | 158 | 2 | | 3 | Henry | 23 | 女 | 185 | 1 | | 4 | Jane | 22 | 男 | 162 | 3 | | 8 | Susan | 23 | 男 | 170 | 5 | +----+-------+------+------+--------+-----------+ 5 rows in set (0.00 sec)
注意:字符串与|之间不能有空格。因为,查询过程中,数据库系统会将空格也当作一个字符,这样就查询不出想要的结果。
(6)匹配指定字符串中的任意一个
使用方括号[ ]可以将需要查询的字符组成一个字符集合。只要记录中包含方括号中的任意字符,该记录就会被查询出来。例如,通过“[abc]”可以查询包含 a、b 和 c 等 3 个字母中任意一个的记录。
实例
在 tb_students_info 表中,查询 name 字段值包含字母“i”或“o”的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'[io]\'; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 5 | Jim | 24 | 女 | 175 | 2 | | 6 | John | 21 | 女 | 172 | 4 | | 7 | Lily | 22 | 男 | 165 | 4 | | 9 | Thomas | 22 | 女 | 178 | 5 | | 10 | Tom | 23 | 女 | 165 | 5 | | 11 | LiMing | 22 | 男 | 180 | 7 | +----+--------+------+------+--------+-----------+ 6 rows in set (0.00 sec)
从查询结果可以看到,所有返回记录的 name 字段值都包含字母 i 或 o,或者两个都有。方括号[ ]还可以指定集合的区间。例如,“[a-z]”表示从 a~z 的所有字母;“[0-9]”表示从 0~9 的所有数字;“[a-z0-9]”表示包含所有的小写字母和数字;“[a-zA-Z]”表示匹配所有字符。
实例
在 tb_students_info 表中,查询 name 字段值中包含 1、2 或 3 的记录,SQL 语句和执行过程如下。
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'[123]\';
匹配集合“[123]”也可以写成“[1-3]”,即指定集合区间。
(7)使用{n,}或者{n,m}来指定字符串连续出现的次数
字符串{n,}表示字符串连续出现 n 次;字符串{n,m}表示字符串连续出现至少 n 次,最多 m 次。例如,a{2,} 表示字母 a 连续出现至少 2 次,也可以大于 2 次;a{2,4} 表示字母 a 连续出现最少 2 次,最多不能超过 4 次。
实例
在 tb_students_info 表中,查询 name 字段值出现字母‘e’ 至少 2 次的记录,SQL 语句如下:
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'e{2,}\'; +----+-------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+-------+------+------+--------+-----------+ | 2 | Green | 23 | 男 | 158 | 2 | +----+-------+------+------+--------+-----------+ 1 row in set (0.00 sec)
实例
在 tb_students_info 表中,查询 name 字段值出现字符串“i” 最少 1 次,最多 3 次的记录,SQL 语句如下:
mysql> SELECT * FROM tb_students_info WHERE name REGEXP \'i{1,3}\'; +----+--------+------+------+--------+-----------+ | id | name | age | sex | height | course_id | +----+--------+------+------+--------+-----------+ | 5 | Jim | 24 | 女 | 175 | 2 | | 7 | Lily | 22 | 男 | 165 | 4 | | 11 | LiMing | 22 | 男 | 180 | 7 | +----+--------+------+------+--------+-----------+ 3 rows in set (0.00 sec)
17、插入数据
INSERT 语句有两种语法形式,分别是 INSERT…VALUES 语句和 INSERT…SET 语句。
(1)INSERT…VALUES语句
INSERT INTO <表名> [ <列名1> [ , … <列名n>] ] VALUES (值1) [… , (值n) ];
语法说明如下。
-
<表名>:指定被操作的表名。 -
<列名>:指定需要插入数据的列名。若向表中的所有列插入数据,则全部的列名均可以省略,直接采用 INSERT<表名>VALUES(…) 即可。 -
VALUES或VALUE子句:该子句包含要插入的数据清单。数据清单中数据的顺序要和列的顺序相对应。
(2)INSERT…SET语句
INSERT INTO <表名> SET <列名1> = <值1>,<列名2> = <值2>, …
此语句用于直接给表中的某些列指定对应的列值,即要插入的数据的列名在 SET 子句中指定,col_name 为指定的列名,等号后面为指定的数据,而对于未指定的列,列值会指定为该列的默认值。
(3)注意事项
由 INSERT 语句的两种形式可以看出:
- 使用 INSERT…VALUES 语句可以向表中插入一行数据,也可以插入多行数据;
- 使用 INSERT…SET 语句可以指定插入行中每列的值,也可以指定部分列的值;
- INSERT…SELECT 语句向表中插入其他表的数据。
- 采用 INSERT…SET 语句可以向表中插入部分列的值,这种方式更为灵活;
- INSERT…VALUES 语句可以一次插入多条数据。
- 在 MySQL 中,用单条 INSERT 语句处理多个插入要比使用多条 INSERT 语句更快。当使用单条 INSERT 语句插入多行数据的时候,只需要将每行数据用圆括号括起来即可。
(4)实例
在 test_db 数据库中创建一个课程信息表 tb_courses,包含课程编号 course_id、课程名称 course_name、课程学分 course_grade 和课程备注 course_info,输入的 SQL 语句和执行结果如下所示。
mysql> CREATE TABLE tb_courses -> ( -> course_id INT NOT NULL AUTO_INCREMENT, -> course_name CHAR(40) NOT NULL, -> course_grade FLOAT NOT NULL, -> course_info CHAR(100) NULL, -> PRIMARY KEY(course_id) -> ); Query OK, 0 rows affected (0.00 sec)
向表中的全部字段添加值
向表中所有字段插入值的方法有两种:一种是指定所有字段名;另一种是完全不指定字段名。
-
指定所有字段名。INSERT 语句后面的列名称顺序可以不是 tb_courses 表定义时的顺序,即插入数据时,不需要按照表定义的顺序插入,只要保证值的顺序与列字段的顺序相同就可以。
mysql> INSERT INTO tb_courses -> (course_id,course_name,course_grade,course_info) -> VALUES(1,\'Network\',3,\'Computer Network\');
- 使用 INSERT 插入数据时,允许列名称列表 column_list 为空,此时值列表中需要为表的每一个字段指定值,并且值的顺序必须和数据表中字段定义时的顺序相同。
mysql> INSERT INTO tb_courses -> VLAUES(3,\'Java\',4,\'Java EE\'); Query OK, 1 rows affected (0.08 sec)
INSERT 语句中没有指定插入列表,只有一个值列表。在这种情况下,值列表为每一个字段列指定插入的值,并且这些值的顺序必须和 tb_courses 表中字段定义的顺序相同。
注意:虽然使用 INSERT 插入数据时可以忽略插入数据的列名称,若值不包含列名称,则 VALUES 关键字后面的值不仅要求完整,而且顺序必须和表定义时列的顺序相同。如果表的结构被修改,对列进行增加、删除或者位置改变操作,这些操作将使得用这种方式插入数据时的顺序也同时改变。如果指定列名称,就不会受到表结构改变的影响。
向表中指定字段添加值
- 为表的指定字段插入数据,是在 INSERT 语句中只向部分字段中插入值,而其他字段的值为表定义时的默认值。
mysql> INSERT INTO tb_courses -> (course_name,course_grade,course_info) -> VALUES(\'System\',3,\'Operation System\');
使用 INSERT INTO…FROM 语句复制表数据
SELECT 子句返回的是一个查询到的结果集,INSERT 语句将这个结果集插入指定表中,结果集中的每行数据的字段数、字段的数据类型都必须与被操作的表完全一致。
mysql> INSERT INTO tb_courses_new -> (course_id,course_name,course_grade,course_info) -> SELECT course_id,course_name,course_grade,course_info -> FROM tb_courses;
18、修改数据
UPDATE <表名> SET 字段 1=值 1 [,字段 2=值 2… ] [WHERE 子句 ] [ORDER BY 子句] [LIMIT 子句]
语法说明如下:
-
<表名>:用于指定要更新的表名称。 -
SET子句:用于指定表中要修改的列名及其列值。其中,每个指定的列值可以是表达式,也可以是该列对应的默认值。如果指定的是默认值,可用关键字 DEFAULT 表示列值。 -
WHERE子句:可选项。用于限定表中要修改的行。若不指定,则修改表中所有的行。 -
ORDER BY子句:可选项。用于限定表中的行被修改的次序。 -
LIMIT子句:可选项。用于限定被修改的行数。
注意:修改一行数据的多个列值时,SET 子句的每个值用逗号分开即可。
(1)在 tb_courses_new 表中,更新所有行的 course_grade 字段值为 4,输入的 SQL 语句和执行结果如下所示。
mysql> UPDATE tb_courses_new SET course_grade=4;
(2)根据条件修改表中的数据
mysql> UPDATE tb_courses_new -> SET course_name=\'DB\',course_grade=3.5 -> WHERE course_id=2;
注意:保证 UPDATE 以 WHERE 子句结束,通过 WHERE 子句指定被更新的记录所需要满足的条件,如果忽略 WHERE 子句,MySQL 将更新表中所有的行。
19、删除数据
(1)删除单个表中的数据
DELETE FROM <表名> [WHERE 子句] [ORDER BY 子句] [LIMIT 子句]
语法说明如下:
-
<表名>:指定要删除数据的表名。 -
ORDER BY子句:可选项。表示删除时,表中各行将按照子句中指定的顺序进行删除。 -
WHERE子句:可选项。表示为删除操作限定删除条件,若省略该子句,则代表删除该表中的所有行。 -
LIMIT子句:可选项。用于告知服务器在控制命令被返回到客户端前被删除行的最大值。
注意:在不使用 WHERE 条件的时候,将删除所有数据。
(2)删除表中的全部数据
mysql> DELETE FROM tb_courses_new;
(3)根据条件删除表中的数据
mysql> DELETE FROM tb_courses WHERE course_id=4;
20、清空表记录
MySQL 提供了 DELETE 和 TRUNCATE 关键字来删除表中的数据。TRUNCATE 关键字用于完全清空一个表。其语法格式如下:
TRUNCATE [TABLE] 表名
其中,TABLE 关键字可省略。
实例
mysql> TRUNCATE TABLE tb_student_course;
(1)TRUNCATE 和 DELETE 的区别
从逻辑上说,TRUNCATE 语句与 DELETE 语句作用相同,但是在某些情况下,两者在使用上有所区别。
- DELETE 是 DML 类型的语句;TRUNCATE 是 DDL 类型的语句。它们都用来清空表中的数据。
- DELETE 是逐行一条一条删除记录的;TRUNCATE 则是直接删除原来的表,再重新创建一个一模一样的新表,而不是逐行删除表中的数据,执行数据比 DELETE 快。因此需要删除表中全部的数据行时,尽量使用 TRUNCATE 语句, 可以缩短执行时间。
- DELETE 删除数据后,配合事件回滚可以找回数据;TRUNCATE 不支持事务的回滚,数据删除后无法找回。
- DELETE 删除数据后,系统不会重新设置自增字段的计数器;TRUNCATE 清空表记录后,系统会重新设置自增字段的计数器。
- DELETE 的使用范围更广,因为它可以通过 WHERE 子句指定条件来删除部分数据;而 TRUNCATE 不支持 WHERE 子句,只能删除整体。
- DELETE 会返回删除数据的行数,但是 TRUNCATE 只会返回 0,没有任何意义。
(2)总结
当不需要该表时,用 DROP;当仍要保留该表,但要删除所有记录时,用 TRUNCATE;当要删除部分记录时,用 DELETE。
| Delete | Truncate | Drop | |
|---|---|---|---|
| 类型 | 属于DML | 属于DDL | 属于DDL |
| 回滚 | 可回滚 | 不可回滚 | 不可回滚 |
| 删除内容 | 表结构还在,删除表的全部或者一部分数据行 | 表结构还在,删除表中的所有数据 | 从数据库中删除表,所有的数据行,索引和权限也会被删除 |
| 删除速度 | 删除速度慢,需要逐行删除 | 删除速度快 | 删除速度最快 |
八、SQL优化
1、问题定位
2、SQL的生命周期?
- 应用服务器与数据库服务器建立一个连接
- 数据库进程拿到请求sql
- 解析并生成执行计划,执行
- 读取数据到内存并进行逻辑处理
- 通过步骤一的连接,发送结果到客户端
- 关掉连接,释放资源
3、大表数据查询,怎么优化
- 优化shema、sql语句+索引;
- 第二加缓存,memcached, redis;
- 主从复制,读写分离;
- 垂直拆分,根据你模块的耦合度,将一个大的系统分为多个小的系统,也就是分布式系统;
- 水平切分,针对数据量大的表,这一步最麻烦,最能考验技术水平,要选择一个合理的sharding key, 为了有好的查询效率,表结构也要改动,做一定的冗余,应用也要改,sql中尽量带sharding key,将数据定位到限定的表上去查,而不是扫描全部的表;
4、超大分页怎么处理?
-
数据库层面:这也是我们主要集中关注的,类似于select * from table where age > 20 limit 1000000,10这种查询其实也是有可以优化的余地的。这条语句需要load1000000数据然后基本上全部丢弃,只取10条当然比较慢.。当时我们可以修改为select * from table where id in (select id from table where age > 20 limit 1000000,10)。这样虽然也load了一百万的数据,但是由于索引覆盖,要查询的所有字段都在索引中,所以速度会很快。同时如果ID连续,我们还可以select * from table where id > 1000000 limit 10,效率也是不错的,优化的可能性有许多种,但是核心思想都一样,就是减少load的数据。
- 从需求的角度减少这种请求:主要是不做类似的需求(直接跳转到几百万页之后的具体某一页,只允许逐页查看或者按照给定的路线走,这样可预测,可缓存)以及防止ID泄漏且连续被人恶意攻击。
解决超大分页,其实主要是靠缓存,可预测性的提前查到内容,缓存至redis等k-V数据库中,直接返回即可。在阿里巴巴《Java开发手册》中,对超大分页的解决办法是类似于上面提到的第一种。
【推荐】利用延迟关联或者子查询优化超多分页场景。 说明:MySQL并不是跳过offset行,而是取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。 正例:先快速定位需要获取的id段,然后再关联: SELECT a.* FROM 表1 a, (select id from 表1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
5、慢查询日志
用于记录执行时间超过某个临界值的SQL日志,用于快速定位慢查询,为我们的优化做参考。
(1)开启慢查询日志
show variables like ‘slov_query_log’
可以使用查看是否开启,如果状态值为OFF,可以使用以下语句来开启,它会在datadir下产生一个xxx-slow.log的文件。
set GLOBAL slow_query_log = on
(2)设置临界时间
查看临界时间,单位秒
show VARIABLES like \'long_query_time\'
设置临界时间
set long_query_time=0.5
实操时应该从长时间设置到短的时间,即将最慢的SQL优化掉,查看日志,一旦SQL超过了我们设置的临界时间就会被记录到xxx-slow.log中
6、慢查询优化
慢查询的优化首先要搞明白慢的原因是什么? 是查询条件没有命中索引?是load了不需要的数据列?还是数据量太大?所以优化也是针对这三个方向来的:
- 首先分析语句,看看是否load了额外的数据,可能是查询了多余的行并且抛弃掉了,可能是加载了许多结果中并不需要的列,对语句进行分析以及重写。
- 分析语句的执行计划,然后获得其使用索引的情况,之后修改语句或者修改索引,使得语句可以尽可能的命中索引。
- 如果对语句的优化已经无法进行,可以考虑表中的数据量是否太大,如果是的话可以进行横向或者纵向的分表。
7、主键使用自增ID还是UUID?
推荐使用自增ID,不要使用UUID。因为在InnoDB存储引擎中,主键索引是作为聚簇索引存在的,也就是说,主键索引的B+树叶子节点上存储了主键索引以及全部的数据(按照顺序),如果主键索引是自增ID,那么只需要不断向后排列即可,如果是UUID,由于到来的ID与原来的大小不确定,会造成非常多的数据插入,数据移动,然后导致产生很多的内存碎片,进而造成插入性能的下降。总之,在数据量大一些的情况下,用自增主键性能会好一些。关于主键是聚簇索引,如果没有主键,InnoDB会选择一个唯一键来作为聚簇索引,如果没有唯一键,会生成一个隐式的主键。
8、字段为什么要求定义为not null?
-
包含允许null字段的那些查询,mysql难以优化,而且它会使索性、索引统计和值更加复杂。
-
可空列(null)需要更多的存储空间。就是说允许为空的字段需要一个额外字节作为判断是否为NULL的标志位。
基于以上说明,null值会占用更多的字节,且会在程序中造成很多与预期不符的情况。字段设置成not null比设置成allow null要好很多。
9、优化查询过程中的数据访问
(1)访问数据太多导致查询性能下降,解决办法:
- 确定应用程序是否在检索大量超过需要的数据,需要指定列名
- 确认MySQL服务器是否在分析大量不必要的数据行,可使用limit解决
- 判断是否重复查询相同的数据。解决办法:可以缓存数据,下次直接读取缓存
(2)使用explain进行分析,如果发现查询需要扫描大量的数据,但只返回少数的行,可以通过如下技巧去优化:
- 使用索引覆盖扫描,把所有的列都放到索引中,这样存储引擎不需要回表获取对应行就可以返回结果。
- 改变数据库和表的结构,修改数据表范式
- 重写SQL语句,让优化器可以以更优的方式执行查询。
10、优化UNION查询
- UNION ALL的效率高于UNION
11、优化WHERE子句
- 对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引。
- 应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num is null -- 可以在num上设置默认值0,确保表中num列没有null值,然后这样查询: select id from t where num=0
- 3.应尽量避免在 where 子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描。
- 4.应尽量避免在 where 子句中使用or 来连接条件,否则将导致引擎放弃使用索引而进行全表扫描,如:
select id from t where num=10 or num=20 -- 可以这样查询: select id from t where num=10 union all select id from t where num=20
- 5.in 和 not in 也要慎用,否则会导致全表扫描,如:
select id from t where num in(1,2,3) -- 对于连续的数值,能用 between 就不要用 in 了: select id from t where num between 1 and 3
- 6.下面的查询也将导致全表扫描:
select id from t where name like ‘%李%’若要提高效率,可以考虑全文检索。
- 7.如果在 where 子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能将访问计划的选择推迟到运行时;它必须在编译时进行选择。然 而,如果在编译时建立访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句将进行全表扫描:
select id from t where num=@num -- 可以改为强制查询使用索引: select id from t with(index(索引名)) where num=@num
- 8.应尽量避免在 where 子句中对字段进行表达式操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where num/2=100 -- 应改为: select id from t where num=100*2
- 9.应尽量避免在where子句中对字段进行函数操作,这将导致引擎放弃使用索引而进行全表扫描。如:
select id from t where substring(name,1,3)=’abc’ -- name以abc开头的id应改为: select id from t where name like ‘abc%’
-
10.不要在 where 子句中的“=”左边进行函数、算术运算或其他表达式运算,否则系统将可能无法正确使用索引。
-
11.在使用索引字段作为条件时,如果该索引是复合索引,那么必须使用到该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用,并且应尽可能的让字段顺序与索引顺序相一致。
-
12.很多时候用 exists 代替 in 是一个好的选择:
select num from a where num in(select num from b) 用下面的语句替换: select num from a where exists(select 1 from b where num=a.num)
-
13.并不是所有索引对查询都有效,SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如一表中有字段sex,male、female几乎各一半,那么即使在sex上建了索引也对查询效率起不了作用。
-
14.索引并不是越多越好,索引固然可以提高相应的 select 的效率,但同时也降低了 insert 及 update 的效率,因为 insert 或 update 时有可能会重建索引,所以怎样建索引需要慎重考虑,视具体情况而定。一个表的索引数最好不要超过6个,若太多则应考虑一些不常使用到的列上建的索引是否有必要。
-
15.应尽可能的避免更新 clustered 索引数据列,因为 clustered 索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新 clustered 索引数据列,那么需要考虑是否应将该索引建为 clustered 索引。
-
16.尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。
-
17.尽可能的使用 varchar/nvarchar 代替 char/nchar ,因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高些。
-
18.任何地方都不要使用 select * from t ,用具体的字段列表代替“*”,不要返回用不到的任何字段。
-
19.尽量使用表变量来代替临时表。如果表变量包含大量数据,请注意索引非常有限(只有主键索引)。
-
20.避免频繁创建和删除临时表,以减少系统表资源的消耗。
-
21.临时表并不是不可使用,适当地使用它们可以使某些例程更有效,例如,当需要重复引用大型表或常用表中的某个数据集时。但是,对于一次性事件,最好使用导出表。
-
22.在新建临时表时,如果一次性插入数据量很大,那么可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果数据量不大,为了缓和系统表的资源,应先create table,然后insert。
-
23.如果使用到了临时表,在存储过程的最后务必将所有的临时表显式删除,先 truncate table ,然后 drop table ,这样可以避免系统表的较长时间锁定。
-
24.尽量避免使用游标,因为游标的效率较差,如果游标操作的数据超过1万行,那么就应该考虑改写。
-
25.使用基于游标的方法或临时表方法之前,应先寻找基于集的解决方案来解决问题,基于集的方法通常更有效。
-
26.与临时表一样,游标并不是不可使用。对小型数据集使用 FAST_FORWARD 游标通常要优于其他逐行处理方法,尤其是在必须引用几个表才能获得所需的数据时。在结果集中包括“合计”的例程通常要比使用游标执行的速度快。如果开发时间允许,基于游标的方法和基于集的方法都可以尝试一下,看哪一种方法的效果更好。
-
27.在所有的存储过程和触发器的开始处设置 SET NOCOUNT ON ,在结束时设置 SET NOCOUNT OFF 。无需在执行存储过程和触发器的每个语句后向客户端发送 DONE_IN_PROC 消息。
-
28.尽量避免向客户端返回大数据量,若数据量过大,应该考虑相应需求是否合理。
-
29.尽量避免大事务操作,提高系统并发能力。
九、总结
1、varchar(M)中M的涵义
最多存放M个字符,表示字符数。varchar(10)和(20)存储"Hello World"所占空间一样,但后者在排序时会消耗更多内存,因为order by col采用fixed_length计算col长度。
2、int(M)解析
int(M) 在 integer 数据类型中,M 表示最大显示宽度。在 int(M) 中,M 的值跟 int(M) 所占多少存储空间并无任何关系。 int(3)、int(4)、int(8) 在磁盘上都是占用 4 btyes 的存储空间。说白了,除了显示给用户的方式有点不同外,int(M) 跟 int 数据类型是相同的。如int(2)并不表示数据的大小最大为99,其最大的值和int表示的最大值一样,只是表示查询时显示的结果长度为2位。int(10)显示结果为0000000010,int(3)显示结果为010,就是显示的长度不一样而已 都是占用四个字节的空间。
3、MySql中int(10)和char(10)以及varchar(10)的区别
- int(10)的10表示显示的数据的长度,不是存储数据的大小;chart(10)和varchar(10)的10表示存储数据的大小,即表示存储多少个字符。
-
char(10)表示存储定长的10个字符,不足10个就用空格补齐,占用更多的存储空间
-
varchar(10)表示存储10个变长的字符,存储多少个就是多少个,空格也按一个字符存储,这一点是和char(10)的空格不同的,char(10)的空格表示占位不算一个字符
4、FLOAT和DOUBLE的区别是什么?
- FLOAT类型数据可以存储至多8位十进制数,并在内存中占4字节。
- DOUBLE类型数据可以存储至多18位十进制数,并在内存中占8字节。