数据类型与运算符
数据类型是数据库设计的基础,正确选择数据类型对于提高数据库性能、节省存储空间以及保证数据完整性至关重要。本章将详细介绍MySQL支持的数据类型以及如何选择合适的数据类型,同时也会介绍MySQL中的各种运算符及其使用方法。
4.1 数据类型介绍
MySQL支持多种数据类型,主要分为以下几大类:
- 整数类型
- 浮点数类型和定点数类型
- 日期与时间类型
- 文本字符串类型
- 二进制字符串类型
4.1.1 整数类型
MySQL提供了多种整数类型,它们的区别在于取值范围和存储空间不同。选择合适的整数类型可以节省存储空间并提高性能。
数据类型 | 字节数 | 有符号取值范围 | 无符号取值范围 | 说明 |
---|---|---|---|---|
TINYINT | 1 | -128 到 127 | 0 到 255 | 非常小的整数 |
SMALLINT | 2 | -32768 到 32767 | 0 到 65535 | 小整数 |
MEDIUMINT | 3 | -8388608 到 8388607 | 0 到 16777215 | 中等大小整数 |
INT | 4 | -2147483648 到 2147483647 | 0 到 4294967295 | 标准整数 |
BIGINT | 8 | -9223372036854775808 到 9223372036854775807 | 0 到 18446744073709551615 | 大整数 |
示例:整数类型的使用
-- 创建包含各种整数类型的表
CREATE TABLE integer_types (
id INT PRIMARY KEY AUTO_INCREMENT,
tiny_col TINYINT,
small_col SMALLINT,
medium_col MEDIUMINT,
int_col INT,
big_col BIGINT,
unsigned_tiny TINYINT UNSIGNED,
unsigned_int INT UNSIGNED
);
-- 插入数据
INSERT INTO integer_types (
tiny_col, small_col, medium_col, int_col, big_col,
unsigned_tiny, unsigned_int
) VALUES (
127, 32767, 8388607, 2147483647, 9223372036854775807,
255, 4294967295
);
4.1.2 浮点数类型和定点数类型
浮点数类型用于存储小数,包括FLOAT、DOUBLE和REAL。定点数类型用于存储精确的小数,主要是DECIMAL类型。
数据类型 | 字节数 | 精度 | 说明 |
---|---|---|---|
FLOAT | 4 | 约7位 | 单精度浮点数 |
DOUBLE | 8 | 约15位 | 双精度浮点数 |
DECIMAL(M,D) | 可变 | M位,小数点后D位 | 定点数,精确小数 |
示例:浮点数和定点数类型的使用
-- 创建包含浮点数和定点数类型的表
CREATE TABLE numeric_types (
id INT PRIMARY KEY AUTO_INCREMENT,
float_col FLOAT,
double_col DOUBLE,
decimal_col DECIMAL(10,2),
decimal_col2 DECIMAL(18,6)
);
-- 插入数据
INSERT INTO numeric_types (
float_col, double_col, decimal_col, decimal_col2
) VALUES (
3.1415926, 3.141592653589793, 1234567.89, 123456.789012
);
-- 浮点数的精度问题
-- 注意:以下两个值在浮点数中可能被视为相等
INSERT INTO numeric_types (float_col) VALUES (0.1);
INSERT INTO numeric_types (float_col) VALUES (0.10000000149011612);
提示:对于需要精确计算的场景,如财务数据,应使用DECIMAL类型而不是FLOAT或DOUBLE,以避免浮点数精度问题。
4.1.3 日期与时间类型
MySQL提供了多种日期与时间类型,用于存储日期、时间或日期时间组合。
数据类型 | 字节数 | 取值范围 | 格式 | 说明 |
---|---|---|---|---|
DATE | 3 | 1000-01-01 到 9999-12-31 | YYYY-MM-DD | 日期值 |
TIME | 3 | -838:59:59 到 838:59:59 | HH:MM:SS | 时间值或持续时间 |
DATETIME | 8 | 1000-01-01 00:00:00 到 9999-12-31 23:59:59 | YYYY-MM-DD HH:MM:SS | 混合日期和时间值 |
TIMESTAMP | 4 | 1970-01-01 00:00:01 UTC 到 2038-01-19 03:14:07 UTC | YYYY-MM-DD HH:MM:SS | 时间戳,支持时区 |
YEAR | 1 | 1901 到 2155 | YYYY | 年份值 |
示例:日期与时间类型的使用
-- 创建包含日期与时间类型的表
CREATE TABLE datetime_types (
id INT PRIMARY KEY AUTO_INCREMENT,
date_col DATE,
time_col TIME,
datetime_col DATETIME,
timestamp_col TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
year_col YEAR
);
-- 插入数据
INSERT INTO datetime_types (
date_col, time_col, datetime_col, year_col
) VALUES (
'2023-05-15', '14:30:45', '2023-05-15 14:30:45', 2023
);
-- 使用系统日期时间
INSERT INTO datetime_types (
date_col, datetime_col
) VALUES (
CURDATE(), NOW()
);
-- 更新记录,timestamp_col会自动更新
UPDATE datetime_types SET date_col = '2023-05-16' WHERE id = 1;
提示:DATETIME和TIMESTAMP的主要区别在于:
- 取值范围不同:DATETIME的范围更大
- 时区支持:TIMESTAMP存储的是UTC时间,查询时会根据会话的时区设置进行转换;而DATETIME存储的是原始时间,不会进行时区转换
- 存储空间:TIMESTAMP占用4字节,DATETIME占用8字节
- 自动更新:TIMESTAMP可以设置为自动更新,DATETIME需要通过触发器或应用程序实现
4.1.4 文本字符串类型
文本字符串类型用于存储字符数据,包括CHAR、VARCHAR、TEXT等。
数据类型 | 最大长度 | 存储方式 | 说明 |
---|---|---|---|
CHAR(M) | 255 | 固定长度 | 适合存储长度固定的数据,如身份证号、手机号等 |
VARCHAR(M) | 65535 | 可变长度 | 适合存储长度可变的数据,如姓名、地址等 |
TINYTEXT | 255 | 可变长度 | 非常小的文本数据 |
TEXT | 65535 | 可变长度 | 小文本数据 |
MEDIUMTEXT | 16777215 | 可变长度 | 中等大小的文本数据 |
LONGTEXT | 4294967295 | 可变长度 | 大型文本数据 |
示例:文本字符串类型的使用
-- 创建包含文本字符串类型的表
CREATE TABLE text_types (
id INT PRIMARY KEY AUTO_INCREMENT,
char_col CHAR(10),
varchar_col VARCHAR(100),
text_col TEXT,
mediumtext_col MEDIUMTEXT,
longtext_col LONGTEXT
);
-- 插入数据
INSERT INTO text_types (
char_col, varchar_col, text_col, mediumtext_col, longtext_col
) VALUES (
'固定长度', '这是一个可变长度的字符串', '这是一个文本数据...',
'这是一个中等大小的文本数据...', '这是一个大型文本数据...'
);
提示:在选择CHAR和VARCHAR时,通常的建议是:
- 如果数据长度基本固定,或者长度变化很小,使用CHAR类型
- 如果数据长度变化较大,使用VARCHAR类型
- 对于非常长的文本数据,使用TEXT类型系列
4.1.5 二进制字符串类型
二进制字符串类型用于存储二进制数据,包括BINARY、VARBINARY、BLOB等。
数据类型 | 最大长度 | 存储方式 | 说明 |
---|---|---|---|
BINARY(M) | 255 | 固定长度 | 固定长度的二进制数据 |
VARBINARY(M) | 65535 | 可变长度 | 可变长度的二进制数据 |
TINYBLOB | 255 | 可变长度 | 非常小的二进制数据 |
BLOB | 65535 | 可变长度 | 小二进制数据 |
MEDIUMBLOB | 16777215 | 可变长度 | 中等大小的二进制数据 |
LONGBLOB | 4294967295 | 可变长度 | 大型二进制数据 |
示例:二进制字符串类型的使用
-- 创建包含二进制字符串类型的表
CREATE TABLE binary_types (
id INT PRIMARY KEY AUTO_INCREMENT,
binary_col BINARY(10),
varbinary_col VARBINARY(100),
blob_col BLOB,
mediumblob_col MEDIUMBLOB,
longblob_col LONGBLOB
);
-- 插入二进制数据(通常通过应用程序插入,这里仅作示例)
INSERT INTO binary_types (varbinary_col) VALUES (UNHEX('48656C6C6F20576F726C64'));
-- 上述语句插入的是ASCII字符串"Hello World"的十六进制表示
提示:二进制数据类型通常用于存储图片、音频、视频等多媒体文件,或者加密数据、序列化对象等。但在实际应用中,通常不建议将大文件直接存储在数据库中,而是存储文件路径,将文件保存在文件系统中。
4.2 如何选择数据类型
选择合适的数据类型对于数据库性能、存储空间和数据完整性至关重要。以下是一些选择数据类型的基本原则:
4.2.1 最小化数据类型的大小
在满足需求的前提下,应尽量选择占用存储空间小的数据类型。这不仅可以节省存储空间,还可以提高数据的读写性能,因为更小的数据类型意味着更少的I/O操作和更快的内存处理。
选择合适整数类型的步骤
- 确定需要存储的数值范围
- 选择能够覆盖该范围的最小整数类型
- 如果数值永远不会为负数,考虑使用无符号(UNSIGNED)版本
4.2.2 优先考虑简单数据类型
简单数据类型通常具有更好的性能。例如,整数类型的处理速度比字符类型快,因此,如果可以用整数类型存储数据(如用0和1表示布尔值),应优先选择整数类型。
4.2.3 避免NULL值
NULL值会使索引、索引统计和值比较变得复杂,并且会占用额外的存储空间。在设计表时,应尽量避免使用NULL值,特别是对于索引列。如果必须使用NULL值,应确保该列不会被频繁用于查询条件。
4.2.4 为字符串选择合适的类型
对于字符串类型,应根据数据的长度和特性选择合适的类型:
- 如果数据长度固定或变化很小,使用CHAR类型
- 如果数据长度变化较大,使用VARCHAR类型
- 对于非常长的文本数据,使用TEXT类型系列
- 对于二进制数据,使用BLOB类型系列
4.2.5 为日期和时间选择合适的类型
对于日期和时间类型,应根据需求选择合适的类型:
- 如果只需要日期,使用DATE类型
- 如果只需要时间,使用TIME类型
- 如果需要日期和时间,并且不需要时区支持,使用DATETIME类型
- 如果需要日期和时间,并且需要时区支持,使用TIMESTAMP类型
- 如果只需要年份,使用YEAR类型
4.2.6 为小数选择合适的类型
对于小数类型,应根据精度要求选择合适的类型:
- 如果需要精确计算,特别是对于财务数据,使用DECIMAL类型
- 如果可以接受一定的精度损失,并且需要较高的计算性能,使用FLOAT或DOUBLE类型
示例:合理选择数据类型
-- 不合理的表设计
CREATE TABLE bad_design (
id INT PRIMARY KEY,
name VARCHAR(255), -- 对于大多数姓名来说太长
age INT, -- 对于年龄来说太大
salary FLOAT, -- 不适合存储财务数据
hire_date VARCHAR(20) -- 不适合存储日期
);
-- 合理的表设计
CREATE TABLE good_design (
id INT PRIMARY KEY,
name VARCHAR(50), -- 更合适的长度
age TINYINT UNSIGNED, -- 对于年龄来说足够,且使用无符号
salary DECIMAL(10,2), -- 适合存储财务数据
hire_date DATE -- 使用日期类型
);
4.3 常见运算符介绍
运算符是SQL中用于执行操作的特殊符号。MySQL支持多种类型的运算符,包括算术运算符、比较运算符、逻辑运算符、位运算符等。
4.3.1 运算符概述
MySQL支持的运算符主要分为以下几类:
- 算术运算符
- 比较运算符
- 逻辑运算符
- 位运算符
4.3.2 算术运算符
算术运算符用于执行数学运算,如加减乘除等。
运算符 | 描述 | 示例 |
---|---|---|
+ | 加法 | 10 + 5 = 15 |
- | 减法 | 10 - 5 = 5 |
* | 乘法 | 10 * 5 = 50 |
/ | 除法 | 10 / 5 = 2 |
% | 取模(求余) | 10 % 3 = 1 |
DIV | 整除 | 10 DIV 3 = 3 |
MOD | 取模(同%) | 10 MOD 3 = 1 |
示例:使用算术运算符
-- 基本算术运算
SELECT 10 + 5 AS addition,
10 - 5 AS subtraction,
10 * 5 AS multiplication,
10 / 5 AS division,
10 % 3 AS modulo,
10 DIV 3 AS integer_division;
-- 对表中的数据进行算术运算
SELECT id, name, salary, salary * 1.1 AS increased_salary
FROM employees;
-- 计算两个日期之间的天数差
SELECT DATEDIFF('2023-12-31', '2023-01-01') AS days_diff;
-- 计算年龄
SELECT name, DATE_FORMAT(FROM_DAYS(DATEDIFF(NOW(), birth_date)), '%Y') + 0 AS age
FROM employees;
4.3.3 比较运算符
比较运算符用于比较两个表达式的值,并返回布尔值(TRUE、FALSE或NULL)。
运算符 | 描述 | 示例 |
---|---|---|
= | 等于 | 5 = 5 返回 TRUE |
<> 或 != | 不等于 | 5 <> 3 返回 TRUE |
< | 小于 | 5 < 10 返回 TRUE |
> | 大于 | 10 > 5 返回 TRUE |
<= | 小于等于 | 5 <= 5 返回 TRUE |
>= | 大于等于 | 10 >= 5 返回 TRUE |
BETWEEN | 在指定范围内 | 5 BETWEEN 1 AND 10 返回 TRUE |
IN | 在指定集合中 | 5 IN (1, 3, 5, 7) 返回 TRUE |
NOT IN | 不在指定集合中 | 5 NOT IN (1, 3, 7) 返回 TRUE |
LIKE | 模式匹配 | 'abc' LIKE 'a%' 返回 TRUE |
NOT LIKE | 不匹配模式 | 'abc' NOT LIKE 'd%' 返回 TRUE |
IS NULL | 为空 | NULL IS NULL 返回 TRUE |
IS NOT NULL | 不为空 | 5 IS NOT NULL 返回 TRUE |
示例:使用比较运算符
-- 基本比较运算
SELECT 5 = 5 AS equal,
5 <> 3 AS not_equal,
5 < 10 AS less_than,
10 > 5 AS greater_than;
-- 在WHERE子句中使用比较运算符
SELECT id, name, salary
FROM employees
WHERE salary > 5000;
-- 使用BETWEEN运算符
SELECT id, name, age
FROM employees
WHERE age BETWEEN 25 AND 35;
-- 使用IN运算符
SELECT id, name, department
FROM employees
WHERE department IN ('HR', 'Finance', 'IT');
-- 使用LIKE运算符进行模式匹配
SELECT id, name
FROM employees
WHERE name LIKE 'J%'; -- 查找姓名以J开头的员工
-- 使用IS NULL运算符
SELECT id, name, manager_id
FROM employees
WHERE manager_id IS NULL; -- 查找没有经理的员工
4.3.4 逻辑运算符
逻辑运算符用于组合多个条件,并返回布尔值。
运算符 | 描述 | 示例 |
---|---|---|
AND | 逻辑与 | TRUE AND TRUE 返回 TRUE |
OR | 逻辑或 | TRUE OR FALSE 返回 TRUE |
NOT | 逻辑非 | NOT FALSE 返回 TRUE |
XOR | 逻辑异或 | TRUE XOR FALSE 返回 TRUE |
示例:使用逻辑运算符
-- 基本逻辑运算
SELECT TRUE AND TRUE AS and_result,
TRUE OR FALSE AS or_result,
NOT FALSE AS not_result,
TRUE XOR FALSE AS xor_result;
-- 在WHERE子句中使用逻辑运算符
SELECT id, name, salary, department
FROM employees
WHERE salary > 5000 AND department = 'IT';
-- 组合多个条件
SELECT id, name, age, salary
FROM employees
WHERE (age < 30 AND salary > 4000) OR (age >= 30 AND salary > 6000);
-- 使用NOT运算符
SELECT id, name, department
FROM employees
WHERE NOT department = 'HR';
-- 使用XOR运算符
SELECT id, name, age, salary
FROM employees
WHERE (age < 30 XOR salary > 5000);
4.3.5 位运算符
位运算符用于对二进制数进行位级操作。
运算符 | 描述 | 示例 |
---|---|---|
& | 位与 | 5 & 3 = 1 (二进制: 101 & 011 = 001) |
| | 位或 | 5 | 3 = 7 (二进制: 101 | 011 = 111) |
^ | 位异或 | 5 ^ 3 = 6 (二进制: 101 ^ 011 = 110) |
~ | 位非 | ~5 = -6 (二进制补码表示) |
<< | 左移 | 5 << 1 = 10 (二进制: 101 << 1 = 1010) |
>> | 右移 | 5 >> 1 = 2 (二进制: 101 >> 1 = 10) |
示例:使用位运算符
-- 基本位运算
SELECT 5 & 3 AS bit_and,
5 | 3 AS bit_or,
5 ^ 3 AS bit_xor,
~5 AS bit_not,
5 << 1 AS left_shift,
5 >> 1 AS right_shift;
-- 使用位运算符存储权限信息
-- 假设有以下权限:
-- 1 (0001) - 读取权限
-- 2 (0010) - 写入权限
-- 4 (0100) - 更新权限
-- 8 (1000) - 删除权限
-- 创建用户权限表
CREATE TABLE user_permissions (
user_id INT PRIMARY KEY,
permissions INT NOT NULL
);
-- 插入具有读取和写入权限的用户
INSERT INTO user_permissions VALUES (1, 1 | 2); -- 权限值为3 (0011)
-- 插入具有所有权限的用户
INSERT INTO user_permissions VALUES (2, 1 | 2 | 4 | 8); -- 权限值为15 (1111)
-- 检查用户是否具有读取权限
SELECT user_id, (permissions & 1) > 0 AS has_read_permission
FROM user_permissions;
-- 检查用户是否具有写入和更新权限
SELECT user_id, ((permissions & 2) > 0 AND (permissions & 4) > 0) AS has_write_and_update_permissions
FROM user_permissions;
-- 添加删除权限
UPDATE user_permissions SET permissions = permissions | 8 WHERE user_id = 1;
-- 移除写入权限
UPDATE user_permissions SET permissions = permissions & ~2 WHERE user_id = 2;
4.3.6 运算符的优先级
当一个表达式包含多个运算符时,MySQL会按照运算符的优先级来决定运算顺序。如果不确定运算符的优先级,建议使用括号来明确运算顺序。
以下是MySQL中运算符的优先级从高到低排列:
优先级 | 运算符 |
---|---|
1 | 圆括号 () |
2 | 位非 ~ |
3 | 算术运算符:! (逻辑非), - (负号), + (正号) |
4 | 乘法、除法、取模:*, /, DIV, %, MOD |
5 | 加法、减法:+, - |
6 | 位左移、位右移:<<, >> |
7 | 比较运算符:<, <=, >, >=, <>, !=, =, IS, LIKE, REGEXP, IN |
8 | 位与 & |
9 | 位异或 ^ |
10 | 位或 | |
11 | 逻辑与 AND, && |
12 | 逻辑异或 XOR |
13 | 逻辑或 OR, || |
示例:运算符优先级
-- 以下两个表达式的结果不同,因为乘法的优先级高于加法
SELECT 5 + 3 * 2; -- 结果为 11 (先计算 3*2=6,再计算 5+6=11)
SELECT (5 + 3) * 2; -- 结果为 16 (先计算 5+3=8,再计算 8*2=16)
-- 逻辑运算符的优先级
SELECT TRUE OR FALSE AND FALSE; -- 结果为 TRUE (先计算 FALSE AND FALSE=FALSE,再计算 TRUE OR FALSE=TRUE)
SELECT (TRUE OR FALSE) AND FALSE; -- 结果为 FALSE (先计算 TRUE OR FALSE=TRUE,再计算 TRUE AND FALSE=FALSE)
-- 比较运算符和逻辑运算符的优先级
SELECT 5 > 3 AND 2 < 4; -- 结果为 TRUE (先计算两个比较运算,再计算逻辑与)
SELECT 5 > (3 AND 2) < 4; -- 结果取决于具体实现,建议使用括号明确运算顺序
提示:虽然了解运算符的优先级很重要,但在编写复杂的SQL表达式时,为了代码的可读性和避免错误,建议使用括号来明确运算顺序,而不是依赖运算符的优先级。