MySQL 系列:第2篇 库和表,一切的容器
2026/6/16 10:19:04 网站建设 项目流程

IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。


上一篇文章我们成功用 Python 连接了 MySQL,并创建了第一个数据库与表。但你可能会好奇:数据库和表在 MySQL 内部是如何组织的?建库建表时有哪些重要的配置选项?为什么有时候中文字符会变成乱码?今天我们就深入“容器”内部,掌握 DDL(数据定义语言)的核心知识。

1. 数据库(Database):数据的顶层容器

在 MySQL 中,数据库(Schema)是表的集合,同时也承载着权限、字符集等默认配置。可以把数据库理解为 Excel 中的工作簿,表就是其中的工作表。

1.1 创建数据库的完整语法

CREATE DATABASE[IF NOT EXISTS]数据库名[DEFAULT CHARACTER SET 字符集][DEFAULT COLLATE 排序规则];

为什么需要IF NOT EXISTS
在自动化脚本中,重复执行建库语句会导致错误。加上IF NOT EXISTS后,如果库已存在则静默跳过,不会中断执行。

1.2 用 Python 管理数据库

下面我们用 Python 来创建、查看、删除数据库,完整代码如下:

importmysql.connector from mysql.connectorimportError def get_connection():"""创建连接(不加 database 参数,以便执行建库操作)"""returnmysql.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📋 当前数据库列表:")fordbindatabases: 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:ifconn.is_connected(): cursor.close()conn.close()

运行这段代码,预期输出:

✅ 数据库 shop 创建成功(或已存在) 📋 当前数据库列表: - information_schema - myblog - mysql - performance_schema - shop - sys 🔍 shop 建库语句: CREATE DATABASE`shop`/*!40100DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci */

可以看到 MySQL 自动为我们选用了默认的排序规则utf8mb4_0900_ai_ci

1.3 修改与删除数据库

# 修改数据库的字符集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")

最佳实践
数据库名称应遵循小写+下划线命名,避免使用保留字(如orderuser)。名称一经确定尽量不要改动,因为涉及大量代码调整。

2. 表(Table):数据的真正载体

表是数据的直接存放地。定义一张表,就是定义它包含哪些列,以及每列的数据类型和约束。

2.1 建表的基本语法

CREATE TABLE[IF NOT EXISTS]表名(列名1 数据类型[约束], 列名2 数据类型[约束],...[表级约束])[ENGINE=引擎][DEFAULTCHARSET=字符集];

2.2 创建一张标准的商品表

我们结合电商场景,用 Python 创建一张products表:

importmysql.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(idINT AUTO_INCREMENT, title VARCHAR(200)NOT NULL, price DECIMAL(10,2)NOT NULL, stock INT DEFAULT0, category VARCHAR(50), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(id), UNIQUE KEY uk_title(title))ENGINE=InnoDB DEFAULTCHARSET=utf8mb4COLLATE=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}")forrowincursor.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()

预期输出

✅ 表 products 创建成功 📋 products 表结构: Field Type Null Key Default Extraidint NO PRI None auto_increment title varchar(200)NO UNI None price decimal(10,2)NO None stock int YES0category varchar(50)YES None created_at timestamp YES CURRENT_TIMESTAMP DEFAULT_GENERATED

3. 约束(Constraints):数据完整性的守护神

约束就像是数据的“安检门”,不满足条件的数据将被拒之门外。以下是 MySQL 中最常用的约束。

3.1 PRIMARY KEY(主键)

主键 = 非空 + 唯一 + 一张表只能有一个

-- 单列主键idINT PRIMARY KEY AUTO_INCREMENT -- 复合主键(多列共同唯一) PRIMARY KEY(user_id, role_id)

为什么推荐 AUTO_INCREMENT?
自增主键能保证插入有序,对 InnoDB 的 B+Tree 索引非常友好,可减少页分裂,提升写入性能。

3.2 UNIQUE(唯一约束)

确保一列或多列的组合值不重复,但允许 NULL(多个 NULL 不算重复)。

# 测试唯一约束try: cursor.execute("INSERT INTO products (title, price) VALUES ('iPhone 15', 6999.00)")conn.commit()print("✅ 插入成功")# 再次插入相同 titlecursor.execute("INSERT INTO products (title, price) VALUES ('iPhone 15', 6999.00)")conn.commit()except mysql.connector.IntegrityError as e: print(f"❌ 违反唯一约束: {e}")

预期输出

✅ 插入成功 ❌ 违反唯一约束:1062(23000): Duplicate entry'iPhone 15'forkey'products.uk_title'

3.3 NOT NULL(非空约束)

列值不能为 NULL。对于重要字段(如价格、标题)必须施加此约束。

3.4 DEFAULT(默认值)

插入时不指定该列,则自动填充默认值。上面stock默认 0,created_at默认当前时间戳。

3.5 CHECK(检查约束,MySQL 8.0.16+)

限制列的取值范围,比如价格必须大于 0:

price DECIMAL(10,2)NOT NULL CHECK(price>0)

3.6 FOREIGN KEY(外键)

用于关联两张表,确保子表引用的数据在父表存在。但互联网高并发场景下很少使用数据库级外键,而是由应用层保证逻辑一致性(避免死锁、性能损耗)。

-- 仅做了解 CREATE TABLE orders(idINT 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:按二进制值比较,大小写敏感(如aA

# 演示排序规则区别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 字符集配置建议(最佳实践)

在创建数据库时就指定好,避免后续迁移痛苦:

CREATE DATABASE mydb DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_unicode_ci;

建表时也可以覆盖数据库的默认设置。确保 Python 连接也指定charset='utf8mb4'

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)。要求如下:

  1. articles包含:自增主键、标题(唯一,非空)、正文、作者名、发布时间(默认当前时间)、状态(0草稿/1已发布,默认为0)。

  2. comments包含:自增主键、关联文章 ID、评论人昵称、评论内容、创建时间。

  3. 所有字符串使用utf8mb4,引擎使用 InnoDB。

请先自己尝试写出建表 SQL,再用 Python 执行并打印SHOW CREATE TABLE的结果。

参考代码

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(idINT 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 DEFAULT0COMMENT'0草稿,1已发布')ENGINE=InnoDB DEFAULTCHARSET=utf8mb4""")# 建评论表cursor.execute(""" CREATE TABLE IF NOT EXISTS comments(idINT 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 DEFAULTCHARSET=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 思维 !

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询