介紹
mysqldb是python操作mysql數據庫的一個庫.mysql的幾乎所有的操作都可以實現,另外,mysqldb的一些比較的option讓數據操作更符合pythonic風格.在python2系列使用Mysqldb,在python3系列使用pymysql和mysql.connect.
Mysqldb的安裝
下面按python2系列安裝
1. pip方式安裝
pip install MySQL-python
2. yum安裝
sudo yum install python-mysqldb
3. apt安裝
sudo apt-get install python-mysqldb
4.源碼安裝(這里就不介紹了,源碼地址給出)
https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip#md5=654f75b302db6ed8dc5a898c625e030c
Mysqldb的使用介紹
我們直接操作mysql的流程如下:
1.使用mysql-client連上數據庫
mysql -uuser_anme -ppassword -hhost_name
2.再執行具體的sql語句,獲取所需的數據
>use db_name;
>select * from table_name;
python-mysqldb的使用方式和上面的流程是一樣的.下面進入正題.
1. 連接數據庫
import MySQLdb
conn = MySQLdb.connect(db='database', host='172.16.0.1', user='user', passwd='password', port=3306)
上面只是進行了數據庫的連接
2. 游標對象
cur = conn.cursor()
3. 執行語句
sql = '****'
cur.execute(sql)
介紹一下具體的對象的method:
下面介紹一下method的使用
connect對象
conn = MySQLdb.connect(db='database', host='172.16.0.1', user='user', passwd='password', port=3306)
connect()的參數列表
參數 | 描述 |
---|---|
user | username |
passwd | password |
host | hostname |
database | databasename |
dsn | data source name |
port | 端口,int |
conv | 數據轉換 |
charset | 字符集 |
其他的一些參數:unix_socket,compress,connect_timeout,named_pipe,
init_command,read_default_file,read_default_group,cursorclass,use_unicode,sql_mode,ssl
下面詳細介紹一下conv這個參數
介紹一個類型對象的概念,通常不同的系統的接口要求的參數類型是不一致的,譬如python調用c函數時python對象和c類型之間就需要進行數據格式的轉換.所以,在python對象和原生數據庫對象之間也需要進行數據格式的轉換.
在MySQLdb.converters.conversions中
encoders = {
bool: escape_bool,
int: escape_int,
long_type: escape_int,
float: escape_float,
str: escape_str,
text_type: escape_unicode,
tuple: escape_sequence,
list: escape_sequence,
set: escape_sequence,
dict: escape_dict,
type(None): escape_None,
datetime.date: escape_date,
datetime.datetime: escape_datetime,
datetime.timedelta: escape_timedelta,
datetime.time: escape_time,
time.struct_time: escape_struct_time,
Decimal: str,
}
decoders = {
FIELD_TYPE.BIT: convert_bit,
FIELD_TYPE.TINY: int,
FIELD_TYPE.SHORT: int,
FIELD_TYPE.LONG: int,
FIELD_TYPE.FLOAT: float,
FIELD_TYPE.DOUBLE: float,
FIELD_TYPE.DECIMAL: float,
FIELD_TYPE.NEWDECIMAL: float,
FIELD_TYPE.LONGLONG: int,
FIELD_TYPE.INT24: int,
FIELD_TYPE.YEAR: int,
FIELD_TYPE.TIMESTAMP: convert_mysql_timestamp,
FIELD_TYPE.DATETIME: convert_datetime,
FIELD_TYPE.TIME: convert_timedelta,
FIELD_TYPE.DATE: convert_date,
FIELD_TYPE.SET: convert_set,
FIELD_TYPE.BLOB: through,
FIELD_TYPE.TINY_BLOB: through,
FIELD_TYPE.MEDIUM_BLOB: through,
FIELD_TYPE.LONG_BLOB: through,
FIELD_TYPE.STRING: through,
FIELD_TYPE.VAR_STRING: through,
FIELD_TYPE.VARCHAR: through,
FIELD_TYPE.DECIMAL: Decimal,
FIELD_TYPE.NEWDECIMAL: Decimal,
}
下面來說說,自己如何自定義使用:
from MySQLdb.constants import FIELD_TYPE
my_conv = {
FIELD_TYPE.LONG: int, # 長整型轉成int,默認數據后面有一個L,去掉
FIELD_TYPE.DATE: str # 日期轉成字符串
}
conn = MySQLdb.connect(db='database', host='172.16.0.1', user='user', passwd='password', port=3306,conv=my_conv)
開始擴展connect對象的方法
方法名 | 描述 |
---|---|
close() | 關閉連接 |
commit() | 提交當前事務 |
autocommit() | 自動提交事務 |
rollback() | 取消當前事務 |
cursor() | 實例一個游標對象 |
errorhandler(cxn,cur,errcls,errval) | 作為已給游標的句柄 |
這里介紹一下事務
先來舉個小例子:
conn = MySQLdb.connect(*args, **kwags)
cur = conn.cursor()
sql = "insert into tb_name values (%(id)s,%(name)s,%(age)s);" % {'id': 1, 'name': 'ruyu', 'age': 99}
cur.excute(sql)
cur.close()
conn.close()
插入一行數據,上面操作是不生效的
需要添加一行
conn.commit()
補充知識: 數據庫事務
數據庫事務(Database Transaction) ,是指作為單個邏輯工作單元執行的一系列操作,要么完全地執行,要么完全地不執行。
事務處理可以確保除非事務性單元內的所有操作都成功完成,否則不會永久更新面向數據的資源。
事務的屬性:必須滿足ACID
- (Atomic)(Atomicity)原子性
- (Consistent)(Consistency)一致性
- (Insulation)(Isolation)隔離性
- (Duration)(Durability) 持久性
順便提一下mysql的隔離級別吧! **
- Read Uncommitted(讀取未提交內容)
- Read Committed(讀取提交內容)
- Repeatable Read(可重讀)
- Serializable(可串行化)
這里涉及一些臟讀,幻讀,不可重復讀的概念,希望讀者自己百度,找個教程,做一下數據庫事務的實驗,理解一下事務的概念.
游標對象
cur = MySQLdb.connect(*args, **kwags).cursor()
先介紹一下cursor()的函數
def cursor(self, cursorclass=None):
return (cursorclass or self.cursorclass)(self)
其中一個參數是cursorclass
在MySQLdb.cursors中有所有的cursorclass
分別如下:
BaseCursor,CursorStoreResultMixIn,CursorUseResultMixIn,CursorTupleRowsMixIn,CursorDictRowsMixIn,CursorOldDictRowsMixIn,CursorDictRowsMixIn,CursorOldDictRowsMixIn,Cursor,DictCursor,SSCursor,SSDictCursor
經常使用的應該是DictCursor,Cursor
一種是返回的數據以字典格式,一種是tuple格式.
下面介紹cursor對象的屬性和方法
對象屬性和方法 | 描述 |
---|---|
arraysize | 使用fetchmany()方法一次取出多少條記錄,默認值為1 |
connection | 創建此游標對象的連接connect對象 |
description | 返回游標活動狀態(七個元素的元祖):(name,type_code,display_size,interal_size,precision,scale,null_ok);只有name和type_code是必須提供的 |
lastrowid | 返回最后更新的id(可選),適用場景,在插入數據,返回插入數據的id |
rowcount | 最后一次execute()操作返回或影響的行數 |
callproc(func[,args]) | 調用一個存儲過程 |
close() | 關閉游標對象 |
execute(op[,args]) | 執行一個數據庫查詢或命令 |
executemany(op,args) | 類似execute()和map()的結合,為給定的每一個參數準備并執行一個數據庫的查詢/命令 |
fetchone() | 得到結果集的下一行 |
fetchmany([size=cursor.arraysize]) | 得到結果集的下幾行 |
fetchall() | 返回所有的結果 |
_iter_() | 創建一個迭代對象 |
messages | 游標執行后數據庫返回的信息列表 |
next() | 使用迭代對象得到結果集的下一行 |
nextset() | 移到下一個結果集 |
rownumber | 當前結果集中游標的索引 |
setinput-sizes(sizes) | 設置輸入最大值 |
setoutput-size(size[,col]) | 設置大列的緩沖區大寫 |
提示: 使用最多的就是execute和fetch.
上面的介紹差不多了,這里給出一個mysqldb的封裝的庫torndb的源碼:
#!/usr/bin/env python
from __future__ import absolute_import, division, with_statement
import copy
import logging
import os
import time
#下面是解決python2和python3的mysqldb不同的
try:
import MySQLdb.constants
import MySQLdb.converters
import MySQLdb.cursors
except ImportError:
try:
import pymysql as MySQLdb
except ImportError:
if 'READTHEDOCS' in os.environ:
MySQLdb = None
else:
raise
version = "0.3"
version_info = (0, 3, 0, 0)
# 定義一個connect類,可以實例connect()對象
class Connection(object):
def __init__(self, host, database, user=None, password=None,
max_idle_time=7 * 3600, connect_timeout=0,
time_zone="+0:00", charset="utf8", sql_mode="TRADITIONAL",
**kwargs):
self.host = host
self.database = database
self.max_idle_time = float(max_idle_time)
args = dict(conv=CONVERSIONS, use_unicode=True, charset=charset,
db=database, init_command=('SET time_zone = "%s"' % time_zone),
connect_timeout=connect_timeout, sql_mode=sql_mode, **kwargs)
if user is not None:
args["user"] = user
if password is not None:
args["passwd"] = password
# We accept a path to a MySQL socket file or a host(:port) string
if "/" in host:
args["unix_socket"] = host
else:
self.socket = None
pair = host.split(":")
if len(pair) == 2:
args["host"] = pair[0]
args["port"] = int(pair[1])
else:
args["host"] = host
args["port"] = 3306
self._db = None
self._db_args = args
self._last_use_time = time.time()
try:
self.reconnect()
except Exception:
logging.error("Cannot connect to MySQL on %s", self.host,
exc_info=True)
def __del__(self): # 刪除和關閉
self.close()
def close(self): # 關閉并置位空
if getattr(self, "_db", None) is not None:
self._db.close()
self._db = None
def reconnect(self): # 重新連接
self.close()
self._db = MySQLdb.connect(**self._db_args)
self._db.autocommit(True) # 開啟自動提交的功能
def iter(self, query, *parameters, **kwparameters): # 進行迭代的
self._ensure_connected()
cursor = MySQLdb.cursors.SSCursor(self._db)
try:
self._execute(cursor, query, parameters, kwparameters)
column_names = [d[0] for d in cursor.description]
for row in cursor:
yield Row(zip(column_names, row))
finally:
cursor.close()
def query(self, query, *parameters, **kwparameters): # 進行執行,返回結果
"""Returns a row list for the given query and parameters."""
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
column_names = [d[0] for d in cursor.description]
return [Row(zip(column_names, row)) for row in cursor]
finally:
cursor.close()
def get(self, query, *parameters, **kwparameters):
rows = self.query(query, *parameters, **kwparameters)
if not rows:
return None
elif len(rows) > 1:
raise Exception("Multiple rows returned for Database.get() query")
else:
return rows[0]
def execute(self, query, *parameters, **kwparameters):
return self.execute_lastrowid(query, *parameters, **kwparameters)
def execute_lastrowid(self, query, *parameters, **kwparameters):
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
return cursor.lastrowid
finally:
cursor.close()
def execute_rowcount(self, query, *parameters, **kwparameters):
cursor = self._cursor()
try:
self._execute(cursor, query, parameters, kwparameters)
return cursor.rowcount
finally:
cursor.close()
def executemany(self, query, parameters):
return self.executemany_lastrowid(query, parameters)
def executemany_lastrowid(self, query, parameters):
cursor = self._cursor()
try:
cursor.executemany(query, parameters)
return cursor.lastrowid
finally:
cursor.close()
def executemany_rowcount(self, query, parameters):
cursor = self._cursor()
try:
cursor.executemany(query, parameters)
return cursor.rowcount
finally:
cursor.close()
update = delete = execute_rowcount
updatemany = executemany_rowcount
insert = execute_lastrowid
insertmany = executemany_lastrowid
def _ensure_connected(self):
if (self._db is None or
(time.time() - self._last_use_time > self.max_idle_time)):
self.reconnect()
self._last_use_time = time.time()
def _cursor(self):
self._ensure_connected()
return self._db.cursor()
def _execute(self, cursor, query, parameters, kwparameters):
try:
return cursor.execute(query, kwparameters or parameters)
except OperationalError:
logging.error("Error connecting to MySQL on %s", self.host)
self.close()
raise
class Row(dict):
def __getattr__(self, name):
try:
return self[name]
except KeyError:
raise AttributeError(name)
if MySQLdb is not None:
FIELD_TYPE = MySQLdb.constants.FIELD_TYPE
FLAG = MySQLdb.constants.FLAG
CONVERSIONS = copy.copy(MySQLdb.converters.conversions)
field_types = [FIELD_TYPE.BLOB, FIELD_TYPE.STRING, FIELD_TYPE.VAR_STRING]
if 'VARCHAR' in vars(FIELD_TYPE):
field_types.append(FIELD_TYPE.VARCHAR)
for field_type in field_types:
CONVERSIONS[field_type] = [(FLAG.BINARY, str)] + CONVERSIONS[field_type]
IntegrityError = MySQLdb.IntegrityError
OperationalError = MySQLdb.OperationalError
上面源碼就不解釋了,都是很基礎的內容,很適合入門學習.
這里說一個python-mysqldb遇到的問題,很復雜的sql語句,在mysql中有數據,但是在mysqldb第一次執行確有部分字段是None,第二次或后面都是沒問題的,我也請教了我們的python大神,他說他遇到,t并且他說這不是bug.他不能重現問題,所以也沒解決,我的緩兵之計如下:
def query(self, query):
cursor = self._cursor()
try:
cursor.execute(query)
data = cursor.fetchall()
cursor.execute(query)
raw = cursor.fetchall()
return raw
finally:
cursor.close()
內容到這里就結束了!!!!
預告一下,后續的內容: ORM,自己帶著寫一個ORM,Django ORM的queryset,manager以及一些定制化的,當然還有sqlalchemy.