数据类型与运算符

数据类型是数据库设计的基础,正确选择数据类型对于提高数据库性能、节省存储空间以及保证数据完整性至关重要。本章将详细介绍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的主要区别在于:

  1. 取值范围不同:DATETIME的范围更大
  2. 时区支持:TIMESTAMP存储的是UTC时间,查询时会根据会话的时区设置进行转换;而DATETIME存储的是原始时间,不会进行时区转换
  3. 存储空间:TIMESTAMP占用4字节,DATETIME占用8字节
  4. 自动更新: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操作和更快的内存处理。

选择合适整数类型的步骤

  1. 确定需要存储的数值范围
  2. 选择能够覆盖该范围的最小整数类型
  3. 如果数值永远不会为负数,考虑使用无符号(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表达式时,为了代码的可读性和避免错误,建议使用括号来明确运算顺序,而不是依赖运算符的优先级。