首页 > 数据库 >python--mysql--驱动简介和使用

python--mysql--驱动简介和使用

时间:2023-08-08 23:35:54浏览次数:47  
标签:cur -- sql db pymysql cursor python mysql con


本篇文章介绍 Python 操作 MySQL 的几种方式,你可以在实际开发过程中根据实际情况合理选择。

MySQL-python(MySQLdb)–支持python2.x

MySQL-python 又叫 MySQLdb,是 Python 连接 MySQL 最流行的一个驱动,很多框架都也是基于此库进行开发,遗憾的是它只支持 Python2.x,而且安装的时候有很多前置条件,因为它是基于C开发的库,在 Windows 平台安装非常不友好,经常出现失败的情况,现在基本不推荐使用,取代的是它的衍生版本。

Python-MySQL资格最老,核心由C语言打造,接口精炼,性能最棒,缺点是环境依赖较多,安装复杂,近两年已停止更新,只支持Python2,不支持Python3。

前置条件

sudo apt-get install python-dev libmysqlclient-dev # Ubuntu
sudo yum install python-devel mysql-devel # Red Hat / CentOS

安装

pip install MySQL-python

Windows 直接通过下载 exe 文件安装

使用

#!/usr/bin/python
import MySQLdb

db = MySQLdb.connect(
     host="localhost",    # 主机名
     user="john",         # 用户名
     passwd="megajonhy",  # 密码
     db="jonhydb")        # 数据库名称

# 查询前,必须先获取游标
cur = db.cursor()

# 执行的都是原生SQL语句
cur.execute("SELECT * FROM YOUR_TABLE_NAME")

for row in cur.fetchall():
    print(row[0])

db.close()

mysqlclient–支持python2.x,Python3.x

由于 MySQL-python 年久失修,后来出现了它的 Fork 版本 mysqlclient,完全兼容 MySQLdb,同时支持 Python3.x,是 Django ORM的依赖工具,如果你想使用原生 SQL 来操作数据库,那么推荐此驱动。安装方式和 MySQLdb 是一样的,Windows 可以在 https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient 网站找到 对应版本的 whl 包下载安装。

Windows安装

pip install some-package.whl

linux 前置条件

sudo apt-get install python3-dev # debian / Ubuntu
sudo yum install python3-devel # Red Hat / CentOS
brew install mysql-connector-c # macOS (Homebrew)

pip install mysqlclient

PyMySQL–支持python2.x,Python3.x

PyMySQL 是纯 Python 实现的驱动,速度上比不上 MySQLdb,最大的特点可能就是它的安装方式没那么繁琐,同时也兼容Python-MySQL(MySQLdb)

PyMySQL为替代Python-MySQL(MySQLdb)而生,纯python打造,接口与Python-MySQL兼容,安装方便,支持Python3。

pip install PyMySQL

PyMySQL兼容mysqldb

只需要加入

pymysql.install_as_MySQLdb()

使用

import pymysql
conn = pymysql.connect(host='127.0.0.1', user='root', passwd="xxx", db='mysql')
cur = conn.cursor()
cur.execute("SELECT Host,User FROM user")
for r in cur:
    print(r)
cur.close()
conn.close()

peewee–支持python2.x,Python3.x

写原生 SQL 的过程非常繁琐,代码重复,没有面向对象思维,继而诞生了很多封装 wrapper 包和 ORM 框架,ORM 是 Python 对象与数据库关系表的一种映射关系,有了 ORM 你不再需要写 SQL 语句。提高了写代码的速度,同时兼容多种数据库系统,如sqlite, mysql、postgresql,付出的代价可能就是性能上的一些损失。如果你对 Django 自带的 ORM 熟悉的话,那么 peewee的学习成本几乎为零。它是 Python 中是最流行的 ORM 框架。

pip install peewee

使用

import peewee
from peewee import *

db = MySQLDatabase('jonhydb', user='john', passwd='megajonhy')

class Book(peewee.Model):
    author = peewee.CharField()
    title = peewee.TextField()

    class Meta:
        database = db

Book.create_table()
book = Book(author="me", title='Peewee is cool')
book.save()
for book in Book.filter(author="me"):
    print(book.title)

官方文档:http://docs.peewee-orm.com/en/latest/peewee/installation.html

SQLAlchemy–支持python2.x,Python3.x

SQLAlchemy是一个ORM框架,它并不提供底层的数据库操作,而是要借助于MySQLdb、PyMySQL等第三方库来完成,目前SQLAlchemy在Web编程领域应用广泛。

如果想找一种既支持原生 SQL,又支持 ORM 的工具,那么 SQLAlchemy 是最好的选择,它非常接近 Java 中的 Hibernate 框架。

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

from sqlalchemy_declarative import Address, Base, Person

class Address(Base):
    __tablename__ = 'address'
    id = Column(Integer, primary_key=True)
    street_name = Column(String(250))

engine = create_engine('sqlite:///sqlalchemy_example.db')
Base.metadata.bind = engine

DBSession = sessionmaker(bind=engine)
session = DBSession()

# Insert a Person in the person table
new_person = Person(name='new person')
session.add(new_person)
session.commit()

更多PyMySQL用法

安装

简单的方式:

pip install pymysql

如果无法联网,需要进行离线安装,例如:

pip install pymysql-x.x.x.tar.gz

导入

import pymysql

连接

def connect_wxremit_db():
    return pymysql.connect(host='10.123.5.28',
                           port=3306,
                           user='root',
                           password='root1234',
                           database='db_name',
                           charset='latin1')

参数列表:

参数

描述

host

数据库服务器地址,默认 localhost

user

用户名,默认为当前程序运行用户

password

登录密码,默认为空字符串

database

默认操作的数据库

port

数据库端口,默认为 3306

bind_address

当客户端有多个网络接口时,指定连接到主机的接口。参数可以是主机名或IP地址。

unix_socket

unix 套接字地址,区别于 host 连接

read_timeout

读取数据超时时间,单位秒,默认无限制

write_timeout

写入数据超时时间,单位秒,默认无限制

charset

数据库编码

sql_mode

指定默认的 SQL_MODE

read_default_file

Specifies my.cnf file to read these parameters from under the [client] section.

conv

Conversion dictionary to use instead of the default one. This is used to provide custom marshalling and unmarshaling of types.

use_unicode

Whether or not to default to unicode strings. This option defaults to true for Py3k.

client_flag

Custom flags to send to MySQL. Find potential values in constants.CLIENT.

cursorclass

设置默认的游标类型

init_command

当连接建立完成之后执行的初始化 SQL 语句

connect_timeout

连接超时时间,默认 10,最小 1,最大 31536000

ssl

A dict of arguments similar to mysql_ssl_set()’s parameters. For now the capath and cipher arguments are not supported.

read_default_group

Group to read from in the configuration file.

compress

Not supported

named_pipe

Not supported

autocommit

是否自动提交,默认不自动提交,参数值为 None 表示以服务器为准

local_infile

Boolean to enable the use of LOAD DATA LOCAL command. (default: False)

max_allowed_packet

发送给服务器的最大数据量,默认为 16MB

defer_connect

是否惰性连接,默认为立即连接

auth_plugin_map

A dict of plugin names to a class that processes that plugin. The class will take the Connection object as the argument to the constructor. The class needs an authenticate method taking an authentication packet as an argument. For the dialog plugin, a prompt(echo, prompt) method can be used (if no authenticate method) for returning a string from the user. (experimental)

server_public_key

SHA256 authenticaiton plugin public key value. (default: None)

db

参数 database 的别名

passwd

参数 password 的别名

binary_prefix

Add _binary prefix on bytes and bytearray. (default: False)

创建表格

# 获取游标
cursor = connection.cursor()
    
# 创建数据表
effect_row = cursor.execute('''
CREATE TABLE `users` (
  `name` varchar(32) NOT NULL,
  `age` int(10) unsigned NOT NULL DEFAULT '0',
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
''')

查询

def query_country_name(cc2):
    sql_str = ("SELECT Fcountry_name_zh"
                + " FROM t_country_code"
                + " WHERE Fcountry_2code='%s'" % (cc2))
    logging.info(sql_str)

    con = mysql_api.connect_wxremit_db()
    cur = con.cursor()
    cur.execute(sql_str)
    rows = cur.fetchall()
	# 获取单条数据
    row = cur.fetchone()
	# 获取前N条数据
    rowmany =  cur.fetchmany(3)
    cur.close()
    con.close()

    assert len(rows) == 1, 'Fatal error: country_code does not exists!'
    return rows[0][0]

查询遍历

import pymysql  #导入 pymysql
 
#打开数据库连接
db= pymysql.connect(host="localhost",user="root",
 	password="123456",db="test",port=3307)
 
# 使用cursor()方法获取操作游标
cur = db.cursor()
 
#1.查询操作
# 编写sql 查询语句  user 对应我的表名
sql = "select * from user"
try:
	cur.execute(sql) 	#执行sql语句
 
	results = cur.fetchall()	#获取查询的所有记录
	print("id","name","password")
	#遍历结果
	for row in results :
		id = row[0]
		name = row[1]
		password = row[2]
		print(id,name,password)
except Exception as e:
	raise e
finally:
	db.close()	#关闭连接

超过千万行的数据遍历

当使用sql查询的结果有非常多行时,如果使用默认的cursor,你的程序在接受数据的的时候很可能卡住或者被杀死,原因是mysql客户端(Java,Pyhton)默认在内存里缓存下所有行然后再处理,如果内存溢出后,你的程序就会被杀死。

解决方式是实用流式游标,在Python中,你可以使用pymysql.cursors.SSCursor(或者SSDictCursor)来解决这个问题

import pymysql
conn = pymysql.connect(...)
cursor = pymysql.cursors.SSCursor(conn)
cursor.execute(...)
while True:
    row = cursor.fetchone()
    if not row:
        break
    ...

这里有两点需要注意下:

使用pymysql.cursors.SSCursor代替默认的cursor。可以使用以上代码,或者这样写:conn.cursor(pymysql.cursors.SSCursor)
使用fetchone去每次只获得一行,别使用fetchall。也可以使用fetchmay,但是这样其实是多次调用fetchone。
对于SSCursor有一个错误的理解,就是SSCursor是服务端一次性读出所有数据然后一条一条返给客户端,其实不是这样的,这个cursor实际上没有缓存下来任何数据,它不会读取所有所有到内存中,它的做法是从储存块中读取记录,并且一条一条返回给你。这里有一个更适合的名字:流式游标。

因为SSCursor是没有缓存的游标,这里有几条约束:

这个connection只能读完所有行之后才能处理其他sql。如果你需要并行执行sql,在另外一个connection中执行,否则你会遇到 error 2014 , “Commands out of sync; you can’t run this command now.”
必须一次性读完所有行,每次读取后处理数据要快,不能超过60s,否则mysql将会断开这次连接( error2013 , “Lost connection to MySQL server during query),也可以修改 SET NET_WRITE_TIMEOUT = xx 来增加超时间隔。
参考:Techualization: Retrieving million of rows from MySQL(原文更加详细)

查询数量

def count_t_quality_inspection_by_chip_barcode(chip, barcode):
    sql_str = ("SELECT * "
               + " FROM t_quality"
               + " WHERE chip='%s' and barcode='%s'" % (chip, barcode))
    con = connect_quality_db()
    cur = con.cursor()
    cur.execute(sql_str)
    rows = cur.rowcount
    logger.info("%s %s 在质检表中数量为 %i" % (chip, barcode,rows) )
    cur.close()
    con.close()
    return rows

游标控制

所有的数据查询操作均基于游标,我们可以通过cursor.scroll(num, mode)控制游标的位置。

cursor.scroll(1, mode='relative') # 相对当前位置移动
cursor.scroll(2, mode='absolute') # 相对绝对位置移动

设置游标类型

查询时,默认返回的数据类型为元组,可以自定义设置返回类型。支持5种游标类型:

Cursor: 默认,元组类型
DictCursor: 字典类型
DictCursorMixin: 支持自定义的游标类型,需先自定义才可使用
SSCursor: 无缓冲元组类型
SSDictCursor: 无缓冲字典类型
无缓冲游标类型,适用于数据量很大,一次性返回太慢,或者服务端带宽较小时。源码注释:

Unbuffered Cursor, mainly useful for queries that return a lot of data, or for connections to remote servers over a slow network.
Instead of copying every row of data into a buffer, this will fetch rows as needed. The upside of this is the client uses much less memory, and rows are returned much faster when traveling over a slow network or if the result set is very big.
There are limitations, though. The MySQL protocol doesn’t support returning the total number of rows, so the only way to tell how many rows there are is to iterate over every row returned. Also, it currently isn’t possible to scroll backwards, as only the current row is held in memory.

创建连接时,通过 cursorclass 参数指定类型:

connection = pymysql.connect(host='localhost',
                             user='root',
                             password='root',
                             db='demo',
                             charset='utf8',
                             cursorclass=pymysql.cursors.DictCursor)

也可以在创建游标时指定类型:

cursor = connection.cursor(cursor=pymysql.cursors.DictCursor)

注意:INSERT、UPDATE、DELETE 等修改数据的语句需手动执行connection.commit()完成对数据修改的提交。

简单插入

def insert_file_rec(self, file_name, file_md5):
        con = mysql_api.connect_wxremit_db()
        cur = con.cursor()
        try:
            sql_str = ("INSERT INTO t_forward_file (Ffile_name, Ffile_md5)", 
                       + " VALUES ('%s', '%s')" % (file_name, file_md5))
            cur.execute(sql_str)
            con.commit()
        except:
            con.rollback()
            logging.exception('Insert operation error')
            raise
        finally:
            cur.close()
            con.close()

批量插入

#new_items = [('1234', 'CAD'), ('5678', 'HKD')]

new_items=[]
for i in range(10000):
    new_item=(str(i),'zhangxiaofan')
	new_items.append(new_item)

con = mysql_api.connect_wxremit_db()
        cur = con.cursor()
        try:
                cur.executemany("INSERT INTO t_order (Fremit_id, Fcur_type, Fcreate_time"
                                                + " VALUES (%s, %s, now())", new_items)
                assert cur.rowcount == len(new_items), 'my error message'
                con.commit()
        except Exception as e:
                con.rollback()
                logging.exception('Insert operation error')
        finally:
                cur.close()
                con.close()

executemany与on duplicate key update

#new_items = [('1234', 'CAD'), ('5678', 'HKD')]

new_items=[]
for i in range(10000):
    new_item=(str(i),'zhangxiaofan')
	new_items.append(new_item)

con = mysql_api.connect_wxremit_db()
        cur = con.cursor()
        try:
                cur.executemany("INSERT INTO t_order (Fremit_id, Fcur_type, Fcreate_time"
                                                + " VALUES (%s, %s, now())    on duplicate key update Fcreate_time = values(Fcreate_time)      ", new_items)
                assert cur.rowcount == len(new_items), 'my error message'
                con.commit()
        except Exception as e:
                con.rollback()
                logging.exception('Insert operation error')
        finally:
                cur.close()
                con.close()

更新

def update_refund_trans(self, remit_id):
        con = mysql_api.connect_wxremit_db()
        cur = con.cursor()
        try:
            sql_str = ("SELECT Fremit_id"
                       + " FROM t_wxrefund_trans"
                       + " WHERE Fremit_id='%s'" % remit_id
                       + " FOR UPDATE")
            logging.info(sql_str)

            cur.execute(sql_str)
            assert cur.rowcount == 1, 'Fatal error: The wx-refund record be deleted!'

            sql_str = ("UPDATE t_wxrefund_trans"
                        + " SET Fcheck_amount_flag=1"
                        + ", Fmodify_time=now()"
                        + " WHERE Fremit_id='%s'" % remit_id
            logging.info(sql_str)
            cur.execute(sql_str)

            assert cur.rowcount == 1, 'The number of affected rows not equal to 1'
            con.commit()
        except:
            con.rollback()
            logging.exception('Update operation error')
            raise
        finally:
            cur.close()
            con.close()

事务处理

开启事务 connection.begin()

提交修改 connection.commit()

回滚事务 connection.rollback()

#!/usr/bin/python3
 
import pymysql
 
# 打开数据库连接
connection = pymysql.connect("localhost","testuser","test123","TESTDB" )
 
# 使用cursor()方法获取操作游标 
cursor = connection.cursor()
 
# SQL 插入语句
sql = "INSERT INTO EMPLOYEE(FIRST_NAME, \
       LAST_NAME, AGE, SEX, INCOME) \
       VALUES ('%s', '%s',  %s,  '%s',  %s)" % \
       ('Mac', 'Mohan', 20, 'M', 2000)
try:
   # 执行sql语句
   cursor.execute(sql)
   # 执行sql语句
   connection.commit()
except:
   # 发生错误时回滚
   connection.rollback()
 
# 关闭数据库连接
db.close()

防 SQL 注入

转义特殊字符 connection.escape_string(str)

参数化语句 支持传入参数进行自动转义、格式化 SQL 语句,以避免 SQL 注入等安全问题。

# 插入数据(元组或列表)
effect_row = cursor.execute('INSERT INTO `users` (`name`, `age`) VALUES (%s, %s)', ('mary', 18))
# 插入数据(字典)
info = {'name': 'fake', 'age': 15}
effect_row = cursor.execute('INSERT INTO `users` (`name`, `age`) VALUES (%(name)s, %(age)s)', info)
# 批量插入
effect_row = cursor.executemany(
    'INSERT INTO `users` (`name`, `age`) VALUES (%s, %s) ON DUPLICATE KEY UPDATE age=VALUES(age)', [
        ('hello', 13),
        ('fake', 28),
    ])

参考链接:
https://foofish.net/python-mysql.html
https://shockerli.net/post/python3-pymysql/
http://www.runoob.com/python3/python3-mysql.html


标签:cur,--,sql,db,pymysql,cursor,python,mysql,con
From: https://blog.51cto.com/u_16218512/7013739

相关文章

  • Springboot集成使用阿里云kafka详细步骤
    明确连接认证类型首先要明确使用哪种连接认证类型Ons模式参考https://github.com/AliwareMQ/aliware-kafka-demos/tree/master/kafka-java-demo/betaOns模式的conf内容KafkaClient{com.aliyun.openservices.ons.sasl.client.OnsLoginModulerequiredAccessKey="......
  • python积累--读写文本文件实例
    读写文件是最常见的IO操作。我们经常从文件读取输入,将内容写到文件。读文件在Python中,读文件主要分为三个步骤:打开文件读取内容关闭文件一般使用形式如下:try:f=open('/path/to/file','r')#打开文件data=f.read()#读取文件内容fina......
  • aws----文件存储efs的全面了解
    efs简介AmazonElasticFileSystem可扩展的、弹性原生云NFS文件系统,每月0.08USD/GBAmazonEFS是一项完全托管的服务,让您可以轻松地在Amazon云中设置和扩展文件存储,并对其进行成本优化。只需在AWS管理控制台中单击几次,您就可以创建文件系统,供AmazonEC2实例通过文件系统......
  • 单调栈
    LargestRectangleinaHistogram经典题单调栈:保持栈内元素单调递增因为如果递减后面的元素的高度就会替换前面的元素的高度前面元素等价于多个后面元素当有新元素加入是将原先的递增序列从后往前更新答案最后使得栈保持原有性质这样可以保证每个元素只会被考虑一次,不用......
  • k8s--kubernetes--argo----使用动态存储PVC---基于nfs 的storageclass
    PVC简介Docker中有volumes的概念,在Docker中,volume是对物理节点服务器node路径目录的一种映射,也就是我们可以把服务器的一个目录挂载给镜像使用。同样的,k8s创建的pod也可以挂载volume,而且不仅仅支持pod所在的服务器node的目录映射,也可以挂载其他网络存储的作为目录挂载。k8s支......
  • 红帽认证RedHat-RHCSA shell的基本应用用户和组管理网络配置和防火墙管理笔记汇总
    shell命令概述Shell作用:命令解释器介于操作系统内核与用户之间,负责解释命令行获得命令帮助内部命令help命令的“--help”选项使用man命令阅读手册页命令行编辑的几个辅助操作Tab键:自动补齐反斜杠“\”:强制换行快捷键Ctrl+U:清空至行首快捷键Ctrl+K:清空至行尾快捷键Ctr......
  • Kotlin中的Data Class
    DataClassKotlin中的DataClass主要用于封装和持有数据,作用类似Java中的POJO对象。dataclassUser(nickname:String,sex:Int,age:Int)在class之前加上data关键字即可声明一个DataClass。Kotlin中编译器会自动为DataClass实现equals()方法hashcode()方法toStr......
  • docker--k8s---终端terminal和普通程序以及jupyter notebook-创建永久的静态环境变量-
    终端terminal和普通程序linux系统的中主要有两种启用系统环境的情况,一种是用户登录,比如进入bash或者sh等命令行操作shell界面,一种是用户不登录,而是程序自动运行。根据两种情况,适用的创建永久的环境变量的方式就会有所不同。创建永久的静态环境变量静态环境变量是指内容不会变......
  • hadoop组件---spark实战-----airflow----调度工具airflow定时运行任务的理解
    我们在前面已经初步了解了airflow:hadoop组件—spark实战-----airflow----调度工具airflow的介绍和使用示例但是我们开始尝试使用airflow的定时任务的时候,常常遇到一个尴尬的情况,任务没有成功运行,或者说设置开始时间是今天,但是明天才开始运行。本篇文章尝试说明其中的......
  • k8s 学习笔记之配置存储——ConfigMap&Secret
    配置存储ConfigMapConfigMap是一种比较特殊的存储卷,它的主要作用是用来存储配置信息的。创建configmap.yaml,内容如下:apiVersion:v1kind:ConfigMapmetadata:name:configmapnamespace:devdata:info:|(这个|后面整个都是值)username:adminpassword:12......