MySQL 官方驱动模块
mysql connector 是mysql官方的驱动模块,兼容性特别好
下载地址:https://dev.mysql.com/downloads/connector/python/
mysql> show databases; +--------------------+ | Database | +--------------------+ | gpc | | information_schema | | mysql | | performance_schema | | sys | | test | +--------------------+ 6 rows in set (0.00 sec) mysql> use test; Database changed mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | gongzi | +----------------+ 1 row in set (0.00 sec) mysql> select * from gongzi; +------+-----------+------+-------------+-------+-------+ | id | name | sex | tel | years | wages | +------+-----------+------+-------------+-------+-------+ | 1 | zhangsan | man | 18398989898 | 1 | 1000 | | 2 | lisi | man | 18398555555 | 2 | 3000 | | 3 | wangwu | man | 18398454542 | 3 | 2000 | | 4 | mazi | man | 18398989898 | 4 | 4000 | | 5 | xiao_ming | man | 18398989898 | 5 | 5000 | +------+-----------+------+-------------+-------+-------+ 5 rows in set (0.00 sec) mysql>使用python进行交互
# -*- coding: utf-8 -*-
import mysql.connector
con =mysql.connector.connect(
host="localhost",port = "3306",
user = "root",password = "cheng.123456789",
database = "test"
)
cursor=con.cursor() # 创建游标:用来执行sql语句,而且查询的结果集也会保存在游标之中
sql="select * from gongzi;" #定义我们的sql语句
cursor.execute(sql) # 执行
for one in cursor:
print(one)
con.close() # 关闭链接
sql注入与防御
import mysql.connector
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.12345",
"database":"gpc"
}#创建一个mysql连接信息
con=mysql.connector.connect(**config) # 创建一个链接
username="1 OR 1=1" #uername=1成立不成立无所谓,or(或者)关键是1肯定等于1为真
password="1 OR 1=1" # or后面跟上永远成立的条件
sql="SELECt COUNT(*) FROM t_user WHERe username="+username+
" AND AES_DECRYPT(UNHEx(password),'HelloWorld')="+password;
cursor=con.cursor()
cursor.execute(sql)
print(cursor.fetchone()[0])
con.close()
sql注入攻击的危害:
- 由于sql语句是解释型语言,所以在拼接sql语句的时候,容易被注入恶意的sql语句
- id = “1 or 1=1”
- sql = delete from t_news where id=1 or 1=1;
mysql> select * from t_student; +------+------+------+------+-------+ | no | name | sex | age | email | +------+------+------+------+-------+ | 1 | 2 | 2 | 2 | 2 | +------+------+------+------+-------+ 1 row in set (0.00 sec) mysql> select * from t_news; +----+----------+-----------+---------+------------+--------+---------------------+---------------------+-------+ | id | title | editor_id | type_id | content_id | is_top | create_time | update_time | state | +----+----------+-----------+---------+------------+--------+---------------------+---------------------+-------+ | 1 | xiaoming | 121 | 123 | 2112 | 123 | 2022-05-10 21:14:38 | 2022-05-18 21:14:42 | 鑽夌? | +----+----------+-----------+---------+------------+--------+---------------------+---------------------+-------+ 1 row in set (0.00 sec) mysql> delete from t_news where id=1 or 1=1; Query OK, 1 row affected (0.06 sec) mysql> delete from t_student where no=1 or 1=1; Query OK, 1 row affected (0.04 sec) mysql> select * from t_news; Empty set (0.00 sec) mysql> select * from t_student; Empty set (0.00 sec) mysql>
防御
- sql 预编译机制
- 预编译sql就是数据库提前把sql语句编译成二进制,这样反复执行同一条sql语句的效率就会提升
- 使用%s作为占位符代替具体的数据,传给数据库编译成二进制
- sql语句编译的过程中,关键字已经被解析过了,所以向编译后的sql语句传入参数,都被当作字符串处理,数据库不会解析其中注入的sql语句
import mysql.connector
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.12345",
"database":"gpc"
}
con=mysql.connector.connect(**config)
username="1 OR 1=1"
password="1 OR 1=1"
sql="SELECt COUNT(*) FROM t_user WHERe username=%s "
"AND AES_DECRYPT(UNHEx(password),'HelloWorld')=%s";
cursor=con.cursor()
cursor.execute(sql,(username,password))#先执行sql,然后以参数形式传给已经编译好的sql语句
print(cursor.fetchone()[0])
con.close()
异常处理
con.start_transaction([事物隔离级别])
con.commit() 事物提交
con.rollback() 事物回滚
未写入数据前:
mysql> select * from t_emp; +-------+--------+-----------+------+------------+---------+---------+--------+ | empno | ename | job | mgr | hiredate | sal | comm | deptno | +-------+--------+-----------+------+------------+---------+---------+--------+ | 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 | | 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 | | 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 | | 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 | | 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 | | 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 | | 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 | | 7788 | SCOTT | ANALYST | 7566 | 1982-12-09 | 3000.00 | NULL | 20 | | 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 | | 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 | | 7876 | ADAMS | CLERK | 7788 | 1983-01-12 | 1100.00 | NULL | 20 | | 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 | | 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 | | 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 | +-------+--------+-----------+------+------------+---------+---------+--------+ 14 rows in set (0.00 sec)
写入数据
import mysql.connector
try: # 捕获异常
con=mysql.connector.connect(
host="localhost",
port=3306,
user="root",
password="cheng.12345",
database="gpc"
) # 连接信息
con.start_transaction() # 开启事务
cursor=con.cursor() # 创建游标
sql="INSERT INTO t_emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) "
"VALUES(%s,%s,%s,%s,%s,%s,%s,%s)" # 预编译形式写入数据
cursor.execute(sql,(9600,"赵娜","SALESMAN",None,"1985-12-1",2500,None,10)) # 执行sql语句(传入参数)
con.commit() # 事务提交
except Exception as e: # 处理异常
if "con" in dir(): #如果con不存在就
con.rollback() # 回滚事务
print(e) # 打印异常
finally: # 强制执行
if "con" in dir(): #判断con这个变量是否存在
con.close() # 如果存在就关闭连接
mysql> select * from t_emp; +-------+--------+-----------+------+------------+---------+---------+--------+ | empno | ename | job | mgr | hiredate | sal | comm | deptno | +-------+--------+-----------+------+------------+---------+---------+--------+ | 7369 | SMITH | CLERK | 7902 | 1980-12-17 | 800.00 | NULL | 20 | | 7499 | ALLEN | SALESMAN | 7698 | 1981-02-20 | 1600.00 | 300.00 | 30 | | 7521 | WARD | SALESMAN | 7698 | 1981-02-22 | 1250.00 | 500.00 | 30 | | 7566 | JONES | MANAGER | 7839 | 1981-04-02 | 2975.00 | NULL | 20 | | 7654 | MARTIN | SALESMAN | 7698 | 1981-09-28 | 1250.00 | 1400.00 | 30 | | 7698 | BLAKE | MANAGER | 7839 | 1981-05-01 | 2850.00 | NULL | 30 | | 7782 | CLARK | MANAGER | 7839 | 1981-06-09 | 2450.00 | NULL | 10 | | 7788 | SCOTT | ANALYST | 7566 | 1982-12-09 | 3000.00 | NULL | 20 | | 7839 | KING | PRESIDENT | NULL | 1981-11-17 | 5000.00 | NULL | 10 | | 7844 | TURNER | SALESMAN | 7698 | 1981-09-08 | 1500.00 | 0.00 | 30 | | 7876 | ADAMS | CLERK | 7788 | 1983-01-12 | 1100.00 | NULL | 20 | | 7900 | JAMES | CLERK | 7698 | 1981-12-03 | 950.00 | NULL | 30 | | 7902 | FORD | ANALYST | 7566 | 1981-12-03 | 3000.00 | NULL | 20 | | 7934 | MILLER | CLERK | 7782 | 1982-01-23 | 1300.00 | NULL | 10 | | 9600 | 赵娜 | SALESMAN | NULL | 1985-12-01 | 2500.00 | NULL | 10 | +-------+--------+-----------+------+------------+---------+---------+--------+ 15 rows in set (0.00 sec)数据库连接池
- 数据库连接是一种关键的,有限的,昂贵的资源,在并发执行的应用程序中体现得尤为突出
- tcp连接需要三次握手,四次挥手,然后数据库还要验证用户信息
- 数据库连接池,预先创建出一些数据库连接,然后缓存起来,避免了程序语言反复创建和销毁连接昂贵代价
import mysql.connector.pooling
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.123455",
"database":"gpc"
} # 定义连接信息
try:
pool=mysql.connector.pooling.MySQLConnectionPool(
**config, # 创建连接池,传入连接信息进行连接
pool_size=10 # 连接数量为10
)
con=pool.get_connection() # 取出一个空闲连接
con.start_transaction() # 事务开启
cursor=con.cursor()# 创建游标执行
sql="update t_emp set sal=sal+%s where deptno=%s" #编译sql语句
cursor.execute(sql,(200000,20)) # 执行sql传入参数
con.commit() # 事务提交
except Exception as e:
if "con" in dir():# 如果出现异常就回滚事务
con.rollback()
print(e)
删除数据
import mysql.connector.pooling
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.1333",
"database":"gpc"
} # 定义连接信息
try:
pool=mysql.connector.pooling.MySQLConnectionPool(
**config, # 创建连接池,传入连接信息进行连接
pool_size=10 # 连接数量为10
)
con=pool.get_connection() # 取出一个空闲连接
con.start_transaction() # 事务开启
cursor=con.cursor()# 创建游标执行
sql="delete from t_emp where deptno = 20; " #删除t_emp表中deptno是20的数据
#sql="TRUNCATE TABLE t_emp" #清空t_emp数据
cursor.execute(sql) # 执行sql传入参数
con.commit() # 事务提交
except Exception as e: #如果出现异常
if "con" in dir():# 判断con是否存在,存在就回滚事务
con.rollback() # 回滚
print(e)
清空
import mysql.connector.pooling
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.12333",
"database":"gpc"
} # 定义连接信息
try:
pool=mysql.connector.pooling.MySQLConnectionPool(
**config, # 创建连接池,传入连接信息进行连接
pool_size=10 # 连接数量为10
)
con=pool.get_connection() # 取出一个空闲连接
con.start_transaction() # 事务开启
cursor=con.cursor()# 创建游标执行
sql="TRUNCATE TABLE t_emp;" #清空t_emp数据
cursor.execute(sql) # 执行sql传入参数
con.commit() # 事务提交
except Exception as e: #如果出现异常
if "con" in dir():# 判断con是否存在,存在就回滚事务
con.rollback() # 回滚
print(e)
循环执行sql语句
executemany()
import mysql.connector.pooling
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.1023",
"database":"gpc"
}
try:
pool=mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
)
con=pool.get_connection()
con.start_transaction()
cursor=con.cursor()
sql="INSERT INTO t_dept(deptno,dname,loc) VALUES(%s,%s,%s)"
data=[
[100,"A部门","北京"],[110,"B部门","上海"]
]#在列表中定义多条需要插入的数据
cursor.executemany(sql,data) #executemany循环执行
con.commit()
except Exception as e:
if "con" in dir():
con.rollback()
print(e)
案例
使用insert 语句,把部门平均底薪超过公司平均底薪的这样部门里的员工信息导入到t_emp_new表里面,并且让这些员工隶属于sales部门
一阶段原:
# -*- coding: utf-8 -*-
import mysql.connector.pooling # 导入模块
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng",
"database":"gpc"
} # 链接信息
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
) # 创建链接池
con = pool.get_connection() # 取出一个链接
con.start_transaction() # 开启事务
cursor = con.cursor() # 创建游标
sql = "CREATE TABLE t_emp_new AS (SELECt * FROM t_emp)" # sql语句 (快速建表(复制表)查询t_emp信息并复制)
cursor.execute(sql) # 执行sql语句
con.commit()# 提交事务
except Exception as e:
print(e)
现:
# -*- coding: utf-8 -*-
import mysql.connector.pooling # 导入模块
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng",
"database":"gpc"
} # 链接信息
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
) # 创建链接池
con = pool.get_connection() # 取出一个链接
con.start_transaction() # 开启事务
cursor = con.cursor() # 创建游标
sql = "DROp TABLE IF EXISTS t_emp_new" # 如果t_emp_new存在就删除数据表(为避免该表存在我们先删除然后下面在创建)
cursor.execute(sql)# 执行sql语句
sql = "CREATE TABLE t_emp_new LIKE t_emp" # sql语句 (只复制t_emp表的结构不复制数据)
cursor.execute(sql) # 执行sql语句
# 使用INSERT语句,把部门平均底薪超过公司平均底薪的这样部门里的
# 员工信息导入到t_emp_new表里面,并且让这些员工隶属于sales部门
sql="SELECT AVG(sal) AS avg FROM t_emp" # 查看全公司平均底薪 起个别名avg
cursor.execute(sql)# 执行sql语句
temp = cursor.fetchone() # 因平均值只有一个 定义一个变量取出一条输出并打印
avg = temp[0] # 公司平均底薪
print("公司平均底薪:",temp)
sql="SELECt deptno FROM t_emp GROUP BY deptno HAVINg AVG(sal)>=%s" # 查询每个部门的平均底薪并大于公司平均底薪的部门
cursor.execute(sql,[avg]) # 执行sql语句 传入公司平均底薪参数
for one in cursor:
print(one)# 循环打印结果
con.commit()# 提交事务
except Exception as e:
print(e)
插入
# -*- coding: utf-8 -*-
import mysql.connector.pooling # 导入模块
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng.1023",
"database":"gpc"
} # 链接信息
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
) # 创建链接池
con = pool.get_connection() # 取出一个链接
con.start_transaction() # 开启事务
cursor = con.cursor() # 创建游标
sql = "DROp TABLE IF EXISTS t_emp_new" # 如果t_emp_new存在就删除数据表(为避免该表存在我们先删除然后下面在创建)
cursor.execute(sql)# 执行sql语句
sql = "CREATE TABLE t_emp_new LIKE t_emp" # sql语句 (只复制t_emp表的结构不复制数据)
cursor.execute(sql) # 执行sql语句
# 使用INSERT语句,把部门平均底薪超过公司平均底薪的这样部门里的
# 员工信息导入到t_emp_new表里面,并且让这些员工隶属于sales部门
sql="SELECT AVG(sal) AS avg FROM t_emp" # 查看全公司平均底薪 起个别名avg
cursor.execute(sql)# 执行sql语句
temp = cursor.fetchone() # 因平均值只有一个 # *fetchone() :返回单个的元组,也就是一条记录(row),如果没有结果, 则返回None
avg = temp[0] # 公司平均底薪
print("公司平均底薪:",temp)
sql="SELECt deptno FROM t_emp GROUP BY deptno HAVINg AVG(sal)>=%s" # 查询每个部门的平均底薪并大于公司平均底薪的部门
cursor.execute(sql,[avg]) # 执行sql语句 传入公司平均底薪参数
temp = cursor.fetchall() # *fetchall() :返回多个元组,即返回多条记录(rows), 如果没有结果, 则返回()
print(temp)
sql="INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (" # 把查询结果用在insert语句里面并写入新的表 where判断
for index in range(len(temp)): # sql语句拼接
one = temp[index][0]
if index < len(temp)-1:
sql+=str(one)+","
else:
sql+=str(one)
sql+=")"
print(sql) # INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (20,10)
cursor.execute(sql) # 执行sql语句
con.commit()# 提交事务
except Exception as e:
print(e)
三阶段
# -*- coding: utf-8 -*-
import mysql.connector.pooling # 导入模块
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng",
"database":"gpc"
} # 链接信息
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
) # 创建链接池
con = pool.get_connection() # 取出一个链接
con.start_transaction() # 开启事务
cursor = con.cursor() # 创建游标
sql = "DROp TABLE IF EXISTS t_emp_new" # 如果t_emp_new存在就删除数据表(为避免该表存在我们先删除然后下面在创建)
cursor.execute(sql)# 执行sql语句
sql = "CREATE TABLE t_emp_new LIKE t_emp" # sql语句 (只复制t_emp表的结构不复制数据)
cursor.execute(sql) # 执行sql语句
# 使用INSERT语句,把部门平均底薪超过公司平均底薪的这样部门里的
# 员工信息导入到t_emp_new表里面,并且让这些员工隶属于sales部门
sql="SELECT AVG(sal) AS avg FROM t_emp" # 查看全公司平均底薪 起个别名avg
cursor.execute(sql)# 执行sql语句
temp = cursor.fetchone() # 因平均值只有一个 # *fetchone() :返回单个的元组,也就是一条记录(row),如果没有结果, 则返回None
avg = temp[0] # 公司平均底薪
print("公司平均底薪:",temp)
sql="SELECt deptno FROM t_emp GROUP BY deptno HAVINg AVG(sal)>=%s" # 查询每个部门的平均底薪并大于公司平均底薪的部门
cursor.execute(sql,[avg]) # 执行sql语句 传入公司平均底薪参数
temp = cursor.fetchall() # *fetchall() :返回多个元组,即返回多条记录(rows), 如果没有结果, 则返回()
print(temp)
sql="INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (" # 把查询结果用在insert语句里面并写入新的表 where判断
for index in range(len(temp)): # sql语句拼接
one = temp[index][0]
if index < len(temp)-1:
sql+=str(one)+","
else:
sql+=str(one)
sql+=")"
print(sql) # INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (20,10)
cursor.execute(sql) # 执行sql语句
sql="DELETe FROM t_emp WHERe deptno IN (" # 删除t_emp 里面的 大于公司平均底薪的人
for index in range(len(temp)):
one=temp[index][0]
if index < len(temp)-1:
sql+=str(one)+","
else:
sql+=str(one)
sql+=")"
print(sql)
cursor.execute(sql)
con.commit()# 提交事务
except Exception as e:
if "con" in dir():
con.rollback() # 事务回滚
print(e)
# -*- coding: utf-8 -*-
import mysql.connector.pooling # 导入模块
config={
"host":"localhost",
"port":3306,
"user":"root",
"password":"cheng",
"database":"gpc"
} # 链接信息
try:
pool = mysql.connector.pooling.MySQLConnectionPool(
**config,
pool_size=10
) # 创建链接池
con = pool.get_connection() # 取出一个链接
con.start_transaction() # 开启事务
cursor = con.cursor() # 创建游标
sql = "DROp TABLE IF EXISTS t_emp_new" # 如果t_emp_new存在就删除数据表(为避免该表存在我们先删除然后下面在创建)
cursor.execute(sql)# 执行sql语句
sql = "CREATE TABLE t_emp_new LIKE t_emp" # sql语句 (只复制t_emp表的结构不复制数据)
cursor.execute(sql) # 执行sql语句
# 使用INSERT语句,把部门平均底薪超过公司平均底薪的这样部门里的
# 员工信息导入到t_emp_new表里面,并且让这些员工隶属于sales部门
sql="SELECT AVG(sal) AS avg FROM t_emp" # 查看全公司平均底薪 起个别名avg
cursor.execute(sql)# 执行sql语句
temp = cursor.fetchone() # 因平均值只有一个 # *fetchone() :返回单个的元组,也就是一条记录(row),如果没有结果, 则返回None
avg = temp[0] # 公司平均底薪
print("公司平均底薪:",temp)
sql="SELECt deptno FROM t_emp GROUP BY deptno HAVINg AVG(sal)>=%s" # 查询每个部门的平均底薪并大于公司平均底薪的部门
cursor.execute(sql,[avg]) # 执行sql语句 传入公司平均底薪参数
temp = cursor.fetchall() # *fetchall() :返回多个元组,即返回多条记录(rows), 如果没有结果, 则返回()
print(temp)
sql="INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (" # 把查询结果用在insert语句里面并写入新的表 where判断
for index in range(len(temp)): # sql语句拼接
one = temp[index][0]
if index < len(temp)-1:
sql+=str(one)+","
else:
sql+=str(one)
sql+=")"
print(sql) # INSERT INTO t_emp_new SELECt * FROM t_emp WHERe deptno IN (20,10)
cursor.execute(sql) # 执行sql语句
sql="DELETe FROM t_emp WHERe deptno IN (" # 删除t_emp 里面的 大于公司平均底薪的人
for index in range(len(temp)):
one=temp[index][0]
if index < len(temp)-1:
sql+=str(one)+","
else:
sql+=str(one)
sql+=")"
print(sql)
cursor.execute(sql)
sql="SELECt deptno FROM t_dept WHERe dname=%s"
cursor.execute(sql,["SALES"])
deptno=cursor.fetchone()[0]
sql="UPDATE t_emp_new SET deptno=%s" # 修改新表的部门为SALES=30
cursor.execute(sql,[deptno])
con.commit()# 提交事务
except Exception as e:
if "con" in dir():
con.rollback() # 事务回滚
print(e)



