當前位置:首頁 » 數據倉庫 » python3創建資料庫
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

python3創建資料庫

發布時間: 2023-08-16 18:48:54

Ⅰ linux環境下python怎樣操作mysql資料庫

linux環境下python怎樣操作mysql資料庫呢?方法如下:

首先在Linux環境下安裝mysql-python

1、下載mysql-python

打開終端:

cd /usr/local

sudo wgethttp://nchc.dl.sourceforge.net/sourceforge/mysql-python/MySQL-python-1.2.2.tar.gz

官網地址:http://sourceforge.net/projects/mysql-python/

2、解壓

sudo tar -zxvf MySQL-python-1.2.2.tar.gz

cd MySQL-python-1.2.2

3、在安裝前需進行配置

a、修改setup_posix.py中的mysql_config.path為你mysql安裝目錄的mysql_config路徑

b、修改site.cfg中的threadsafe = False,去掉mysql_config前的注釋,並改為mysql_config = /usr/local/mysql/bin/mysql_config

c、執行命令:

export LD_LIBRARY_PATH=/usr/local/mysql/lib/mysql

sudo ln -s /usr/local/mysql/lib/mysql/libmysqlclient.so /usr/lib/libmysqlclient.so.14

sudo ldconfig (這個很重要,否則後面會報錯ImportError: libmysqlclient.so.14: cannot open shared object file)

4、編譯安裝

1)python setup.py build

若未成功的話,需手動安裝setuptools:

sudo apt-get install python-setuptools
2)sudo python setup.py install

5、測試

python

>>> import MySQLdb

沒有錯誤,則表示安裝成功了。

使用python操作MySQL

使用python連接MySQL,創建資料庫,創建表格,插入/查詢數據。python_mysql.py代碼如下:

若出現類似於此的警告:

/usr/local/lib/python2.6/dist-packages/MySQL_python-1.2.2-py2.6-linux-i686.egg/MySQLdb/__init__.py:34: DeprecationWarning: the sets mole is deprecated from sets import ImmutableSet

解決辦法如下:
找到上面路徑MySQLdb下的__init__.py文件
1) 在文件中 "__init__.py"中, 注釋掉:
from sets import ImmutableSet
class DBAPISet(ImmutableSet):
新增:
class DBAPISet(frozenset):


2) 在文件"converters.py"中,注釋掉 from sets import BaseSet, Set 這一句話。

3) 在文件"converters.py"中, 修改 其中的"Set" 成為 "set" ( 只有兩個地方需要修改,即大寫改小寫)

大概 line 45: return Set([ i for i in s.split(',') if i ]) 改為 return set([ i for i in s.split(',') if i ])

大概 line 129: Set: Set2Str, 改為 set: Set2Str,

1.引入MySQLdb庫

import MySQLdb

2.和資料庫建立連接

conn=MySQLdb.connect(host="localhost",user="root",passwd="sa",db="mytable",charset="utf8")

提供的connect方法用來和資料庫建立連接,接收數個參數,返回連接對象.

比較常用的參數包括

host:資料庫主機名.默認是用本地主機.

user:資料庫登陸名.默認是當前用戶.

passwd:資料庫登陸的秘密.默認為空.

db:要使用的資料庫名.沒有默認值.

port:MySQL服務使用的TCP埠.默認是3306.

charset:資料庫編碼.

然後,這個連接對象也提供了對事務操作的支持,標準的方法

commit() 提交

rollback() 回滾

3.執行sql語句和接收返回值

cursor=conn.cursor()

n=cursor.execute(sql,param)

首先,我們用使用連接對象獲得一個cursor對象,接下來,我們會使用cursor提供的方法來進行工作.這些方法包括兩大類:1.執行命令,2.接收返回值

cursor用來執行命令的方法:

callproc(self, procname, args):用來執行存儲過程,接收的參數為存儲過程名和參數列表,返回值為受影響的行數

execute(self, query, args):執行單條sql語句,接收的參數為sql語句本身和使用的參數列表,返回值為受影響的行數

executemany(self, query, args):執行單條sql語句,但是重復執行參數列表裡的參數,返回值為受影響的行數

nextset(self):移動到下一個結果集

cursor用來接收返回值的方法:

fetchall(self):接收全部的返回結果行.

fetchmany(self, size=None):接收size條返回結果行.如果size的值大於返回的結果行的數量,則會返回cursor.arraysize條數據.

fetchone(self):返回一條結果行.

scroll(self, value, mode='relative'):移動指針到某一行.如果mode='relative',則表示從當前所在行移動value條,如果mode='absolute',則表示從結果集的第一行移動value條.

下面的代碼是一個完整的例子.

#使用sql語句,這里要接收的參數都用%s佔位符.要注意的是,無論你要插入的數據是什麼類型,佔位符永遠都要用%s

sql="insert into cdinfo values(%s,%s,%s,%s,%s)"

#param應該為tuple或者list

param=(title,singer,imgurl,url,alpha)

#執行,如果成功,n的值為1

n=cursor.execute(sql,param)

#再來執行一個查詢的操作

cursor.execute("select * from cdinfo")

#我們使用了fetchall這個方法.這樣,cds里保存的將會是查詢返回的全部結果.每條結果都是一個tuple類型的數據,這些tuple組成了一個tuple

cds=cursor.fetchall()

#因為是tuple,所以可以這樣使用結果集

print cds[0][3]

#或者直接顯示出來,看看結果集的真實樣子

print cds

#如果需要批量的插入數據,就這樣做

sql="insert into cdinfo values(0,%s,%s,%s,%s,%s)"

#每個值的集合為一個tuple,整個參數集組成一個tuple,或者list

param=((title,singer,imgurl,url,alpha),(title2,singer2,imgurl2,url2,alpha2))

#使用executemany方法來批量的插入數據.這真是一個很酷的方法!

n=cursor.executemany(sql,param)

4.關閉資料庫連接

需要分別的關閉指針對象和連接對象.他們有名字相同的方法

cursor.close()

conn.close()

以上方法就可以了。

另外,附MySQLdb的相關資料

更多關於MySQLdb的信息可以查這里:http://mysql-python.sourceforge.net/MySQLdb.html

Ⅱ python3.6下如何用Django1.9創建資料庫報錯

錯誤描述
python==3.5
django==1.7
django創建項目時報錯如下:
Traceback (most recent call last):
File "/root/envs/django-test/bin/django-admin", line 11, in <mole>
sys.exit(execute_from_command_line())
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
utility.execute()
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/management/__init__.py", line 354, in execute
django.setup()
File "/root/envs/django-test/lib/python3.5/site-packages/旅弊django/__init__.py", line 18, in setup
from django.utils.log import configure_logging
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/log.py", line 10, in <mole>
from django.views.debug import ExceptionReporter, get_exception_reporter_filter
File "/root/envs/django-test/lib/python3.5/site-packages/django/views/debug.py", line 10, in <mole>
from django.http import (HttpResponse, HttpResponseServerError,
File "/root/envs/django-test/lib/python3.5/site-packages/django/http/__init__.py", line 4, in <mole>
from django.http.response import (HttpResponse, StreamingHttpResponse,
File "/root/envs/django-test/lib/python3.5/site-packages/django/http/response.py", line 13, in <mole>
from django.core.serializers.json import DjangoJSONEncoder
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/serializers/__init__.py", line 23, in <mole>
from django.core.serializers.base import SerializerDoesNotExist
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/serializers/base.py", line 6, in <mole>
from django.db import models
File "/root/envs/django-test/lib/python3.5/site-packages/django/db/models/__init__.py", line 6, in <mole>
from django.db.models.query import Q, QuerySet, Prefetch # NOQA
File "/root/envs/django-test/lib/python3.5/site-packages/django/db/models/query.py", line 13, in <姿冊mole>
from django.db.models.fields import AutoField, Empty
File "跡鎮宏/root/envs/django-test/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 18, in <mole>
from django import forms
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/__init__.py", line 6, in <mole>
from django.forms.fields import * # NOQA
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/fields.py", line 18, in <mole>
from django.forms.utils import from_current_timezone, to_current_timezone
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/utils.py", line 15, in <mole>
from django.utils.html import format_html, format_html_join, escape
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/html.py", line 16, in <mole>
from .html_parser import HTMLParser, HTMLParseError
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/html_parser.py", line 12, in <mole>
HTMLParseError = _html_parser.HTMLParseError
AttributeError: mole 'html.parser' has no attribute 'HTMLParseError'
(django-test) root@localhost:~/source/djangotest# django-admin startproject superlists
Traceback (most recent call last):
File "/root/envs/django-test/bin/django-admin", line 11, in <mole>
sys.exit(execute_from_command_line())
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
utility.execute()
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/management/__init__.py", line 354, in execute
django.setup()
File "/root/envs/django-test/lib/python3.5/site-packages/django/__init__.py", line 18, in setup
from django.utils.log import configure_logging
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/log.py", line 10, in <mole>
from django.views.debug import ExceptionReporter, get_exception_reporter_filter
File "/root/envs/django-test/lib/python3.5/site-packages/django/views/debug.py", line 10, in <mole>
from django.http import (HttpResponse, HttpResponseServerError,
File "/root/envs/django-test/lib/python3.5/site-packages/django/http/__init__.py", line 4, in <mole>
from django.http.response import (HttpResponse, StreamingHttpResponse,
File "/root/envs/django-test/lib/python3.5/site-packages/django/http/response.py", line 13, in <mole>
from django.core.serializers.json import DjangoJSONEncoder
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/serializers/__init__.py", line 23, in <mole>
from django.core.serializers.base import SerializerDoesNotExist
File "/root/envs/django-test/lib/python3.5/site-packages/django/core/serializers/base.py", line 6, in <mole>
from django.db import models
File "/root/envs/django-test/lib/python3.5/site-packages/django/db/models/__init__.py", line 6, in <mole>
from django.db.models.query import Q, QuerySet, Prefetch # NOQA
File "/root/envs/django-test/lib/python3.5/site-packages/django/db/models/query.py", line 13, in <mole>
from django.db.models.fields import AutoField, Empty
File "/root/envs/django-test/lib/python3.5/site-packages/django/db/models/fields/__init__.py", line 18, in <mole>
from django import forms
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/__init__.py", line 6, in <mole>
from django.forms.fields import * # NOQA
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/fields.py", line 18, in <mole>
from django.forms.utils import from_current_timezone, to_current_timezone
File "/root/envs/django-test/lib/python3.5/site-packages/django/forms/utils.py", line 15, in <mole>
from django.utils.html import format_html, format_html_join, escape
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/html.py", line 16, in <mole>
from .html_parser import HTMLParser, HTMLParseError
File "/root/envs/django-test/lib/python3.5/site-packages/django/utils/html_parser.py", line 12, in <mole>
HTMLParseError = _html_parser.HTMLParseError
AttributeError: mole 'html.parser' has no attribute 'HTMLParseError'5767757677

原因
HTMLParseError在pythons3.5已經沒有了
解決方法
將python版本回退到3.3或3.4
升級django版本

Ⅲ 如何用python寫sql

python可以利用pymysql模塊操作資料庫。

什麼是 PyMySQL?

PyMySQL 是在 Python3.x 版本中用於連接 MySQL 伺服器的一個庫,Python2中則使用mysqldb。

PyMySQL 遵循 Python 資料庫 API v2.0 規范,並包含了 pure-Python MySQL 客戶端庫。

PyMySQL 安裝

在使用 PyMySQL 之前,我們需要確保 PyMySQL 已安裝。

PyMySQL 下載地址:https://github.com/PyMySQL/PyMySQL。

如果還未安裝,我們可以使用以下命令安裝最新版的 PyMySQL:

$ pip3 install PyMySQL

如果你的系統不支持 pip 命令,可以使用以下方式安裝:

1、使用 git 命令下載安裝包安裝(你也可以手動下載):

$ git clone https://github.com/PyMySQL/PyMySQL$ cd PyMySQL/$ python3 setup.py install

2、如果需要制定版本號,可以使用 curl 命令來安裝:

$ # X.X 為 PyMySQL 的版本號$ curl -L https://github.com/PyMySQL/PyMySQL/tarball/pymysql-X.X | tar xz$ cd PyMySQL*$ python3 setup.py install
$ # 現在你可以刪除 PyMySQL* 目錄

注意:請確保您有root許可權來安裝上述模塊。

安裝的過程中可能會出現"ImportError: No mole named setuptools"的錯誤提示,意思是你沒有安裝setuptools,你可以訪問https://pypi.python.org/pypi/setuptools找到各個系統的安裝方法。

Linux 系統安裝實例:

$ wget https://bootstrap.pypa.io/ez_setup.py$ python3 ez_setup.py

資料庫連接

連接資料庫前,請先確認以下事項:

  • 您已經創建了資料庫 TESTDB.

  • 在TESTDB資料庫中您已經創建了表 EMPLOYEE

  • EMPLOYEE表欄位為 FIRST_NAME, LAST_NAME, AGE, SEX 和 INCOME。

  • 連接資料庫TESTDB使用的用戶名為 "testuser" ,密碼為 "test123",你可以可以自己設定或者直接使用root用戶名及其密碼,Mysql資料庫用戶授權請使用Grant命令。

  • 在你的機子上已經安裝了 Python MySQLdb 模塊。

  • 如果您對sql語句不熟悉,可以訪問我們的SQL基礎教程

  • 實例:

    以下實例鏈接 Mysql 的 TESTDB 資料庫:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用 cursor() 方法創建一個游標對象 cursorcursor = db.cursor()
    # 使用 execute() 方法執行 SQL 查詢 cursor.execute("SELECT VERSION()")
    # 使用 fetchone() 方法獲取單條數據.data = cursor.fetchone()
    print ("Database version : %s " % data)
    # 關閉資料庫連接db.close()

    執行以上腳本輸出結果如下:

  • Database version : 5.5.20-log

  • 創建資料庫表

    如果資料庫連接存在我們可以使用execute()方法來為資料庫創建表,如下所示創建表EMPLOYEE:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用 cursor() 方法創建一個游標對象 cursorcursor = db.cursor()
    # 使用 execute() 方法執行 SQL,如果表存在則刪除cursor.execute("DROP TABLE IF EXISTS EMPLOYEE")
    # 使用預處理語句創建表sql = """CREATE TABLE EMPLOYEE (
    FIRST_NAME CHAR(20) NOT NULL,
    LAST_NAME CHAR(20),
    AGE INT,
    SEX CHAR(1),
    INCOME FLOAT )"""
    cursor.execute(sql)
    # 關閉資料庫連接db.close()

    資料庫插入操作

    以下實例使用執行 SQL INSERT 語句向表 EMPLOYEE 插入記錄:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用cursor()方法獲取操作游標 cursor = db.cursor()
    # SQL 插入語句sql = """INSERT INTO EMPLOYEE(FIRST_NAME,
    LAST_NAME, AGE, SEX, INCOME)
    VALUES ('Mac', 'Mohan', 20, 'M', 2000)"""try: # 執行sql語句
    cursor.execute(sql)
    # 提交到資料庫執行
    db.commit()except: # 如果發生錯誤則回滾
    db.rollback()
    # 關閉資料庫連接db.close()

    以上例子也可以寫成如下形式:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用cursor()方法獲取操作游標 cursor = db.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語句
    db.commit()except: # 發生錯誤時回滾
    db.rollback()
    # 關閉資料庫連接db.close()

    以下代碼使用變數向SQL語句中傳遞參數:

  • ..................................user_id = "test123"password = "password"con.execute('insert into Login values( %s, %s)' % (user_id, password))..................................

  • 資料庫查詢操作

    Python查詢Mysql使用 fetchone() 方法獲取單條數據, 使用fetchall() 方法獲取多條數據。

  • fetchone():該方法獲取下一個查詢結果集。結果集是一個對象

  • fetchall():接收全部的返回結果行.

  • rowcount:這是一個只讀屬性,並返回執行execute()方法後影響的行數。

  • 實例:

    查詢EMPLOYEE表中salary(工資)欄位大於1000的所有數據:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用cursor()方法獲取操作游標 cursor = db.cursor()
    # SQL 查詢語句sql = "SELECT * FROM EMPLOYEE
    WHERE INCOME > %s" % (1000)try: # 執行SQL語句
    cursor.execute(sql)
    # 獲取所有記錄列表
    results = cursor.fetchall()
    for row in results: fname = row[0]
    lname = row[1]
    age = row[2]
    sex = row[3]
    income = row[4]
    # 列印結果
    print ("fname=%s,lname=%s,age=%s,sex=%s,income=%s" % (fname, lname, age, sex, income ))except: print ("Error: unable to fetch data")
    # 關閉資料庫連接db.close()

    以上腳本執行結果如下:

  • fname=Mac, lname=Mohan, age=20, sex=M, income=2000

  • 資料庫更新操作

    更新操作用於更新數據表的的數據,以下實例將 TESTDB 表中 SEX 為 'M' 的 AGE 欄位遞增 1:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用cursor()方法獲取操作游標 cursor = db.cursor()
    # SQL 更新語句sql = "UPDATE EMPLOYEE SET AGE = AGE + 1 WHERE SEX = '%c'" % ('M')try: # 執行SQL語句
    cursor.execute(sql)
    # 提交到資料庫執行
    db.commit()except: # 發生錯誤時回滾
    db.rollback()
    # 關閉資料庫連接db.close()

    刪除操作

    刪除操作用於刪除數據表中的數據,以下實例演示了刪除數據表 EMPLOYEE 中 AGE 大於 20 的所有數據:

    實例(Python 3.0+)

    #!/usr/bin/python3
    import pymysql
    # 打開資料庫連接db = pymysql.connect("localhost","testuser","test123","TESTDB" )
    # 使用cursor()方法獲取操作游標 cursor = db.cursor()
    # SQL 刪除語句sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)try: # 執行SQL語句
    cursor.execute(sql)
    # 提交修改
    db.commit()except: # 發生錯誤時回滾
    db.rollback()
    # 關閉連接db.close()

    執行事務

    事務機制可以確保數據一致性。

    事務應該具有4個屬性:原子性、一致性、隔離性、持久性。這四個屬性通常稱為ACID特性。

  • 原子性(atomicity)。一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。

  • 一致性(consistency)。事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。

  • 隔離性(isolation)。一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對並發的其他事務是隔離的,並發執行的各個事務之間不能互相干擾。

  • 持久性(rability)。持續性也稱永久性(permanence),指一個事務一旦提交,它對資料庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

  • Python DB API 2.0 的事務提供了兩個方法 commit 或 rollback。

    實例

    實例(Python 3.0+)

    # SQL刪除記錄語句sql = "DELETE FROM EMPLOYEE WHERE AGE > %s" % (20)try: # 執行SQL語句
    cursor.execute(sql)
    # 向資料庫提交
    db.commit()except: # 發生錯誤時回滾
    db.rollback()

    對於支持事務的資料庫, 在Python資料庫編程中,當游標建立之時,就自動開始了一個隱形的資料庫事務。

    commit()方法游標的所有更新操作,rollback()方法回滾當前游標的所有操作。每一個方法都開始了一個新的事務。

    錯誤處理

    DB API中定義了一些資料庫操作的錯誤及異常,下表列出了這些錯誤和異常:

    異常

    描述

    Warning 當有嚴重警告時觸發,例如插入數據是被截斷等等。必須是 StandardError 的子類。

    Error 警告以外所有其他錯誤類。必須是 StandardError 的子類。

    InterfaceError 當有資料庫介面模塊本身的錯誤(而不是資料庫的錯誤)發生時觸發。 必須是Error的子類。

    DatabaseError 和資料庫有關的錯誤發生時觸發。 必須是Error的子類。

    DataError 當有數據處理時的錯誤發生時觸發,例如:除零錯誤,數據超范圍等等。 必須是DatabaseError的子類。

    OperationalError 指非用戶控制的,而是操作資料庫時發生的錯誤。例如:連接意外斷開、 資料庫名未找到、事務處理失敗、內存分配錯誤等等操作資料庫是發生的錯誤。 必須是DatabaseError的子類。

    IntegrityError 完整性相關的錯誤,例如外鍵檢查失敗等。必須是DatabaseError子類。

    InternalError 資料庫的內部錯誤,例如游標(cursor)失效了、事務同步失敗等等。 必須是DatabaseError子類。

    ProgrammingError 程序錯誤,例如數據表(table)沒找到或已存在、SQL語句語法錯誤、 參數數量錯誤等等。必須是DatabaseError的子類。

    NotSupportedError 不支持錯誤,指使用了資料庫不支持的函數或API等。例如在連接對象上 使用.rollback()函數,然而資料庫並不支持事務或者事務已關閉。 必須是DatabaseError的子類。

Ⅳ python能創建sql server資料庫,執行create database語句嗎

應該可行吧,畢竟django可以通過第三方支持MSSQL的。

Ⅳ 如何用 Python 實現一個圖資料庫(Graph Database)

本文章是 重寫 500 Lines or Less 系列的其中一篇,目標是重寫 500 Lines or Less 系列的原有項目:Dagoba: an in-memory graph database。

Dagoba 是作者設計用來展示如何從零開始自己實現一個圖資料庫( Graph Database )。該名字似乎來源於作者喜歡的一個樂隊,另一個原因是它的前綴 DAG 也正好是有向無環圖 ( Directed Acyclic Graph ) 的縮寫。本文也沿用了該名稱。

圖是一種常見的數據結構,它將信息描述為若干獨立的節點( vertex ,為了和下文的邊更加對稱,本文中稱為 node ),以及把節點關聯起來的邊( edge )。我們熟悉的鏈表以及多種樹結構可以看作是符合特定規則的圖。圖在路徑選擇、推薦演算法以及神經網路等方面都是重要的核心數據結構。

既然圖的用途如此廣泛,一個重要的問題就是如何存儲它。如果在傳統的關系資料庫中存儲圖,很自然的做法就是為節點和邊各自創建一張表,並用外鍵把它們關聯起來。這樣的話,要查找某人所有的子女,就可以寫下類似下面的查詢:

還好,不算太復雜。但是如果要查找孫輩呢?那恐怕就要使用子查詢或者 CTE(Common Table Expression) 等特殊構造了。再往下想,曾孫輩又該怎麼查詢?孫媳婦呢?

這樣我們會意識到,SQL 作為查詢語言,它只是對二維數據表這種結構而設計的,用它去查詢圖的話非常笨拙,很快會變得極其復雜,也難以擴展。針對圖而言,我們希望有一種更為自然和直觀的查詢語法,類似這樣:

為了高效地存儲和查詢圖這種數據結構,圖資料庫( Graph Database )應運而生。因為和傳統的關系型資料庫存在極大的差異,所以它屬於新型資料庫也就是 NoSql 的一個分支(其他分支包括文檔資料庫、列資料庫等)。圖資料庫的主要代表包括 Neo4J 等。本文介紹的 Dagoba 則是具備圖資料庫核心功能、主要用於教學和演示的一個簡單的圖資料庫。

原文代碼是使用 JavaScript 編寫的,在定義調用介面時大量使用了原型( prototype )這種特有的語言構造。對於其他主流語言的用戶來說,原型的用法多少顯得有些別扭和不自然。

考慮到本系列其他資料庫示例大多是用 Python 實現的,本文也按照傳統,用 Python 重寫了原文的代碼。同樣延續之前的慣例,為了讓讀者更好地理解程序是如何逐步完善的,我們用迭代式的方法完成程序的各個組成部分。

原文在 500lines 系列的 Github 倉庫中只包含了實現代碼,並未包含測試。按照代碼注釋說明,測試程序位於作者的另一個代碼庫中,不過和 500lines 版本的實現似乎略有不同。

本文實現的代碼參考了原作者的測試內容,但跳過了北歐神話這個例子——我承認確實不熟悉這些神祇之間的親緣關系,相信中文背景的讀者們多數也未必了解,雖然作者很喜歡這個例子,想了想還是不要徒增困惑吧。因此本文在編寫測試用例時只參考了原文關於家族親屬的例子,放棄了神話相關的部分,盡管會減少一些趣味性,相信對於入門級的代碼來說這樣也夠用了。

本文實現程序位於代碼庫的 dagoba 目錄下。按照本系列程序的同意規則,要想直接執行各個已完成的步驟,讀者可以在根目錄下的 main.py 找到相應的代碼位置,取消注釋並運行即可。

本程序的所有步驟只需要 Python3 ,測試則使用內置的 unittest , 不需要額外的第三方庫。原則上 Python3.6 以上版本應該都可運行,但我只在 Python3.8.3 環境下完整測試過。

本文實現的程序從最簡單的案例開始,通過每個步驟逐步擴展,最終形成一個完整的程序。這些步驟包括:

接下來依次介紹各個步驟。

回想一下,圖資料庫就是一些點( node )和邊( edge )的集合。現在我們要做出的一個重大決策是如何對節點/邊進行建模。對於邊來說,必須指定它的關聯關系,也就是從哪個節點指向哪個節點。大多數情況下邊是有方向的——父子關系不指明方向可是要亂套的!

考慮到擴展性及通用性問題,我們可以把數據保存為字典( dict ),這樣可以方便地添加用戶需要的任何數據。某些數據是為資料庫內部管理而保留的,為了明確區分,可以這樣約定:以下劃線開頭的特殊欄位由資料庫內部維護,類似於私有成員,用戶不應該自己去修改它們。這也是 Python 社區普遍遵循的約定。

此外,節點和邊存在互相引用的關系。目前我們知道邊會引用到兩端的節點,後面還會看到,為了提高效率,節點也會引用到邊。如果僅僅在內存中維護它們的關系,那麼使用指針訪問是很直觀的,但資料庫必須考慮到序列化到磁碟的問題,這時指針就不再好用了。

為此,最好按照資料庫的一般要求,為每個節點維護一個主鍵( _id ),用主鍵來描述它們之間的關聯關系。

我們第一步要把資料庫的模型建立起來。為了測試目的,我們使用一個最簡單的資料庫模型,它只包含兩個節點和一條邊,如下所示:

按照 TDD 的原則,首先編寫測試:

與原文一樣,我們把資料庫管理介面命名為 Dagoba 。目前,能夠想到的最簡單的測試是確認節點和邊是否已經添加到資料庫中:

assert_item 是一個輔助方法,用於檢查字典是否包含預期的欄位。相信大家都能想到該如何實現,這里就不再列出了,讀者可參考 Github 上的完整源碼。

現在,測試是失敗的。用最簡單的辦法實現資料庫:

需要注意的是,不管添加節點還是查詢,程序都使用了拷貝後的數據副本,而不是直接使用原始數據。為什麼要這樣做?因為字典是可變的,用戶可以在任何時候修改其中的內容,如果資料庫不知道數據已經變化,就很容易發生難以追蹤的一致性問題,最糟糕的情況下會使得數據內容徹底混亂。

拷貝數據可以避免上述問題,代價則是需要佔用更多內存和處理時間。對於資料庫來說,通常查詢次數要遠遠多於修改,所以這個代價是可以接受的。

現在測試應該正常通過了。為了讓它更加完善,我們可以再測試一些邊緣情況,看看資料庫能否正確處理異常數據,比如:

例如,如果用戶嘗試添加重復主鍵,我們預期應拋出 ValueError 異常。因此編寫測試如下:

為了滿足以上測試,代碼需要稍作修改。特別是按照 id 查找主鍵是個常用操作,通過遍歷的方法效率太低了,最好是能夠通過主鍵直接訪問。因此在資料庫中再增加一個字典:

完整代碼請參考 Github 倉庫。

在上個步驟,我們在初始化資料庫時為節點明確指定了主鍵。按照資料庫設計的一般原則,主鍵最好是不具有業務含義的代理主鍵( Surrogate key ),用戶不應該關心它具體的值是什麼,因此讓資料庫去管理主鍵通常是更為合理的。當然,在部分場景下——比如導入外部數據——明確指定主鍵仍然是有用的。

為了同時支持這些要求,我們這樣約定:欄位 _id 表示節點的主鍵,如果用戶指定了該欄位,則使用用戶設置的值(當然,用戶有責任保證它們不會重復);否則,由資料庫自動為它分配一個主鍵。

如果主鍵是資料庫生成的,事先無法預知它的值是什麼,而邊( edge )必須指定它所指向的節點,因此必須在主鍵生成後才能添加。由於這個原因,在動態生成主鍵的情況下,資料庫的初始化會略微復雜一些。還是先寫一個測試:

為支持此功能,我們在資料庫中添加一個內部欄位 _next_id 用於生成主鍵,並讓 add_node 方法返回新生成的主鍵:

接下來,再確認一下邊是否可以正常訪問:

運行測試,一切正常。這個步驟很輕松地完成了,不過兩個測試( DbModelTest 和 PrimaryKeyTest )出現了一些重復代碼,比如 get_item 。我們可以把這些公用代碼提取出來。由於 get_item 內部調用了 TestCase.assertXXX 等方法,看起來應該使用繼承,但從 TestCase 派生基類容易引起一些潛在的問題,所以我轉而使用另一個技巧 Mixin :

實現資料庫模型之後,接下來就要考慮如何查詢它了。

在設計查詢時要考慮幾個問題。對於圖的訪問來說,幾乎總是由某個節點(或符合條件的某一類節點)開始,從與它相鄰的邊跳轉到其他節點,依次類推。所以鏈式調用對查詢來說是一種很自然的風格。舉例來說,要知道 Tom 的孫子養了幾只貓,可以使用類似這樣的查詢:

可以想像,以上每個方法都應該返回符合條件的節點集合。這種實現是很直觀的,不過存在一個潛在的問題:很多時候用戶只需要一小部分結果,如果它總是不計代價地給我們一個巨大的集合,會造成極大的浪費。比如以下查詢:

為了避免不必要的浪費,我們需要另外一種機制,也就是通常所稱的「懶式查詢」或「延遲查詢」。它的基本思想是,當我們調用查詢方法時,它只是把查詢條件記錄下來,而並不立即返回結果,直到明確調用某些方法時才真正去查詢資料庫。

如果讀者比較熟悉流行的 Python ORM,比如 SqlAlchemy 或者 Django ORM 的話,會知道它們幾乎都是懶式查詢的,要調用 list(result) 或者 result[0:10] 這樣的方法才能得到具體的查詢結果。

在 Dagoba 中把觸發查詢的方法定義為 run 。也就是說,以下查詢執行到 run 時才真正去查找數據:

和懶式查詢( Lazy Query )相對應的,直接返回結果的方法一般稱作主動查詢( Eager Query )。主動查詢和懶式查詢的內在查找邏輯基本上是相同的,區別只在於觸發機制不同。由於主動查詢實現起來更加簡單,出錯也更容易排查,因此我們先從主動查詢開始實現。

還是從測試開始。前面測試所用的簡單資料庫數據太少,難以滿足查詢要求,所以這一步先來創建一個更復雜的數據模型:

此關系的復雜之處之一在於反向關聯:如果 A 是 B 的哥哥,那麼 B 就是 A 的弟弟/妹妹,為了查詢到他們彼此之間的關系,正向關聯和反向關聯都需要存在,因此在初始化資料庫時需要定義的邊數量會很多。

當然,父子之間也存在反向關聯的問題,為了讓問題稍微簡化一些,我們目前只需要向下(子孫輩)查找,可以稍微減少一些關聯數量。

因此,我們定義數據模型如下。為了減少重復工作,我們通過 _backward 欄位定義反向關聯,而資料庫內部為了查詢方便,需要把它維護成兩條邊:

然後,測試一個最簡單的查詢,比如查找某人的所有孫輩:

這里 outcome/income 分別表示從某個節點出發、或到達它的節點集合。在原作者的代碼中把上述方法稱為 out/in 。當然這樣看起來更加簡潔,可惜的是 in 在 Python 中是個關鍵字,無法作為函數名。我也考慮過加個下劃線比如 out_.in_ 這種形式,但看起來也有點怪異,權衡之後還是使用了稍微啰嗦一點的名稱。

現在我們可以開始定義查詢介面了。在前面已經說過,我們計劃分別實現兩種查詢,包括主動查詢( Eager Query )以及延遲查詢( Lazy Query )。

它們的內在查詢邏輯是相通的,看起來似乎可以使用繼承。不過遵循 YAGNI 原則,目前先不這樣做,而是只定義兩個新類,在滿足測試的基礎上不斷擴展。以後我們會看到,與繼承相比,把共同的邏輯放到資料庫本身其實是更為合理的。

接下來實現訪問節點的方法。由於 EagerQuery 調用查詢方法會立即返回結果,我們把結果記錄在 _result 內部欄位中。雖然 node 方法只返回單個結果,但考慮到其他查詢方法幾乎都是返回集合,為統一起見,讓它也返回集合,這樣可以避免同時支持集合與單結果的分支處理,讓代碼更加簡潔、不容易出錯。此外,如果查詢對象不存在的話,我們只返回空集合,並不視為一個錯誤。

查詢輸入/輸出節點的方法實現類似這樣:

查找節點的核心邏輯在資料庫本身定義:

以上使用了內部定義的一些輔助查詢方法。用類似的邏輯再定義 income ,它們的實現都很簡單,讀者可以直接參考源碼,此處不再贅述。

在此步驟的最後,我們再實現一個優化。當多次調用查詢方法後,結果可能會返回重復的數據,很多時候這是不必要的。就像關系資料庫通常支持 unique/distinct 一樣,我們也希望 Dagoba 能夠過濾重復的數據。

假設我們要查詢某人所有孩子的祖父,顯然不管有多少孩子,他們的祖父應該是同一個人。因此編寫測試如下:

現在來實現 unique 。我們只要按照主鍵把重復數據去掉即可:

在上個步驟,初始化資料庫指定了雙向關聯,但並未測試它們。因為我們還沒有編寫代碼去支持它們,現在增加一個測試,它應該是失敗的:

運行測試,的確失敗了。我們看看要如何支持它。回想一下,當從邊查找節點時,使用的是以下方法:

這里也有一個潛在的問題:調用 self.edges 意味著遍歷所有邊,當資料庫內容較多時,這是巨大的浪費。為了提高性能,我們可以把與節點相關的邊記錄在節點本身,這樣要查找邊只要看節點本身即可。在初始化時定義出入邊的集合:

在添加邊時,我們要同時把它們對應的關系同時更新到節點,此外還要維護反向關聯。這涉及對字典內容的部分復制,先編寫一個輔助方法:

然後,將添加邊的實現修改如下:

這里的代碼同時添加正向關聯和反向關聯。有的朋友可能會注意到代碼略有重復,是的,但是重復僅出現在該函數內部,本著「三則重構」的原則,暫時不去提取代碼。

實現之後,前面的測試就可以正常通過了。

在這個步驟中,我們來實現延遲查詢( Lazy Query )。

延遲查詢的要求是,當調用查詢方法時並不立即執行,而是推遲到調用特定方法,比如 run 時才執行整個查詢,返回結果。

延遲查詢的實現要比主動查詢復雜一些。為了實現延遲查詢,查詢方法的實現不能直接返回結果,而是記錄要執行的動作以及傳入的參數,到調用 run 時再依次執行前面記錄下來的內容。

如果你去看作者的實現,會發現他是用一個數據結構記錄執行操作和參數,此外還有一部分邏輯用來分派對每種結構要執行的動作。這樣當然是可行的,但數據處理和分派部分的實現會比較復雜,也容易出錯。

本文的實現則選擇了另外一種不同的方法:使用 Python 的內部函數機制,把一連串查詢變換成一組函數,每個函數取上個函數的執行結果作為輸入,最後一個函數的輸出就是整個查詢的結果。由於內部函數同時也是閉包,盡管每個查詢的參數形式各不相同,但是它們都可以被閉包「捕獲」而成為內部變數,所以這些內部函數可以採用統一的形式,無需再針對每種查詢設計額外的數據結構,因而執行過程得到了很大程度的簡化。

首先還是來編寫測試。 LazyQueryTest 和 EagerQueryTest 測試用例幾乎是完全相同的(是的,兩種查詢只在於內部實現機制不同,它們的調用介面幾乎是完全一致的)。

因此我們可以把 EagerQueryTest 的測試原樣不變拷貝到 LazyQueryTest 中。當然拷貝粘貼不是個好注意,對於比較冗長而固定的初始化部分,我們可以把它提取出來作為兩個測試共享的公共函數。讀者可參考代碼中的 step04_lazy_query/tests/test_lazy_query.py 部分。

程序把查詢函數的串列執行稱為管道( pipeline ),用一個變數來記錄它:

然後依次實現各個調用介面。每種介面的實現都是類似的:用內部函數執行真正的查詢邏輯,再把這個函數添加到 pipeline 調用鏈中。比如 node 的實現類似下面:

其他介面的實現也與此類似。最後, run 函數負責執行所有查詢,返回最終結果;

完成上述實現後執行測試,確保我們的實現是正確的。

在前面我們說過,延遲查詢與主動查詢相比,最大的優勢是對於許多查詢可以按需要訪問,不需要每個步驟都返回完整結果,從而提高性能,節約查詢時間。比如說,對於下面的查詢:

以上查詢的意思是從孫輩中找到一個符合條件的節點即可。對該查詢而言,主動查詢會在調用 outcome('son') 時就遍歷所有節點,哪怕最後一步只需要第一個結果。而延遲查詢為了提高效率,應在找到符合條件的結果後立即停止。

目前我們尚未實現 take 方法。老規矩,先添加測試:

主動查詢的 take 實現比較簡單,我們只要從結果中返回前 n 條記錄:

延遲查詢的實現要復雜一些。為了避免不必要的查找,返回結果不應該是完整的列表( list ),而應該是個按需返回的可迭代對象,我們用內置函數 next 來依次返回前 n 個結果:

寫完後運行測試,確保它們是正確的。

從外部介面看,主動查詢和延遲查詢幾乎是完全相同的,所以用單純的數據測試很難確認後者的效率一定比前者高,用訪問時間來測試也並不可靠。為了測試效率,我們引入一個節點訪問次數的概念,如果延遲查詢效率更高的話,那麼它應該比主動查詢訪問節點的次數更少。

為此,編寫如下測試:

我們為 Dagoba 類添加一個成員來記錄總的節點訪問次數,以及兩個輔助方法,分別用於獲取和重置訪問次數:

然後瀏覽代碼,查找修改點。增加計數主要在從邊查找節點的時候,因此修改部分如下:

此外還有 income/outcome 方法,修改都很簡單,這里就不再列出。

實現後再次運行測試。測試通過,表明延遲查詢確實在效率上優於主動查詢。

不像關系資料庫的結構那樣固定,圖的形式可以千變萬化,查詢機制也必須足夠靈活。從原理上講,所有查詢無非是從某個節點出發按照特定方向搜索,因此用 node/income/outcome 這三個方法幾乎可以組合出任意所需的查詢。

但對於復雜查詢,寫出的代碼有時會顯得較為瑣碎和冗長,對於特定領域來說,往往存在更為簡潔的名稱,例如:母親的兄弟可簡稱為舅舅。對於這些場景,如果能夠類似 DSL (領域特定語言)那樣允許用戶根據專業要求自行擴展,從而簡化查詢,方便閱讀,無疑會更為友好。

如果讀者去看原作者的實現,會發現他是用一種特殊語法 addAlias 來定義自己想要的查詢,調用方法時再進行查詢以確定要執行的內容,其介面和內部實現都是相當復雜的。

而我希望有更簡單的方法來實現這一點。所幸 Python 是一種高度動態的語言,允許在運行時向類中增加新的成員,因此做到這一點可能比預想的還要簡單。

為了驗證這一點,編寫測試如下:

無需 Dagoba 的實現做任何改動,測試就可以通過了!其實我們要做的就是動態添加一個自定義的成員函數,按照 Python 對象機制的要求,成員函數的第一個成員應該是名為 self 的參數,但這里已經是在 UnitTest 的內部,為了和測試類本身的 self 相區分,新函數的參數增加了一個下劃線。

此外,函數應返回其所屬的對象,這是為了鏈式調用所要求的。我們看到,動態語言的靈活性使得添加新語法變得非常簡單。

到此,一個初具規模的圖資料庫就形成了。

和原文相比,本文還缺少一些內容,比如如何將資料庫序列化到磁碟。不過相信讀者都看到了,我們的資料庫內部結構基本上是簡單的原生數據結構(列表+字典),因此序列化無論用 pickle 或是 JSON 之類方法都應該是相當簡單的。有興趣的讀者可以自行完成它們。

我們的圖資料庫實現為了提高查詢性能,在節點內部存儲了邊的指針(或者說引用)。這樣做的好處是,無論資料庫有多大,從一個節點到相鄰節點的訪問是常數時間,因此數據訪問的效率非常高。

但一個潛在的問題是,如果資料庫規模非常大,已經無法整個放在內存中,或者出於安全性等原因要實現分布式訪問的話,那麼指針就無法使用了,必須要考慮其他機制來解決這個問題。分布式資料庫無論採用何種數據模型都是一個棘手的問題,在本文中我們沒有涉及。有興趣的讀者也可以考慮 500lines 系列中關於分布式和集群演算法的其他一些文章。

本文的實現和系列中其他資料庫類似,採用 Python 作為實現語言,而原作者使用的是 JavaScript ,這應該和作者的背景有關。我相信對於大多數開發者來說, Python 的對象機制比 JavaScript 基於原型的語法應該是更容易閱讀和理解的。

當然,原作者的版本比本文版本在實現上其實是更為完善的,靈活性也更好。如果想要更為優雅的實現,我們可以考慮使用 Python 元編程,那樣會更接近於作者的實現,但也會讓程序的復雜性大為增加。如果讀者有興趣,不妨對照著去讀讀原作者的版本。

Ⅵ 掌握Python 操作 MySQL 資料庫

本文Python 操作 MySQL 資料庫需要是使用到 PyMySQL 驅動

Python 操作 MySQL 前提是要安裝好 MySQL 資料庫並能正常連接使用,安裝步驟詳見下文。

注意: 安裝過程我們需要通過開啟管理員許可權來安裝,否則會由於許可權不足導致無法安裝。

首先需要先下載 MySQL 安裝包, 官網下載地址 下載對應版本即可,或直接在網上拉取並安裝:

許可權設置:

初始化 MySQL:

啟動 MySQL:

查看 MySQL 運行狀態:

Mysql安裝成功後,默認的root用戶密碼為空,你可以使用以下命令來創建root用戶的密碼:

登陸:

創建資料庫:

查看資料庫:

PyMySQL 模塊使用 pip命令進行安裝:

假如系統不支持 pip 命令,可以使用以下方式安裝:

pymysql .connect 函數:連接上資料庫

輸出結果顯示如下:表面資料庫連接成功

使用 pymysql 的 connect() 方法連接資料庫,connect 參數解釋如下:

conn.cursor():獲取游標

如果要操作資料庫,光連接數據是不夠的,咱們必須拿到操作資料庫的游標,才能進行後續的操作,游標的主要作用是用來接收資料庫操作後的返回結果,比如讀取數據、添加數據。通過獲取到的資料庫連接實例 conn 下的 cursor() 方法來創建游標,實例如下:

輸出結果為:

cursor 返回一個游標實例對象,其中包含了很多操作數據的方法,如執行sql語句,sql 執行命令: execute() 和 executemany()

execute(query,args=None):


executemany(query,args=None):

其他游標對象如下表:

完整資料庫連接操作實例如下:

以上結果輸出為:

創建表代碼如下:

如下所示資料庫表創建成功:

插入數據實現代碼:

插入數據結果:

Python查詢Mysql使用 fetchone() 方法獲取單條數據, 使用fetchall() 方法獲取多條數據。

查詢數據代碼如下:

輸出結果:

DB API中定義了一些資料庫操作的錯誤及異常,下表列出了這些錯誤和異常:

本文給大家介紹 Python 如何連接 Mysql 進行數據的增刪改查操作,文章通過簡潔的代碼方式進行示例演示,給使用 Python 操作 Mysql 的工程師提供支撐。