Python連接數據庫
Python官方制定的數據庫接口標準中,主要包含了頂層connect函數、部分常量、數據庫操作異常、用於管理連接的Connection類以及執行查詢的Cursor類。
DBUtils是python用於管理數據庫連接池的包,爲高頻度高併發的數據庫訪問提供更好的性能,可以自動管理連接對象的創建和釋放。最常用的兩個外部接口是PersistentDB和PooledDB,前者提供了單個線程專用的數據庫連接池,後者則是進程內所有線程共享的數據庫連接池。
在Python中操作數據庫,基本步驟如下:
1)導入相應的Python模塊
2)使用connect函數連接數據庫,並返回一個Connection對象
3)通過Connection對象的cursor方法,返回一個Cursor對象
4)通過Cursor對象的execute方法執行SQL語句
5)如果執行的是查詢語句,通過Cursor對象的fetchall語句返回結果
6)調用Cursor對象的close方法關閉Cursor
7)調用Connection對象的close方法關閉數據庫連接
**python訪問MySQL數據庫需要安裝第三方模塊,使用最廣泛的是mysqldb和pymysql.
python訪問SqlServer數據庫需要安裝第三方模塊pymssql
實現數據庫連接池 pip install dbutils
**
完整示例如下
import pymysql
db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',
db='aac_lens_analysis',connect_timeout=100)
cur = db.cursor()
sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cur.execute(sql)
results = cur.fetchall()
print(cur.rowcount)
cur.close()
db.close()
連接數據庫
模塊中包含了connect函數、常量和異常。調用PyMySQL的Connect函數,將創建一個數據庫連接得到一個Connection對象。Connect函數的部分參數如下:
db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',db='aac_lens_analysis',connect_timeout=100)
host:數據庫服務的地址
port:連接數據庫的端口號,默認mysql端口號3306
user:登錄數據庫的用戶名
passwd:登錄數據庫的密碼
connect_timeout:連接超時時間
read_default_file:讀取MySQL的配置文件中的配置進行連接
Connection類的成員
通過正確的參數調用PyMySQL的Connect函數,將會返回Connection類的對象。
db = pymysql.connect(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',db='aac_lens_analysis',connect_timeout=100)
cursor = db.cursor()
#以字典的形式返回操作結果
#cursor = db.cursor(pymysql.cursors.DictCursor)
常用的方法如下:
begin:開始事務
commit;提交事務
rollback:回滾事務
cursor:返回一個Cursor對象
在實際編程過程中,一般不會直接調用begin、commit、rollback函數,而是通過上下文管理器實現事務的提交與回滾操作。
DictCursor:在默認情況下cursor方法返回的是BaseCursor類型對象,BaseCursor類型對象在執行查詢後每條記錄的結果以列表(list)表示返回,要返回字典(dict)表示的記錄,就要設置cursor參數爲pymysql.cursors.DictCursor類
Cursor類的成員
Cursor對象表示數據庫遊標,用於執行SQL語句並獲取SQL語句的執行結果。Cursor包含了一些異常、常量和函數。常用的常量和函數如下所示:
cursor = db.cursor()
sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cursor.execute(sql)
results = cursor.fetchall()
print(cur.rowcount)
cur.close()
db.close()
execute:執行SQL語句
close:關閉遊標
fetchall:獲取SQL語句的所有記錄
fetchmany:獲取SQL語句的多條記錄
fetchone:獲取SQL語句的一條記錄
rowcount:常量,表示SQL語句的結果集中返回了多少條記錄
使用上下文管理器對數據庫連接進行管理
with語句可以實現任何try/finally語句實現的功能,而且代碼更加清晰簡潔。可以對數據庫的操作進行封裝,封裝完成以後,可以使用with語句保證數據庫連接無論在什麼情況下都會關閉。
還可以使用contextlib模塊中的contextmanager進行上下文管理。
from contextlib import contextmanager
import pymysql
@contextmanager
def get_conn(**kwargs):
conn = pymysql.connect(host=kwargs.get('host','localhost'),
user=kwargs.get('user'),
passwd=kwargs.get('password'),
port=kwargs.get('port',3306),
db=kwargs.get('db')
)
try:
yield conn
finally:
if conn:
conn.close()
conn_args = dict(host="10.133.0.46", port=3306, user='rw_aps', password='rw_aps.aac',
db='aac_lens_analysis',connect_timeout=100)
# 對創建的數據庫連接進行封裝以後,可以使用with語句管理數據庫連接,使得代碼更加清晰,且不容易出錯
with get_conn(**conn_args) as conn:
with conn as cur:
sql = f"select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cur.execute(sql)
results = cur.fetchall()
print(cur.rowcount)
使用數據庫連接池訪問數據庫
使用pymysql、pymssql連接數據庫查詢的弊端是:每次都要連接數據庫浪費資源,所有必須用數據庫連接池。
參數 | 說明 |
---|---|
creater | 數據庫接口 |
mincached | 啓動時開啓的空連接數量 |
maxcached | 連接池最大可用連接數量 |
maxshared | 連接池最大可共享連接數量 |
maxconnections | 最大允許連接數 |
blocking | 達到最大數量時是否阻塞 |
maxusage | 單個連接最大複用次數 |
setsession | 用於傳遞到數據庫的準備會話 |
from DBUtils.PooledDB import PooledDB
import pymysql
config = {
'creator': pymysql,
'host': '10.133.0.46',
'port': 3306,
'user': 'rw_aps',
'password': 'rw_aps.aac',
'db': 'aac_lens_analysis',
'charset': 'utf8',
'maxconnections': 10, # 連接池最大連接數量
'cursorclass': pymysql.cursors.DictCursor #以字典的形式返回操作結果
}
pool = PooledDB(**config)
db = pool.connection()
cur = db.cursor()
sql = "select * from aac_lens_analysis.t_mysql_status where pair_date='2020-06-03' and pair_shift='day' "
cur.execute(sql)
result = cur.fetchall()
for i in result:
print(i)
cur.close()
db.close()
輸出結果如下所示:
{'id': 48, 'pair_date': '2020-06-03', 'pair_shift': 'day', 'mysql_status': 'Success', 'site': '(2080, 2090, 5060)', 'created_time': datetime.datetime(2020, 6, 4, 2, 0)}
案例一: 從csv文件導入數據到MySQL數據庫
csv文件內容如下:
首先創建數據庫連接,在for循環中遍歷get_data函數的結果。get_data函數打開csv文件,並將csv文件的每一行轉換成一個命名元組。由於get_data使用yield語句返回結果,可以直接在for循環中遍歷get_data函數的返回值。得到命名元組以後,拼接SQL語句,然後通過Cursor對象執行SQL語句
import pymysql
import csv
from collections import namedtuple
from contextlib import contextmanager
# 使用上下文管理器對數據庫連接進行管理
@contextmanager
def get_conn(**kwargs):
conn = pymysql.connect(host=kwargs.get('host','localhost'),
user=kwargs.get('user'),
passwd=kwargs.get('password'),
port=kwargs.get('port',3306),
db=kwargs.get('db')
)
try:
yield conn
finally:
if conn:
conn.close()
#使用命名元組以後,允許使用列名代替下標訪問元組中的內容
def get_data(file_name):
with open(file_name) as f:
f_csv = csv.reader(f)
#['id', 'name', 'sex', 'age', 'love']
headings = next(f_csv)
#命名元組
Row = namedtuple('Row',headings)
for r in f_csv:
#['1', '劉備', '男', '20', 'book']
yield Row(*r)
def execute_sql(conn,sql):
with conn as cur:
cur.execute(sql)
def main():
conn_args = dict(host="10.177.33.45", port=3306, user='root', password='root',
db='dw',connect_timeout=100)
with get_conn(**conn_args) as conn:
for i in get_data(r"D:\tmp\test_csv.csv"):
sql = f"insert into student values({i.id},'{i.name}','{i.sex}',{i.age},'{i.love}') "
print(sql)
execute_sql(conn,sql)
if __name__ == '__main__':
main()
數據庫結果如下所示:
案例二:查詢數據庫數據往釘釘羣推送
import requests
import json
import datetime
import pymysql
from collections import namedtuple
api_url = 'https://oapi.dingtalk.com/robot/send?access_token=98574912ab4cde67a19fbb875df93671088e6de87337bc1fedea10e1d2637461'
headers={'Content-Type':'application/json;charset=utf-8'}
retry = 26
# retry = 120
# 重試時間間隔:秒
retry_sec = 60
# retry_sec = 60
stop_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
start_time = datetime.datetime.now() + datetime.timedelta(hours=-1)
start_time = start_time.strftime("%Y-%m-%d %H:%M:%S")
# 往釘釘發送審計日誌
def msg(text):
json_text={
"msgtype":"markdown",
"markdown":{
"title":"編組狀態通知",
"text":text
}
}
print(requests.post(api_url,json.dumps(json_text),headers=headers).content)
# 獲取數倉開始狀態
def get_monitor_audit_data():
try:
db = pymysql.connect(host='10.133.0.19', port=30334, user='rw_etl', password='rw_etl.aac', db='lens_olap')
cursor = db.cursor()
sql = "select time as '命令執行時間',regexp_replace(user, 'default_cluster:', '') as 用戶,cast(query_time / 1000 / 60 as int) as '查詢時長(分鐘)',cast(scan_bytes/1024/1024 as int) as '查詢數據量(M)',scan_rows as 查詢行數,return_rows as 返回行數,stmt as 查詢語句 from doris_audit_db__.doris_audit_tbl__ where query_time > 300000 and time > date_sub(now(), INTERVAL 1 hour) order by time desc"
print("---ddddddddd---------")
print(sql)
cursor.execute(sql)
results = cursor.fetchall()
a=cursor.rowcount
print(a)
list=[]
if len(results) >= 1:
msg(f"{start_time}到{stop_time}時間範圍內,palo數據庫大的sql查詢情況如下:")
headings = ['命令執行時間', '用戶', '查詢時長', '查詢數據量', '查詢行數', '返回行數', '查詢語句']
Row = namedtuple('Row', headings)
for i in results:
a=[*i]
row_data=Row(*a)
text = f"""
用戶:{row_data.用戶}
命令執行時間:{row_data.命令執行時間}
查詢時長(分鐘):{row_data.查詢時長}
查詢數據量(M):{row_data.查詢數據量}
查詢行數:{row_data.查詢行數}
返回行數:{row_data.返回行數}
查詢語句:{row_data.查詢語句}
"""
msg(text)
cursor.close()
db.close()
else:
print(f"{start_time}到{stop_time}時間範圍內,palo數據庫無大的sql查詢")
except Exception as e:
print(e)
msg(f"{start_time}到{stop_time},連接palo數據庫異常")
if __name__ == '__main__':
get_monitor_audit_data()