IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
上一篇文章我们成功用 Python 连接了 MySQL,并创建了第一个数据库与表。但你可能会好奇:数据库和表在 MySQL 内部是如何组织的?建库建表时有哪些重要的配置选项?为什么有时候中文字符会变成乱码?今天我们就深入"容器"内部,掌握 DDL(数据定义语言)的核心知识。
1. 数据库(Database):数据的顶层容器
在 MySQL 中,数据库(Schema)是表的集合,同时也承载着权限、字符集等默认配置。可以把数据库理解为 Excel 中的工作簿,表就是其中的工作表。
1.1 创建数据库的完整语法
bash
CREATE DATABASE [IF NOT EXISTS] 数据库名
[DEFAULT CHARACTER SET 字符集]
[DEFAULT COLLATE 排序规则];
为什么需要 IF NOT EXISTS?
在自动化脚本中,重复执行建库语句会导致错误。加上 IF NOT EXISTS 后,如果库已存在则静默跳过,不会中断执行。
1.2 用 Python 管理数据库
下面我们用 Python 来创建、查看、删除数据库,完整代码如下:
bash
import mysql.connector
from mysql.connector import Error
def get_connection():
"""创建连接(不加 database 参数,以便执行建库操作)"""
return mysql.connector.connect(
host="127.0.0.1",
port=3306,
user="root",
password="MyNewPass123!"
)
try:
conn = get_connection()
cursor = conn.cursor()
# 1. 创建数据库
cursor.execute("CREATE DATABASE IF NOT EXISTS shop DEFAULT CHARACTER SET utf8mb4")
print("✅ 数据库 shop 创建成功(或已存在)")
# 2. 查看所有数据库
cursor.execute("SHOW DATABASES")
databases = cursor.fetchall()
print("\n📋 当前数据库列表:")
for db in databases:
print(f" - {db[0]}")
# 3. 查看创建语句(便于审查)
cursor.execute("SHOW CREATE DATABASE shop")
result = cursor.fetchone()
print(f"\n🔍 shop 建库语句:\n{result[1]}")
except Error as e:
print(f"❌ 错误: {e}")
finally:
if conn.is_connected():
cursor.close()
conn.close()
运行这段代码,预期输出:
bash
✅ 数据库 shop 创建成功(或已存在)
📋 当前数据库列表:
- information_schema
- myblog
- mysql
- performance_schema
- shop
- sys
🔍 shop 建库语句:
CREATE DATABASE `shop` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */
可以看到 MySQL 自动为我们选用了默认的排序规则 utf8mb4_0900_ai_ci。
1.3 修改与删除数据库
bash
# 修改数据库的字符集
cursor.execute("ALTER DATABASE shop DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin")
cursor.execute("SHOW CREATE DATABASE shop")
print(cursor.fetchone()[1])
# 删除数据库(谨慎!生产环境禁用此操作)
# cursor.execute("DROP DATABASE IF EXISTS shop")
最佳实践
数据库名称应遵循小写+下划线命名,避免使用保留字(如
order、user)。名称一经确定尽量不要改动,因为涉及大量代码调整。
2. 表(Table):数据的真正载体
表是数据的直接存放地。定义一张表,就是定义它包含哪些列,以及每列的数据类型和约束。
2.1 建表的基本语法
bash
CREATE TABLE [IF NOT EXISTS] 表名 (
列名1 数据类型 [约束],
列名2 数据类型 [约束],
...
[表级约束]
) [ENGINE=引擎] [DEFAULT CHARSET=字符集];
2.2 创建一张标准的商品表
我们结合电商场景,用 Python 创建一张 products 表:
bash
import mysql.connector
conn = mysql.connector.connect(
host="127.0.0.1",
port=3306,
user="root",
password="MyNewPass123!",
database="shop"
)
cursor = conn.cursor()
create_table_sql = """
CREATE TABLE IF NOT EXISTS products (
id INT AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
price DECIMAL(10, 2) NOT NULL,
stock INT DEFAULT 0,
category VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uk_title (title)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
"""
cursor.execute(create_table_sql)
print("✅ 表 products 创建成功")
# 查看表结构
cursor.execute("DESCRIBE products")
print("\n📋 products 表结构:")
print(f"{'Field':<15} {'Type':<20} {'Null':<8} {'Key':<8} {'Default':<15} {'Extra':<20}")
for row in cursor.fetchall():
print(f"{row[0]:<15} {row[1]:<20} {row[2]:<8} {row[3]:<8} {str(row[4]):<15} {row[5]:<20}")
cursor.close()
conn.close()
预期输出:
bash
✅ 表 products 创建成功
📋 products 表结构:
Field Type Null Key Default Extra
id int NO PRI None auto_increment
title varchar(200) NO UNI None
price decimal(10,2) NO None
stock int YES 0
category varchar(50) YES None
created_at timestamp YES CURRENT_TIMESTAMP DEFAULT_GENERATED
3. 约束(Constraints):数据完整性的守护神
约束就像是数据的"安检门",不满足条件的数据将被拒之门外。以下是 MySQL 中最常用的约束。
3.1 PRIMARY KEY(主键)
主键 = 非空 + 唯一 + 一张表只能有一个
bash
-- 单列主键
id INT PRIMARY KEY AUTO_INCREMENT
-- 复合主键(多列共同唯一)
PRIMARY KEY (user_id, role_id)
为什么推荐 AUTO_INCREMENT?
自增主键能保证插入有序,对 InnoDB 的 B+Tree 索引非常友好,可减少页分裂,提升写入性能。
3.2 UNIQUE(唯一约束)
确保一列或多列的组合值不重复,但允许 NULL(多个 NULL 不算重复)。
bash
# 测试唯一约束
try:
cursor.execute("INSERT INTO products (title, price) VALUES ('iPhone 15', 6999.00)")
conn.commit()
print("✅ 插入成功")
# 再次插入相同 title
cursor.execute("INSERT INTO products (title, price) VALUES ('iPhone 15', 6999.00)")
conn.commit()
except mysql.connector.IntegrityError as e:
print(f"❌ 违反唯一约束: {e}")
预期输出:
bash
✅ 插入成功
❌ 违反唯一约束: 1062 (23000): Duplicate entry 'iPhone 15' for key 'products.uk_title'
3.3 NOT NULL(非空约束)
列值不能为 NULL。对于重要字段(如价格、标题)必须施加此约束。
3.4 DEFAULT(默认值)
插入时不指定该列,则自动填充默认值。上面 stock 默认 0,created_at 默认当前时间戳。
3.5 CHECK(检查约束,MySQL 8.0.16+)
限制列的取值范围,比如价格必须大于 0:
bash
price DECIMAL(10,2) NOT NULL CHECK (price > 0)
3.6 FOREIGN KEY(外键)
用于关联两张表,确保子表引用的数据在父表存在。但互联网高并发场景下很少使用数据库级外键,而是由应用层保证逻辑一致性(避免死锁、性能损耗)。
bash
-- 仅做了解
CREATE TABLE orders (
id INT PRIMARY KEY AUTO_INCREMENT,
product_id INT,
FOREIGN KEY (product_id) REFERENCES products(id)
);
常见误区
很多教程强调外键的重要性,但在大厂实践中,外键通常被禁用。原因是它会引入额外的锁和级联操作,影响性能。数据一致性交由 Service 层或分布式事务保障。
4. 字符集与排序规则:彻底解决乱码
4.1 你遇到的乱码,大概率是字符集在捣鬼
当存储 emoji 表情(如😊)时,如果使用 utf8(最多 3 字节),就会报错或出现 ?。MySQL 8.0 默认的 utf8mb4 支持 4 字节 Unicode,是真正意义上的 UTF-8。
4.2 常用字符集对比
4.3 排序规则(Collation)的影响
排序规则决定了字符串如何比较和排序,命名规则是:字符集_语言_大小写敏感性。
-
utf8mb4_general_ci:通用、快速,但不严谨(旧默认) -
utf8mb4_unicode_ci:更准确,基于 Unicode 标准 -
utf8mb4_0900_ai_ci:MySQL 8.0 新默认,基于 Unicode 9.0,AI=Accent Insensitive -
utf8mb4_bin:按二进制值比较,大小写敏感(如a≠A)
bash
# 演示排序规则区别
cursor.execute("SELECT 'a' = 'A' COLLATE utf8mb4_0900_ai_ci") # 1(相等)
print("大小写不敏感:", cursor.fetchone()[0])
cursor.execute("SELECT 'a' = 'A' COLLATE utf8mb4_bin") # 0(不相等)
print("大小写敏感:", cursor.fetchone()[0])
预期输出:
4.4 字符集配置建议(最佳实践)
在创建数据库时就指定好,避免后续迁移痛苦:
bash
CREATE DATABASE mydb
DEFAULT CHARACTER SET utf8mb4
DEFAULT COLLATE utf8mb4_unicode_ci;
建表时也可以覆盖数据库的默认设置。确保 Python 连接也指定 charset='utf8mb4':
bash
conn = mysql.connector.connect(
...
charset='utf8mb4',
collation='utf8mb4_unicode_ci'
)
5. 修改表结构:拥抱变化
业务需求变化时,需要修改表结构。MySQL 通过 ALTER TABLE 支持:
-
添加列:
ALTER TABLE products ADD COLUMN description TEXT AFTER category; -
修改列类型:
ALTER TABLE products MODIFY COLUMN price DECIMAL(12,2); -
删除列:
ALTER TABLE products DROP COLUMN description; -
添加索引:
ALTER TABLE products ADD INDEX idx_category (category);
在线 DDL 的风险警示
ALTER TABLE 可能会锁表! 对于千万级大表,直接修改结构会导致长时间业务不可用。MySQL 8.0 对许多操作支持 In-place 算法(不会全表复制),但仍有风险。大表变更推荐使用 pt-online-schema-change 工具,本系列后续会详细介绍。
6. 动手试试:设计你自己的数据表
假设你要为博客系统设计两张表:文章表(articles) 和 评论表(comments)。要求如下:
-
articles 包含:自增主键、标题(唯一,非空)、正文、作者名、发布时间(默认当前时间)、状态(0草稿/1已发布,默认为0)。
-
comments 包含:自增主键、关联文章 ID、评论人昵称、评论内容、创建时间。
-
所有字符串使用
utf8mb4,引擎使用 InnoDB。
请先自己尝试写出建表 SQL,再用 Python 执行并打印 SHOW CREATE TABLE 的结果。
参考代码:
bash
conn = mysql.connector.connect(
host="127.0.0.1", port=3306,
user="root", password="MyNewPass123!",
database="blog"
)
cursor = conn.cursor()
# 建库
cursor.execute("CREATE DATABASE IF NOT EXISTS blog DEFAULT CHARSET utf8mb4")
cursor.execute("USE blog")
# 建文章表
cursor.execute("""
CREATE TABLE IF NOT EXISTS articles (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL UNIQUE,
content TEXT NOT NULL,
author VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
status TINYINT DEFAULT 0 COMMENT '0草稿,1已发布'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
""")
# 建评论表
cursor.execute("""
CREATE TABLE IF NOT EXISTS comments (
id INT AUTO_INCREMENT PRIMARY KEY,
article_id INT NOT NULL,
nickname VARCHAR(50) NOT NULL,
body TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
""")
# 打印建表语句
cursor.execute("SHOW CREATE TABLE articles")
print(cursor.fetchone()[1])
cursor.execute("SHOW CREATE TABLE comments")
print(cursor.fetchone()[1])
cursor.close()
conn.close()
7. 总结与下篇预告
今天我们掌握了 MySQL 中两个核心容器------数据库和表的创建与管理,学习了主键、唯一、非空、默认值等约束的实际用法,并彻底弄懂了字符集与排序规则。这些知识是后续所有操作的地基。
下篇文章将深入 数据类型全解,我们一起探究 VARCHAR 与 CHAR 的真相、如何选择最适合的数值与日期类型,以及 JSON 列的最佳实践。敬请期待!
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !