Python核心編程筆記————數據庫編程(一)

數據庫簡介

持久化存儲

. 持久化存儲一般有三種基礎的存儲機制:文件、數據庫系統以及一些混合類型。這種混合類型包括現有系統上的 API、ORM、文件管理器、電子表格、配置文件等。

數據庫基本操作和SQL

, 以下包括一些基礎的數據庫概念和SQL語句

底層存儲

. 數據庫通常使用文件系統作爲基本的持久化存儲。它可以是普通的操作系統文件、專用的操作系統文件,甚至是原始的磁盤分區。

用戶接口

. 大多數數據庫系統提供了命令行工具,可以用其執行 SQL 語句或查詢。此外還有一些GUI 工具,使用命令行客戶端或數據庫客戶端庫,向用戶提供更加便捷的界面。

數據庫

. 一個關係數據庫管理系統(RDBMS)通常可以管理多個數據庫,MySQL 是一種基於服務的RDBMS,因爲它有一個服務器進程始終運行以等待命令行輸入;而 SQLite 和 Gadfly(一個完全使用 Python 編寫的簡單的 RDBMS) 則不會運行服務器。

組件

. 數據庫存儲可以抽象爲一張表。每行數據都有一些字段對應於數據庫的列。每一列的表定義的集合以及每個表的數據類型放到一起定義了數據庫的模式(schema)。

SQL

. 數據庫命令和查詢操作是通過 SQL 語句提交給數據庫的。並非所有數據庫都使用SQL 語句,但是大多數關係數據庫使用大部分數據庫都是不區分大小寫的,尤其是針對數據庫命令而言。一般來說,對數據庫關鍵字使用大寫字母是最爲廣泛接受的風格。大多數命令行程序需要一條結尾的分號(;)來結束這條 SQL語句。以下是一些SQL命令示例:

功能 語句 描述
創建數據庫 CREATE DATABASE test;
GRANT ALL ON test.* to user(s);
第一行創建一個名爲test的數據庫,
第二行爲指定用戶(或所有用戶)提升權限。
使用數據庫 USE test; 如果已經登錄一個數據庫系統,
但還沒有選擇希望使用的數據庫,
這條語句可以指定一個數據庫,
用來執行數據庫操作。
刪除數據庫 DROP DATABASE test; 從數據庫中移除所有表和數據,
並將其從系統中刪除。
創建表 CREATE TABLE users (login VARCHAR(8), userid INT, projid INT); 創建一個新表user,其中包含字符串列 login,以及兩個整型列:userid 和 projid。
刪除表 DROP TABLE users; 刪除數據庫中的一個表,並清空其中的所有數據。
插入行 INSERT INTO users VALUES(‘leanna’, 2111, 1); 向數據庫中插入一個新行。需要指定表名以及其中每列的值
更新行 UPDATE users SET projid=4 WHERE projid=2;
UPDATE users SET projid=1 WHERE userid=311;
修改表中已經存在的行,使用 SET 來確定要修改的列,並提供條件來確定要修改的行
刪除行 DELETE FROM users WHERE projid=%d;
DELETE FROM users;
指定準備刪除的行的表名以及可選的條件。如果沒有這個條件,就會像第二個例子一樣,把所有行都刪除了。

數據庫與Python

. 訪問數據庫包括直接通過數據庫接口訪問和使用 ORM (對象關係映射)訪問兩種方式。其中使用 ORM 訪問的方式不需要顯式給出 SQL 命令,但也能完成相同的任務。
  在Python中數據庫是通過適配器的方式來訪問的。適配器是一個Python模塊,使用它可以與關係數據庫的客戶端庫(通常是使用C語言進行編寫的)接口相連

Python的DB-API

. DB-API 是闡明一系列所需對象和數據庫訪問機制的標準,它可以爲不同的數據庫適配器和底層數據庫系統提供一致性的訪問。DB-API 也是強需求驅動的。這種一致性產生的原因是曾經有很多的數據庫和數據庫適配器,在功能上完全沒有一致性可言,這意味着使用這些接口的應用代碼需要與他們選擇的數據庫模塊進行定製化的處理,接口的任何改變都會導致應用代碼的變更。
  由此,爲了解決 Python 數據庫連接問題的特殊興趣小組成立了,並且撰寫了 1.0 版本的DB-API。該 API 爲不同的關係數據庫提供了一致性的接口,並且使不同數據庫間移植代碼變得更加簡單,通常只需要修改幾行代碼即可。

模塊屬性

. DB-API 標準要求必須提供下文列出的功能和屬性。一個兼容 DB-API 的模塊必須定義下表所示的幾個全局屬性。

屬性 描述
apilevel 需要適配器兼容的 DB-API 版本
threadsafety 本模塊的線程安全級別
paramstyle 本模塊的 SQL 語句參數風格
connect() Connect()函數
(多種異常) 參見後面的表

數據屬性

. apilevel是一個字符串(注意,不是浮點型),它指明瞭模塊需要兼容的DB-API 最高版本,默認值是1.0
  threadsafety是一個整型值,可選值如下:
  0:不支持線程安全。線程間不能共享模塊。
  1:最小化線程安全支持:線程間可以共享模塊,但是不能共享連接。
  2:適度的線程安全支持:線程間可以共享模塊和連接,但是不能共享遊標。
  3:完整的線程安全支持:線程間可以共享模塊、連接和遊標。

參數風格

. DB-API 支持以不同的方式指明如何將參數與SQL 語句進行整合,並最終傳遞給服務器中執行。該參數是一個字符串,用於指定構建查詢行或命令時使用的字符串替代形式

參數風格 描述 示例
numeric 數值位置風格 WHERE name=:1
named 命名風格 WHERE name=:name
pyformat Python 字典 printf()格式轉換 WHERE name=%(name)s
qmark 問號風格 WHERE name=?
format ANSIC 的 printf()格式轉換 WHERE name=%s

函數屬性

. connect()函數通過 Connection 對象訪問數據庫。兼容模塊必須實現 connect()函數,該函數創建並返回一個 Connection 對象,下表爲connect()函數的參數:

參數 描述
user 用戶名
password 密碼
host 主機名
database 數據庫名
dsn 數據源名

. 使用 DSN 還是獨立參數主要基於所連接的系統。比如,如果你使用的是像ODBC(OpenDatabase Connectivity)或 JDBC(Java Database Connectivity)的 API,則需要使用 DSN;而如果你直接使用數據庫,則更傾向於使用獨立的登錄參數。另一個使用獨立參數的原因是很多數據庫適配器並沒有實現對 DSN 的支持。

異常

. 異常同樣需要包含在兼容的模塊中,如下表所示:

異常 描述
Warning 警告異常基類
Error 錯誤異常基類
…InterfaceError 數據庫接口(非數據庫)錯誤
…DatabaseError 數據庫錯誤
… …DataError 處理數據時出現問題
… …OperationlError 數據庫操作執行期間出現錯誤
… …IntegrityError 數據庫關係完整性錯誤
… …InternalError 數據庫內部錯誤
… …ProgrammingError SQL 命令執行失敗
… …NotSupportedError 出現不支持的操作

connection對象

. 應用與數據庫之間進行通信需要建立數據庫連接,當一個連接(或一個連接池)建立後,可以創建一個遊標,向數據庫發送請求,然後從數據庫中接收回應。

connection對象方法

. Connection 對象不需要包含任何數據屬性,不過應當定義以下方法:

方法名 描述
close() 關閉數據庫連接
commit() 提交當前事務
rollback() 取消當前事務
cursor() 使用該連接創建一個遊標或類遊標對象
errorhandler (cxn, cur, errcls, errval ) 作爲給定連接的遊標的處理程序

. 當使用close時,這個連接將不能被使用,都則會進入異常處理中。
  commit()和rollback()只有在支持事務處理的數據庫中才能使用,如果數據庫啓用了自動提交的功能,commit()方法也不能使用;一般發生異常後,rollback()方法會將數據庫的狀態恢復到事務處理開始前的狀態。
  如果RDBMS不支持遊標,那麼cursor()方法仍會返回一個儘可能模仿遊標的對象。
  DB-API 建議適配器開發者爲連接編寫所有的數據庫模塊異常,但不是強制要求,如果沒有,則認爲 Connection 對象將會拋出對應模塊級別的異常。

Cursor 對象

. 當建立連接後,就可以和數據庫進行通信了。遊標可以讓用戶提交數據庫命令,並獲得查詢的結果行。以下爲Cursor對象的屬性和方法:

對象屬性 描述
arraysize 使用 fetchmany()方法時,一次取出的結果行數,默認爲 1
connection 創建此遊標的連接(可選)
description 返回遊標活動狀態(7項元組):(name, type_code, display_size, internal_ size,precision, scale, null_ok),只有 name 和 type_code 是必需的
lastrowid 上次修改行的行 ID(可選;如果不支持行 ID,則返回 None)
rowcount 上次 execute*()方法處理或影響的行數
callproc( func [,args]) 調用存儲過程
close() 關閉遊標
execute (op[,args]) 執行數據庫查詢或命令
executemany (op,args) 類似 execute()和 map()的結合,爲給定的所有參數準備並執行數據庫查詢或命令
fetchone() 獲取查詢結果的下一行
fetchmany([size=cursor. arraysize]) 獲取查詢結果的下面 size 行
fetchall() 獲取查詢結果的所有(剩餘)行
_iter_() 爲遊標創建迭代器對象
messages 遊標執行後從數據庫中獲得的消息列表(元組集合,可選)
next () 被迭代器用於獲取查詢結果的下一行
nextset() 移動到下一個結果集合(如果支持)
rownumber 當前結果集中游標的索引(以行爲單位,從 0 開始,可選)
setinputsizes(sizes) 設置允許的最大輸入大小(必須有,但是實現是可選的)
setoutputsize(size[,col]) 設置大列獲取的最大緩衝區大小(必須有,但是實現是可選的)

. 遊標對象最重要的屬性是 execute*()和 fetch*()方法,所有針對數據庫的服務請求都
是通過它們執行的。arraysize 數據屬性在爲 fetchmany()設置默認大小時非常有用。

類型對象和構造函數

. 不同系統之間的接口是很令人頭疼的,比如Python對象傳給原生數據庫對象的內容一般是一個字符串,但是數據庫可能需要對其轉換成不同的類型,比如傳入的字符串是應該轉換爲VARCHAR、TEXT還是DATE對象呢?因此,DB-API的另一個需求是創建構造函數,從而構建可以簡單地轉換成適當數據庫對象的特殊對象。下表是用於此目的的一些類:

類型對象 描述
Date (yr, mo, dy) 日期值對象
Time (hr, min, sec) 時間值對象
Timestamp (yr, mo, dy, hr, min, sec) 時間戳值對象
DateFromTicks (ticks) 日期對象,給出從新紀元時間(1970 年 1 月 1 日 00:00:00 UTC)以來的天數
TimeFromTicks (ticks) 時間對象,給出從新紀元時間(1970 年 1 月 1 日 00:00:00 UTC)以來的秒數
TimestampFromTicks (ticks) 時間戳對象,給出從新紀元時間(1970 年 1 月 1 日 00:00:00 UTC)以來的秒數
Binary (string) 對應二進制(長)字符串對象
STRING 表示基於字符串列的對象,比如 VARCHAR
BINARY 表示(長)二進制列的對象,比如 RAW、BLOB
NUMBER 表示數值列的對象
DATETIME 表示日期/時間列的對象
ROWID 表示“行 ID”列的對象

使用數據庫適配器的示例

MySQL

. 這裏使用的是MySQL 官方提供的驅動器:mysql-connector 。

import mysql.connector as mc					#不要把文件名命名爲mysql

md = mc.connect(
    host = "xx.xx.x.xx",						#服務器地址,就是單純的ip地址
    port = "xxxx"								#不寫的話默認爲3306
    user = "xxxx",
    passwd = "xxxx",
    database = "xxxxx"						#數據庫名
)
mycursor = md.cursor()

print("返回所有數據")
mycursor.execute("select * from table")
result = mycursor.fetchall()
for x in result:
    print(x)
    
print("返回一條數據")
mycursor.execute("select * from table")	#因爲前面fetchall了,這裏不重新執行的話,後面會爲空
result = mycursor.fetchone()
print(result)

mycursor.close()
md.close()

oracle

. 這裏使用的是cx_Oracle :

import cx_Oracle as co

str = 'user/[email protected]:xxxx/xxxx'
conn = co.connect(str)
c = conn.cursor()
x = c.execute(sql)
value = x.fetchone()

ORM(對象關係映射)

. 大部分數據庫系統都包含Python接口,能使人更好的利用它們的功能,但是缺點是需要去了解SQL,如果更希望操縱Python對象而不是SQL查詢,且仍然希望使用關係數據庫作爲數據後端,那麼可以使用 ORM。

Python與ORM

.  ORM 系統的作者將純 SQL 語句進行了抽象化處理,將其實現爲 Python 中的對象,數據庫表被神奇地轉化爲 Python 類,其中的數據列作爲屬性,而數據庫操作則會作爲方法。
  以下介紹一個知名的 Python ORM:SQLAlchemy。這不是標準庫中的模塊,需要手動下載。

from sqlalchemy import Column,String,create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()       #創建基類

class CurMode(Base):
    __tablename__ = 'curr_system_mode'

    #表結構
    station = Column(String(8),primary_key=True)
    mode_code = Column(String(4))

#初始化數據庫連接
#'數據庫類型+數據庫驅動名稱://用戶名:口令@機器地址:端口號/數據庫名'
engine = create_engine('mysql+mysqlconnector://xx:[email protected]:3306/xx')
dbsession = sessionmaker(bind = engine)

#創建session對象,執行操作
session = dbsession()
user = session.query(CurMode).one()
print(user.station)
print(user.mode_code)

session.close()

. 以下是一些常用的用於查詢的方法:
• filter_by():將指定列的值作爲關鍵字參數以獲取查詢結果。
• filter():與filter_by()相似,不過更加靈活,還可以使用表達式。比如query.filter_by(userid=1)與query.filter(User.userid==1)相同。
• order_by():與 SQL 的 ORDER BY 指令類似。默認情況下是升序的。需要導入
sqlalchemy.desc()使其降序排列。
• limit():與 SQL 的 LIMIT 指令類似。
• offset():與 SQL 的 OFFSET 指令類似。
• all():返回匹配查詢的所有對象。
• one():返回匹配查詢的唯一一個(下一個)對象。
• first():返回匹配查詢的第一個對象。
• join():按照給定的 JOIN 條件創建 SQL JOIN 語句。
• update():批量更新行。
• delete():批量刪除行。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章