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")最佳实践
数据库名称应遵循小写+下划线命名,避免使用保留字(如order、user)。名称一经确定尽量不要改动,因为涉及大量代码调整。
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_GENERATED3. 约束(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 Insensitiveutf8mb4_bin:按二进制值比较,大小写敏感(如a≠A)
# 演示排序规则区别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)。要求如下:
articles包含:自增主键、标题(唯一,非空)、正文、作者名、发布时间(默认当前时间)、状态(0草稿/1已发布,默认为0)。
comments包含:自增主键、关联文章 ID、评论人昵称、评论内容、创建时间。
所有字符串使用
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 思维 !