鍍金池/ 教程/ Python/ MySQL 數(shù)據(jù)庫 (2)
標準庫 (4)
如何成為 Python 高手
標準庫 (6)
標準庫 (3)
類(2)
Pandas 使用 (2)
xml
用 tornado 做網(wǎng)站 (5)
文件(1)
練習(xí)
列表(3)
從小工到專家
除法
錯誤和異常 (2)
函數(shù)(1)
用 tornado 做網(wǎng)站 (7)
為做網(wǎng)站而準備
函數(shù)練習(xí)
標準庫 (8)
Pandas 使用 (1)
回顧 list 和 str
字典(1)
用 tornado 做網(wǎng)站 (3)
字符串(1)
函數(shù)(2)
寫一個簡單的程序
將數(shù)據(jù)存入文件
語句(5)
SQLite 數(shù)據(jù)庫
集成開發(fā)環(huán)境(IDE)
集合(1)
類(1)
用 tornado 做網(wǎng)站 (6)
用 tornado 做網(wǎng)站 (2)
自省
語句(4)
錯誤和異常 (1)
用 tornado 做網(wǎng)站 (4)
集合(2)
列表(1)
標準庫 (1)
生成器
mysql 數(shù)據(jù)庫 (1)
第三方庫
實戰(zhàn)
運算符
類(3)
字典(2)
語句(1)
數(shù)和四則運算
語句(2)
文件(2)
MySQL 數(shù)據(jù)庫 (2)
電子表格
迭代器
mongodb 數(shù)據(jù)庫 (1)
特殊方法 (2)
特殊方法 (1)
字符編碼
編寫模塊
用 tornado 做網(wǎng)站 (1)
標準庫 (5)
函數(shù)(4)
類(5)
字符串(2)
關(guān)于 Python 的故事
函數(shù)(3)
字符串(4)
處理股票數(shù)據(jù)
常用數(shù)學(xué)函數(shù)和運算優(yōu)先級
字符串(3)
為計算做準備
多態(tài)和封裝
類(4)
迭代
語句(3)
錯誤和異常 (3)
分析 Hello
Python 安裝
標準庫 (2)
列表(2)
元組

MySQL 數(shù)據(jù)庫 (2)

就數(shù)據(jù)庫而言,連接之后就要對其操作。但是,目前那個名字叫做 qiwsirtest 的數(shù)據(jù)僅僅是空架子,沒有什么可操作的,要操作它,就必須在里面建立“表”,什么是數(shù)據(jù)庫的表呢?下面摘抄自維基百科對數(shù)據(jù)庫表的簡要解釋,要想詳細了解,需要看官在找一些有關(guān)數(shù)據(jù)庫的教程和書籍來看看。

在關(guān)系數(shù)據(jù)庫中,數(shù)據(jù)庫表是一系列二維數(shù)組的集合,用來代表和儲存數(shù)據(jù)對象之間的關(guān)系。它由縱向的列和橫向的行組成,例如一個有關(guān)作者信息的名為 authors 的表中,每個列包含的是所有作者的某個特定類型的信息,比如“姓氏”,而每行則包含了某個特定作者的所有信息:姓、名、住址等等。

對于特定的數(shù)據(jù)庫表,列的數(shù)目一般事先固定,各列之間可以由列名來識別。而行的數(shù)目可以隨時、動態(tài)變化,每行通常都可以根據(jù)某個(或某幾個)列中的數(shù)據(jù)來識別,稱為候選鍵。

我打算在 qiwsirtest 中建立一個存儲用戶名、用戶密碼、用戶郵箱的表,其結(jié)構(gòu)用二維表格表現(xiàn)如下:

username password email
qiwsir 123123 qiwsir@gmail.com

特別說明,這里為了簡化細節(jié),突出重點,對密碼不加密,直接明文保存,雖然這種方式是很不安全的。但是,有不少網(wǎng)站還都這么做的,這么做的目的是比較可惡的。就讓我在這里,僅僅在這里可惡一次。

數(shù)據(jù)庫表

因為直接操作數(shù)據(jù)部分,不是本教重點,但是關(guān)聯(lián)到后面的操作,為了讓讀者在閱讀上連貫,也快速地說明建立數(shù)據(jù)庫表并輸入內(nèi)容。

mysql> use qiwsirtest;
Database changed
mysql> show tables;
Empty set (0.00 sec)

show tables 命令顯示這個數(shù)據(jù)庫中是否有數(shù)據(jù)表了。查詢結(jié)果顯示為空。

下面就用如下命令建立一個數(shù)據(jù)表,這個數(shù)據(jù)表的內(nèi)容就是上面所說明的。

mysql> create table users(id int(2) not null primary key auto_increment,username varchar(40),password text,email text)default charset=utf8;
Query OK, 0 rows affected (0.12 sec)

建立的這個數(shù)據(jù)表名稱是:users,其中包含上述字段,可以用下面的方式看一看這個數(shù)據(jù)表的結(jié)構(gòu)。

mysql> show tables;
+----------------------+
| Tables_in_qiwsirtest |
+----------------------+
| users                |
+----------------------+
1 row in set (0.00 sec)

查詢顯示,在 qiwsirtest 這個數(shù)據(jù)庫中,已經(jīng)有一個表,它的名字是:users。

mysql> desc users;
+----------+-------------+------+-----+---------+----------------+
| Field    | Type        | Null | Key | Default | Extra          |
+----------+-------------+------+-----+---------+----------------+
| id       | int(2)      | NO   | PRI | NULL    | auto_increment |
| username | varchar(40) | YES  |     | NULL    |                |
| password | text        | YES  |     | NULL    |                |
| email    | text        | YES  |     | NULL    |                |
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

顯示表 users 的結(jié)構(gòu):

特別提醒:上述所有字段設(shè)置僅為演示,在實際開發(fā)中,要根據(jù)具體情況來確定字段的屬性。

如此就得到了一個空表。可以查詢看看:

mysql> select * from users;
Empty set (0.01 sec)

向里面插入點信息,就只插入一條吧。

mysql> insert into users(username,password,email) values("qiwsir","123123","qiwsir@gmail.com");
Query OK, 1 row affected (0.05 sec)

mysql> select * from users;
+----+----------+----------+------------------+
| id | username | password | email            |
+----+----------+----------+------------------+
|  1 | qiwsir   | 123123   | qiwsir@gmail.com |
+----+----------+----------+------------------+
1 row in set (0.00 sec)

這樣就得到了一個有內(nèi)容的數(shù)據(jù)庫表。

Python 操作數(shù)據(jù)庫

連接數(shù)據(jù)庫,必須的。

>>> import MySQLdb
>>> conn = MySQLdb.connect(host="localhost",user="root",passwd="123123",db="qiwsirtest",charset="utf8")

Python 建立了與數(shù)據(jù)的連接,其實是建立了一個 MySQLdb.connect() 的實例對象,或者泛泛地稱之為連接對象,Python 就是通過連接對象和數(shù)據(jù)庫對話。這個對象常用的方法有:

  • commit():如果數(shù)據(jù)庫表進行了修改,提交保存當前的數(shù)據(jù)。當然,如果此用戶沒有權(quán)限就作罷了,什么也不會發(fā)生。
  • rollback():如果有權(quán)限,就取消當前的操作,否則報錯。
  • cursor([cursorclass]):返回連接的游標對象。通過游標執(zhí)行 SQL 查詢并檢查結(jié)果。游標比連接支持更多的方法,而且可能在程序中更好用。
  • close():關(guān)閉連接。此后,連接對象和游標都不再可用了。

Python 和數(shù)據(jù)之間的連接建立起來之后,要操作數(shù)據(jù)庫,就需要讓 Python 對數(shù)據(jù)庫執(zhí)行 SQL 語句。Python 是通過游標執(zhí)行 SQL 語句的。所以,連接建立之后,就要利用連接對象得到游標對象,方法如下:

>>> cur = conn.cursor()

此后,就可以利用游標對象的方法對數(shù)據(jù)庫進行操作。那么還得了解游標對象的常用方法:

名稱 描述
close() 關(guān)閉游標。之后游標不可用
execute(query[,args]) 執(zhí)行一條 SQL 語句,可以帶參數(shù)
executemany(query, pseq) 對序列 pseq 中的每個參數(shù)執(zhí)行 sql 語句
fetchone() 返回一條查詢結(jié)果
fetchall() 返回所有查詢結(jié)果
fetchmany([size]) 返回 size 條結(jié)果
nextset() 移動到下一個結(jié)果
scroll(value,mode='relative') 移動游標到指定行,如果 mode='relative',則表示從當前所在行移動 value 條,如果 mode='absolute',則表示從結(jié)果集的第一行移動 value 條.

插入

例如,要在數(shù)據(jù)表 users 中插入一條記錄,使得:username="Python",password="123456",email="Python@gmail.com",這樣做:

>>> cur.execute("insert into users (username,password,email) values (%s,%s,%s)",("Python","123456","Python@gmail.com"))
1L

沒有報錯,并且返回一個"1L"結(jié)果,說明有一n 行記錄操作成功。不妨用"mysql>"交互方式查看一下:

mysql> select * from users;
+----+----------+----------+------------------+
| id | username | password | email            |
+----+----------+----------+------------------+
|  1 | qiwsir   | 123123   | qiwsir@gmail.com |
+----+----------+----------+------------------+
1 row in set (0.00 sec)

咦,奇怪呀。怎么沒有看到增加的那一條呢?哪里錯了?可是上面也沒有報錯呀。

特別注意,通過"cur.execute()"對數(shù)據(jù)庫進行操作之后,沒有報錯,完全正確,但是不等于數(shù)據(jù)就已經(jīng)提交到數(shù)據(jù)庫中了,還必須要用到"MySQLdb.connect"的一個屬性:commit(),將數(shù)據(jù)提交上去,也就是進行了"cur.execute()"操作,要將數(shù)據(jù)提交,必須執(zhí)行:

>>> conn.commit()

再到"mysql>"中運行"select * from users"試一試:

mysql> select * from users;
+----+----------+----------+------------------+
| id | username | password | email            |
+----+----------+----------+------------------+
|  1 | qiwsir   | 123123   | qiwsir@gmail.com |
|  2 | python   | 123456   | python@gmail.com |
+----+----------+----------+------------------+
2 rows in set (0.00 sec)

果然如此。這就如同編寫一個文本一樣,將文字寫到文本上,并不等于文字已經(jīng)保留在文本文件中了,必須執(zhí)行"CTRL-S"才能保存。也就是在通過 Python 操作數(shù)據(jù)庫的時候,以"execute()"執(zhí)行各種 sql 語句之后,要讓已經(jīng)執(zhí)行的效果保存,必須運行連接對象的"commit()"方法。

再嘗試一下插入多條的那個命令"executemany(query,args)".

>>> cur.executemany("insert into users (username,password,email) values (%s,%s,%s)",(("google","111222","g@gmail.com"),("facebook","222333","f@face.book"),("github","333444","git@hub.com"),("docker","444555","doc@ker.com")))
4L
>>> conn.commit()

到"mysql>"里面看結(jié)果:

mysql> select * from users;
+----+----------+----------+------------------+
| id | username | password | email            |
+----+----------+----------+------------------+
|  1 | qiwsir   | 123123   | qiwsir@gmail.com |
|  2 | python   | 123456   | python@gmail.com |
|  3 | google   | 111222   | g@gmail.com      |
|  4 | facebook | 222333   | f@face.book      |
|  5 | github   | 333444   | git@hub.com      |
|  6 | docker   | 444555   | doc@ker.com      |
+----+----------+----------+------------------+
6 rows in set (0.00 sec)

成功插入了多條記錄。在"executemany(query, pseq)"中,query 還是一條 sql 語句,但是 pseq 這時候是一個 tuple,這個 tuple 里面的元素也是 tuple,每個 tuple 分別對應(yīng) sql 語句中的字段列表。這句話其實被執(zhí)行多次。只不過執(zhí)行過程不顯示給我們看罷了。

除了插入命令,其它對數(shù)據(jù)操作的命了都可用類似上面的方式,比如刪除、修改等。

查詢

如果要從數(shù)據(jù)庫中查詢數(shù)據(jù),也用游標方法來操作了。

>>> cur.execute("select * from users")    
7L

這說明從 users 表匯總查詢出來了 7 條記錄。但是,這似乎有點不友好,告訴我 7 條記錄查出來了,但是在哪里呢,如果在'mysql>'下操作查詢命令,一下就把 7 條記錄列出來了。怎么顯示 Python 在這里的查詢結(jié)果呢?

要用到游標對象的 fetchall()、fetchmany(size=None)、fetchone()、scroll(value, mode='relative')等方法。

>>> cur.execute("select * from users")    
7L
>>> lines = cur.fetchall()

到這里,已經(jīng)將查詢到的記錄賦值給變量 lines 了。如果要把它們顯示出來,就要用到曾經(jīng)學(xué)習(xí)過的循環(huán)語句了。

>>> for line in lines:
...     print line
... 
(1L, u'qiwsir', u'123123', u'qiwsir@gmail.com')
(2L, u'python', u'123456', u'python@gmail.com')
(3L, u'google', u'111222', u'g@gmail.com')
(4L, u'facebook', u'222333', u'f@face.book')
(5L, u'github', u'333444', u'git@hub.com')
(6L, u'docker', u'444555', u'doc@ker.com')
(7L, u'\u8001\u9f50', u'9988', u'qiwsir@gmail.com')

很好。果然是逐條顯示出來了。列位注意,第七條中的 u'\u8001\u95f5',這里是漢字,只不過由于我的 shell 不能顯示罷了,不必驚慌,不必搭理它。

只想查出第一條,可以嗎?當然可以!看下面的:

>>> cur.execute("select * from users where id=1")
1L
>>> line_first = cur.fetchone()     #只返回一條
>>> print line_first
(1L, u'qiwsir', u'123123', u'qiwsir@gmail.com')

為了對上述過程了解深入,做下面實驗:

>>> cur.execute("select * from users")
7L
>>> print cur.fetchall()
((1L, u'qiwsir', u'123123', u'qiwsir@gmail.com'), (2L, u'python', u'123456', u'python@gmail.com'), (3L, u'google', u'111222', u'g@gmail.com'), (4L, u'facebook', u'222333', u'f@face.book'), (5L, u'github', u'333444', u'git@hub.com'), (6L, u'docker', u'444555', u'doc@ker.com'), (7L, u'\u8001\u9f50', u'9988', u'qiwsir@gmail.com'))

原來,用 cur.execute() 從數(shù)據(jù)庫查詢出來的東西,被“保存在了 cur 所能找到的某個地方”,要找出這些被保存的東西,需要用 cur.fetchall()(或者 fechone 等),并且找出來之后,做為對象存在。從上面的實驗探討發(fā)現(xiàn),被保存的對象是一個 tuple 中,里面的每個元素,都是一個一個的 tuple。因此,用 for 循環(huán)就可以一個一個拿出來了。

接著看,還有神奇的呢。

接著上面的操作,再打印一遍

>>> print cur.fetchall()
()

暈了!怎么什么是空?不是說做為對象已經(jīng)存在了內(nèi)存中了嗎?難道這個內(nèi)存中的對象是一次有效嗎?

不要著急。

通過游標找出來的對象,在讀取的時候有一個特點,就是那個游標會移動。在第一次操作了 print cur.fetchall() 后,因為是將所有的都打印出來,游標就從第一條移動到最后一條。當 print 結(jié)束之后,游標已經(jīng)在最后一條的后面了。接下來如果再次打印,就空了,最后一條后面沒有東西了。

下面還要實驗,檢驗上面所說:

>>> cur.execute('select * from users')
7L
>>> print cur.fetchone() 
(1L, u'qiwsir', u'123123', u'qiwsir@gmail.com')
>>> print cur.fetchone()
(2L, u'python', u'123456', u'python@gmail.com')
>>> print cur.fetchone()
(3L, u'google', u'111222', u'g@gmail.com')

這次我不一次全部打印出來了,而是一次打印一條,看官可以從結(jié)果中看出來,果然那個游標在一條一條向下移動呢。注意,我在這次實驗中,是重新運行了查詢語句。

那么,既然在操作存儲在內(nèi)存中的對象時候,游標會移動,能不能讓游標向上移動,或者移動到指定位置呢?這就是那個 scroll()

>>> cur.scroll(1)
>>> print cur.fetchone()
(5L, u'github', u'333444', u'git@hub.com')
>>> cur.scroll(-2)
>>> print cur.fetchone()
(4L, u'facebook', u'222333', u'f@face.book')

果然,這個函數(shù)能夠移動游標,不過請仔細觀察,上面的方式是讓游標相對與當前位置向上或者向下移動。即:

cur.scroll(n),或者,cur.scroll(n,"relative"):意思是相對當前位置向上或者向下移動,n 為正數(shù),表示向下(向前),n 為負數(shù),表示向上(向后)

還有一種方式,可以實現(xiàn)“絕對”移動,不是“相對”移動:增加一個參數(shù)"absolute"

特別提醒看官注意的是,在 Python 中,序列對象是的順序是從 0 開始的。

>>> cur.scroll(2,"absolute")    #回到序號是 2,但指向第三條
>>> print cur.fetchone()        #打印,果然是
(3L, u'google', u'111222', u'g@gmail.com')

>>> cur.scroll(1,"absolute")
>>> print cur.fetchone()
(2L, u'python', u'123456', u'python@gmail.com')

>>> cur.scroll(0,"absolute")    #回到序號是 0,即指向 tuple 的第一條
>>> print cur.fetchone()
(1L, u'qiwsir', u'123123', u'qiwsir@gmail.com')

至此,已經(jīng)熟悉了 cur.fetchall() 和 cur.fetchone() 以及 cur.scroll() 幾個方法,還有另外一個,接這上邊的操作,也就是游標在序號是 1 的位置,指向了 tuple 的第二條

>>> cur.fetchmany(3)
((2L, u'Python', u'123456', u'python@gmail.com'), (3L, u'google', u'111222', u'g@gmail.com'), (4L, u'facebook', u'222333', u'f@face.book'))

上面這個操作,就是實現(xiàn)了從當前位置(游標指向 tuple 的序號為 1 的位置,即第二條記錄)開始,含當前位置,向下列出 3 條記錄。

讀取數(shù)據(jù),好像有點啰嗦呀。細細琢磨,還是有道理的。你覺得呢?

不過,Python 總是能夠為我們著想的,在連接對象的游標方法中提供了一個參數(shù),可以實現(xiàn)將讀取到的數(shù)據(jù)變成字典形式,這樣就提供了另外一種讀取方式了。

>>> cur = conn.cursor(cursorclass=MySQLdb.cursors.DictCursor)
>>> cur.execute("select * from users")
7L
>>> cur.fetchall()
({'username': u'qiwsir', 'password': u'123123', 'id': 1L, 'email': u'qiwsir@gmail.com'}, {'username': u'mypython', 'password': u'123456', 'id': 2L, 'email': u'python@gmail.com'}, {'username': u'google', 'password': u'111222', 'id': 3L, 'email': u'g@gmail.com'}, {'username': u'facebook', 'password': u'222333', 'id': 4L, 'email': u'f@face.book'}, {'username': u'github', 'password': u'333444', 'id': 5L, 'email': u'git@hub.com'}, {'username': u'docker', 'password': u'444555', 'id': 6L, 'email': u'doc@ker.com'}, {'username': u'\u8001\u9f50', 'password': u'9988', 'id': 7L, 'email': u'qiwsir@gmail.com'})     

這樣,在元組里面的元素就是一個一個字典:

>>> cur.scroll(0,"absolute")
>>> for line in cur.fetchall():
...     print line["username"]
... 
qiwsir
mypython
google
facebook
github
docker
老齊

根據(jù)字典對象的特點來讀取了“鍵-值”。

更新數(shù)據(jù)

經(jīng)過前面的操作,這個就比較簡單了,不過需要提醒的是,如果更新完畢,和插入數(shù)據(jù)一樣,都需要 commit() 來提交保存。

>>> cur.execute("update users set username=%s where id=2",("mypython"))
1L
>>> cur.execute("select * from users where id=2")
1L
>>> cur.fetchone()
(2L, u'mypython', u'123456', u'python@gmail.com')

從操作中看出來了,已經(jīng)將數(shù)據(jù)庫中第二條的用戶名修改為 myPython 了,用的就是 update 語句。

不過,要真的實現(xiàn)在數(shù)據(jù)庫中更新,還要運行:

>>> conn.commit()

這就大事完吉了。

應(yīng)該還有個小尾巴,那就是當你操作數(shù)據(jù)完畢,不要忘記關(guān)門:

>>> cur.close()
>>> conn.close()

門鎖好了,放心離開。


總目錄   |   上節(jié):mysql數(shù)據(jù)庫(1)   |   下節(jié):mongodb數(shù)據(jù)庫

如果你認為有必要打賞我,請通過支付寶:qiwsir@126.com,不勝感激。