python:mysql数据库

一、创建MySql数据库

复制代码
import pymysql
db = pymysql.connect(host='localhost',user='root',password='root',port=3306)
cursor = db.cursor()
cursor.execute('SELECT VERSION()')
data = cursor.fetchone()
print('Database version:',data)
cursor.execute("CREATE DATABASE spiders DEFAULT CHARACTER SET utf8mb4")
db.close()

cursor = db.cursor() 是使用 pymysql 连接 MySQL 数据库后创建一个**游标对象(cursor)**的关键步骤。它的作用是:

通过游标来执行 SQL 语句,并获取查询结果。


🔍 详细解释

当你执行:

复制代码
import pymysql
db = pymysql.connect(host='localhost', user='root', password='root', port=3306)
cursor = db.cursor()
  • db 是数据库连接对象,负责与 MySQL 服务器建立和维持连接。
  • cursor() 方法返回一个 游标对象,你可以把它理解为"SQL 命令的执行器"。

✅ 游标能做什么?

  1. 执行 SQL 语句

    复制代码
    cursor.execute("SELECT VERSION()")
    cursor.execute("CREATE DATABASE spiders DEFAULT CHARACTER SET utf8mb4")
    cursor.execute("INSERT INTO users (name) VALUES ('Alice')")
  2. 获取查询结果

    • fetchone():取一行

    • fetchall():取所有行

    • fetchmany(n):取 n 行

      cursor.execute("SELECT * FROM users")
      row = cursor.fetchone()
      print(row)


上述代码显示了数据库的版本号5.5.53

同时,也创建了spiders数据库


二、创建表

复制代码
import pymysql
db = pymysql.connect(host='localhost',user='root',password='root',port=3306,db='spiders')
cursor = db.cursor()

sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(255) NOT NULL,name VARCHAR(255) NOT NULL,age INT NOT NULL,PRIMARY KEY (id))'

cursor.execute(sql)

db.close()

运行结果:

这个错误的意思是:你试图将一个长度超过 MySQL 允许最大索引长度(1000 字节)的字段设为主键(或索引)

原因分析

在创建表时使用了:

复制代码
id VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
  • VARCHAR(255) 表示最多 255 个字符。
  • 如果你的数据库字符集是 utf8mb4 (这是现代 MySQL 的默认字符集,支持 emoji 等四字节字符),那么每个字符最多占 4 字节
  • 所以 VARCHAR(255) 最多占用:255 × 4 = 1020 字节
  • MySQL 的 MyISAM 或旧版本 InnoDB 引擎对索引长度限制为 1000 字节 (新版本 InnoDB 在启用 innodb_large_prefix 且行格式为 DYNAMICCOMPRESSED 时可支持到 3072 字节,但仍有条件限制)。

因此,主键 id VARCHAR(255) 超过了 1000 字节限制,导致建表失败。


解决方案

✅ 推荐做法:使用更短的 id 长度(最简单有效)

如果你的 id 实际上不需要 255 位(比如是学号、UUID 等),可以缩短长度:

  • 如果是数字 ID:用 INTBIGINT
  • 如果是 UUID(36字符):VARCHAR(36)
  • 如果是自定义字符串 ID,评估最大长度,比如 VARCHAR(64)VARCHAR(100)

例如:

复制代码
sql = '''
CREATE TABLE IF NOT EXISTS students (
    id VARCHAR(64) NOT NULL,
    name VARCHAR(255) NOT NULL,
    age INT NOT NULL,
    PRIMARY KEY (id)
)
'''

64 × 4 = 256 字节,远低于 1000 字节限制,安全。

修改完善代码如下:

复制代码
import pymysql
db = pymysql.connect(host='localhost',user='root',password='root',port=3306,db='spiders')
cursor = db.cursor()

sql = 'CREATE TABLE IF NOT EXISTS students (id VARCHAR(64) NOT NULL,name VARCHAR(255) NOT NULL,age INT NOT NULL,PRIMARY KEY (id))'

cursor.execute(sql)

db.close()

再次运行结果如下:

可以看到表创建成功了!


三、插入数据

复制代码
import pymysql

id = '20251225'
user = 'Chen'
age = 41

db = pymysql.connect(host='localhost',user='root',password='root',port=3306,db='spiders')
cursor = db.cursor()

sql = 'INSERT INTO students(id,name,age) values(%s,%s,%s)'
try:
    cursor.execute(sql,(id,user,age))
    db.commit()
except:
    db.rollback()

db.close()

关键知识点:

1、原书中的%ss是错误的,在代码测试中输入%ss虽然没有报错,但是数据库始终无法写入数据;

应该将三个%ss全部更改为%s才可以,%s才是占位符。

2、commit和rollback

commit() 的作用

复制代码
db.commit()
  • 将当前事务中所有未保存的更改永久写入数据库
  • 一旦执行 commit(),这些更改无法撤销(除非你有备份或日志)。
  • 如果不调用 commit(),即使 cursor.execute() 成功执行了 INSERT/UPDATE/DELETE,数据也不会真正保存(因为默认启用了事务)。

使用场景

当你的 SQL 操作(如插入、更新)执行成功,且业务逻辑确认无误时,调用 commit() 提交。

rollback() 的作用

复制代码
db.rollback()
  • 撤销当前事务中所有未提交的更改,恢复到事务开始前的状态。
  • 常用于异常处理:当某一步操作失败,避免留下"半成品"数据。

使用场景

try...except 中,当捕获到数据库错误(如主键冲突、连接中断、语法错误等),调用 rollback() 回滚,保证数据一致性。

方法 作用 何时调用
commit() 永久保存更改 所有操作成功后
rollback() 撤销未提交的更改 发生错误时

黄金法则

任何修改数据库的操作,都应放在 try...except 中,并配合 commit() / rollback() 使用。

这样能确保你的程序 健壮、安全、数据一致

四、插入数据(升级版)

复制代码
import pymysql

db = pymysql.connect(host='localhost',user='root',password='root',port=3306,db='spiders')
cursor = db.cursor()

data = {
    'id':'20251226',
    'name':'Bob',
    'age':42
}
table = 'students'
keys = ','.join(data.keys())
values = ','.join(['%s']*len(data))
sql = 'INSERT INTO {table}({keys}) VALUES ({values})'.format(table = table,keys = keys,values = values)
try:
    if cursor.execute(sql,tuple(data.values())):
        print('Successful')
        db.commit()
except:
    print('Failed')
    db.rollback()
db.close()

关键知识点:

1、分析values = ','.join(['%s']*len(data))

生成一个由 %s 组成的、用逗号分隔的字符串,数量等于字典 data 中键值对的个数。

例如:

复制代码
data = {'id': '20251226', 'name': 'Bob', 'age': 42}
len(data)  # → 3
['%s'] * 3  # → ['%s', '%s', '%s']
','.join(['%s', '%s', '%s'])  # → '%s,%s,%s'

所以最终 values = '%s,%s,%s'

2、分析.format()

.format() 是 Python 中用于字符串格式化 的一种强大且灵活的方法,广泛用于将变量插入到字符串模板中。它比早期的 % 格式化更清晰、功能更丰富。


✅ 基本语法

复制代码
"模板字符串".format(参数)

模板中使用 {} 作为占位符,.format() 中的参数按顺序或按名称填入。


🔹 2.1. 位置参数(按顺序)

复制代码
name = "Alice"
age = 30
s = "Hello, {}! You are {} years old.".format(name, age)
print(s)  # 输出: Hello, Alice! You are 30 years old.
  • 第一个 {}name
  • 第二个 {}age

注意:从左到右依次匹配。


🔹 2.2. 索引参数(指定位置)

复制代码
s = "I have {1} apples and {0} oranges.".format(5, 3)
print(s)  # 输出: I have 3 apples and 5 oranges.
  • {0} 表示 .format() 的第 0 个参数(5)
  • {1} 表示第 1 个参数(3)

✅ 可重复使用:

复制代码
s = "{0} loves {0}, but {1} hates {0}.".format("Python", "Java")
# 输出: Python loves Python, but Java hates Python.

🔹 2.3. 关键字参数(推荐,可读性高)

复制代码
s = "My name is {name}, and I'm {age} years old.".format(name="Bob", age=25)
print(s)  # 输出: My name is Bob, and I'm 25 years old.

也可以混合使用字典:

复制代码
data = {'name': 'Charlie', 'age': 40}
s = "Name: {name}, Age: {age}".format(**data)
print(s)  # 输出: Name: Charlie, Age: 40

**data 将字典解包为关键字参数。


🔹 2.4. 格式化数字、对齐、精度等

.format() 支持丰富的格式控制,用冒号 : 指定格式:

📌 数字格式化
复制代码
pi = 3.1415926
print("{:.2f}".format(pi))      # 3.14(保留两位小数)
print("{:0>5}".format(42))      # 00042(右对齐,用0填充到5位)
print("{:,}".format(1234567))   # 1,234,567(千位分隔符)
print("{:.2%}".format(0.85))    # 85.00%(百分比)
📌 对齐方式
复制代码
print("{:<10}".format("left"))   # left      (左对齐,总宽10)
print("{:>10}".format("right"))  #      right(右对齐)
print("{:^10}".format("center")) #   center  (居中)

3、tuple(data.values())

将字典 data 中的所有值(values) 转换为一个元组(tuple)

复制代码
data = {'id': '20251226', 'name': 'Bob', 'age': 42}
result = tuple(data.values())
print(result)  # 输出: ('20251226', 'Bob', 42)

五、数据更新

复制代码
import pymysql

db = pymysql.connect(host='localhost',user='root',password='root',port=3306,db='spiders')
cursor = db.cursor()

data = {
    'id':'20251226',
    'name':'Bob',
    'age':22
}
table = 'students'
keys = ','.join(data.keys())
values = ','.join(['%s']*len(data))

sql = 'INSERT INTO {table}({keys}) VALUES ({values}) ON DUPLICATE KEY UPDATE '.format(table = table,keys = keys,values = values)
update = ','.join([f"{key} = %s".format(key=key) for key in data])
sql += update
try:
    if cursor.execute(sql,tuple(data.values())*2):
        print('Successful')
        db.commit()
except:
    print('Failed')
    db.rollback()
db.close()

关键知识点:

1、INSERT ... ON DUPLICATE KEY UPDATE 是 MySQL 中非常实用的语句

用于在执行插入操作时,如果遇到唯一键(主键或唯一索引)冲突,则转而执行更新操作,而非直接抛出错误。这一特性避免了先查询再判断后插入 / 更新的繁琐逻辑,提升了操作的原子性和效率。

基本语法如下:

复制代码
INSERT INTO 表名 (列1, 列2, ...)
VALUES (值1, 值2, ...)
[ON DUPLICATE KEY UPDATE 列1 = 值1, 列2 = 列2 + 值2, ...];
  • 若插入的记录不存在唯一键冲突,执行普通插入
  • 若存在唯一键冲突,执行 UPDATE 子句中指定的更新操作。

2、update = ','.join(["{key} = %s".format(key=key) for key in data])

这段的意图是生成形如 "id = %s,name = %s,age = %s" 的字符串,用于 ON DUPLICATE KEY UPDATE 子句。

这行代码分为两个核心部分:列表推导式字符串拼接

2.1. 列表推导式:["{key} = %s".format(key=key) for key in data]
  • 遍历data的键for key in data会依次取出data中的每个字段名(idnameage)。
  • 格式化字符串"{key} = %s".format(key=key)会将{key}替换为当前遍历的字段名,生成形如"id = %s""name = %s""age = %s"的字符串。
  • 生成列表 :最终得到列表['id = %s', 'name = %s', 'age = %s']
2.2. 字符串拼接:','.join(...)
  • ','.join(列表)会将列表中的每个字符串用逗号连接,最终生成字符串:"id = %s,name = %s,age = %s"
  • 这个字符串会被拼接到ON DUPLICATE KEY UPDATE之后,成为 SQL 的更新子句。

最终拼接出的完整 SQL 如下(以students表为例):

复制代码
INSERT INTO students(id,name,age) VALUES (%s,%s,%s) ON DUPLICATE KEY UPDATE id = %s,name = %s,age = %s
  • 当插入的数据主键 / 唯一键已存在时,MySQL 会执行UPDATE后的逻辑,将对应字段更新为新值。
  • 这里的%s是占位符,后续会通过cursor.execute(sql, tuple(data.values())*2)传入插入值更新值因为插入和更新各需要一次值,所以乘以 2)。
相关推荐
小宇的天下2 小时前
Calibre DESIGNrev DRC/LVS启动和准备文件(10-1)
服务器·数据库·oracle
中科院提名者2 小时前
KNN实战进阶:模型评估、Scikit-learn实现与Numpy手动编码
python·numpy·scikit-learn
半壶清水2 小时前
ubuntu中mysql升级指南
mysql·ubuntu·adb
2401_841495642 小时前
【LeetCode刷题】杨辉三角
数据结构·python·算法·leetcode·杨辉三角·时间复杂度·空间复杂度
我爱娃哈哈2 小时前
告别Redis瓶颈:Caffeine本地缓存优化实战指南
数据库·redis·缓存
虾说羊2 小时前
transferManager为什么在工作中禁止使用 (怎么进行优化 怎么避免多线程的堵塞)
java·服务器·数据库
G_H_S_3_2 小时前
【网络运维】MySQL组成与常用工具
运维·网络·mysql
机灵猫2 小时前
Redis 内部机制:持久化、内存淘汰与延迟优化
数据库·redis·缓存
小成很成2 小时前
sql 开发基础版(命令)
数据库·mysql