Google App Engine中使用資料庫
http://blog.sina.com.cn/s/blog_53a802e90100n5id.html
Google App Engine的教程終於來到了資料庫部分。這是GAE最有用、最複雜,也是限制最多的地方。
閱讀本文需要您懂一般的資料庫使用。
廢話少說,先給參考文件:
官方文件(英文):http://code.google.com/appengine/docs/python/datastore/
中文翻譯(部分,版本較老,與官方文件不同步):
http://blog.csdn.net/lobby/category/400740.aspx
http://blog.csdn.net/jj_liuxin/archive/2008/12/28/3630281.aspx
一、概況
GAE的資料庫叫作datastore,它與傳統的關聯式資料庫不同,可以認為它是一種分散式的物件資料庫。它的底層是由Bigtable資料庫搭建的。
這個資料庫可以儲存db.Model類的資料物件。
實際上,GAE的資料庫模型很像Django。
二、實體和模型
當一個類繼承了db.Model類時,它就可以作為一個資料模型,生成可儲存在資料庫中的資料物件。這個模型就相當於關聯式資料庫的表。
每個模型都可以有很多屬性,這些屬性就定義了其中可儲存的資料型別,它相當於表的欄位。
每個物件在資料庫中都被稱為一個實體(entity)。這個實體就相當於表的記錄。
與關聯式資料庫不同的是,每個實體都可以有自己獨特的屬性,而且屬性的型別也可以不同。
先來看個例子:
複製內容到剪貼簿
程式碼:
fromgoogle.appengine.ext
import db
class Pet(db.Model):
name =db.StringProperty(required=True)
type =db.StringProperty(required=True, choices=set(["cat", "dog","bird"]))
birthdate =db.DateProperty()
weight_in_pounds =db.IntegerProperty()
spayed_or_neutered =db.BooleanProperty()
owner =db.UserProperty(required=True)
這就定義了一個Pet模型,我們可以用它來生成實體。
其中db.StringProperty和db.DateProperty等都是db.Property類的子類。當在模型定義中使用Property類來定義屬性時,這些屬性就是這個模型的固定屬性了;它們的值會被檢驗,以確保能在資料庫中正確儲存。
實體也和一般的物件一樣,可以有自己特有的屬性(以及方法),但這些屬性不會被儲存到資料庫中。
在實體上呼叫put方法,就能將其存入資料庫。例如:
複製內容到剪貼簿
程式碼:
fromgoogle.appengine.api
import users
pet = Pet(name="Fluffy",
type="cat",
owner=users.get_current_user())
# required=True的屬性在構造時就必須賦值
pet.weight_in_pounds = 24
這裡有個注意點,模組是會被快取和重用的,所以不要在模型的配置中使用使用者相關的資料作為屬性的預設值。舉例來說,這個users.get_current_user()就不適合作為owner的預設值,因為使用該模組的使用者如果沒顯式對owner賦值的話,就可能會使用上個使用者作為owner。更多資訊可以看APP的快取機制。
此外,實體還可以擁有動態屬性,這需要模型繼承db.Expando類。
動態屬性也可以儲存在資料庫中,但由於動態屬性沒有相應的模型屬性定義,動態屬性不會被校驗。
當在動態屬性上加過濾器(filter)來查詢時,只會返回有該屬性且屬性型別相同的實體。例如:
複製內容到剪貼簿
程式碼:
classPerson(db.Expando):
first_name =db.StringProperty()
last_name =db.StringProperty()
hobbies =db.StringListProperty()
p =Person(first_name="Albert", last_name="Johnson")
p.hobbies =["chess","travel"]
p.chess_elo_rating =1350
#增加動態屬性
p.travel_countries_visited=["Spain","Italy","USA","Brazil"]
p.travel_trip_count =13
#刪除動態屬性
del p.chess_elo_rating
p1 =Person()
p1.favorite =42 #增加動態屬性
p1.put() #儲存
p2 =Person()
p2.favorite ="blue"
p2.put()
p3 =Person()
p3.put()
people = db.GqlQuery("SELECT * FROM Person WHERE favorite< :1",50)
# people 中包含p1,但不含p2和p3
people = db.GqlQuery("SELECT * FROM Person WHERE favorite> :1",50)
# people 中不包含任何實體
再者,你還可以繼承PolyModel類,以定義繼承性的模型。
使用也和其他模型一樣,例如:
複製內容到剪貼簿
程式碼:
fromgoogle.appengine.ext
import db
from google.appengine.ext.db import polymodel
class Contact(polymodel.PolyModel):
phone_number =db.PhoneNumberProperty()
address =db.PostalAddressProperty()
class Person(Contact):
first_name =db.StringProperty()
last_name =db.StringProperty()
mobile_number =db.PhoneNumberProperty()
class Company(Contact):
name =db.StringProperty()
fax_number =db.PhoneNumberProperty()
p = Person(phone_number='1-206-555-9234',
address='123First Ave., Seattle, WA, 98101',
first_name='Alfred',
last_name='Smith',
mobile_number='1-206-555-0117')
p.put()
c = Company(phone_number='1-503-555-9123',
address='P.O. Box 98765, Salem, OR,97301',
name='DataSolutions, LLC',
fax_number='1-503-555-6622')
c.put()
for contact in Contact.all():
print 'Phone: %s\nAddress:%s\n\n'
%(contact.phone,
contact.address))
三、屬性和型別
在定義模型時可以使用很多種屬性,每個屬性都能在Types andProperty Classes文件裡找到詳細說明,這裡只提注意點。
1.字串
GAE支援最大長度為500位元組的String型別,和最大100萬位元組的Text型別。前者支援索引,可以在搜尋中使用過濾器;後者不行。它們的值可以是unicode,也可以是str,後者預設被當成ascii編碼型別。
此外還有非文字型別ByteString和Blob型別。大小和索引情況同上。Blob型別一般是用於儲存二進位制資料的。
2.列表
要讓一個屬性可以有多個值,則可以使用List和StringList型別。
需要注意的是,當在一個列表屬性上使用過濾器時,將會對其中的成員進行比較,而不是整個列表進行比較。只要其中一個成員符合,就通過這個條件過濾。
複製內容到剪貼簿
程式碼:
#獲取所有包含6的實體(不是實體本身為6)
results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers =6")
# 獲取所有至少包含一個小於10的元素的實體.
results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers< 10")
可以將一個空的list賦值給一個靜態的ListProperty。這個值在datastore中並不存在,但模型例項表現為好像這個值是一個空的list。靜態的ListProperty不能夠是 None值。
一個List動態屬性的值不能是一個空的list。然而它可以是None,並可以刪除。
如果對列表進行正序排序,用來排序的值是列表的最小元素;反之則用最大元素。因此很少對列表進行排序。
3.引用
引用型別是其他實體的key,它相當於關聯式資料庫中的外來鍵。引用的值雖然是key,但它可以自動解引用為實體,可以直接當成實體來使用。同時,實體也能自動引用,當成key來使用。
一個ReferenceProperty屬性值可以像一個模型實體一樣的使用。如果引用的實體在記憶體不存在,訪問它時,將會自動從資料庫裡面取出相應實體。
當一個實體A有一個引用指向實體B,那麼B就稱為A的祖先。注意,如果刪除B,A並不會被刪除,關聯關係也不會消失。但你可以取出A,檢查其ReferenceProperty屬性值是否為None。
另外,ReferenceProperty還有個反向引用(back-references)的特性。即B的secondmodel_set屬性可以返回所有引用它的查詢結果實體集(包含A在內)。
此外,當你需要在一個模型中使用多個引用屬性時,需要顯式地加上collection_name引數,避免往回引用時出錯。
最後,自動引用和解引用、型別檢查,以及反向引用,只有當使用ReferenceProperty時才有效,Expando動態屬性或ListProperty等其他屬性是沒有這些機制的。
4.屬性名
__*__(前後都為2個下劃線)這種形式的屬性名是被資料庫保留的,應用程式不允許建立這種屬性。
以一個下劃線開頭的屬性名會被忽略,資料庫不會儲存這些資料,但你可以在程式中臨時使用。
此外,由於PythonAPI的限制,已經用作模型的方法的名字,也是不能用於屬性名的。但資料庫卻允許這樣做,只需要在屬性的建構函式裡增加name引數即可:
複製內容到剪貼簿
程式碼:
classMyModel(db.Model):
obj_key =db.StringProperty(name="key")
四、建立、獲取和刪除資料
終於開始真正使用資料庫了,其實用起來很簡單,就和一般的物件差不多。
1.建立和更新:
呼叫一個模型的建構函式,即可建立這個類的物件。
更新則只需要修改物件的屬性即可。
呼叫這個物件的put方法,或使用db的put方法,即可儲存該物件到資料庫。
例子:
複製內容到剪貼簿
程式碼:
pet=
Pet(name="Fluffy",
type="cat",
owner=users.get_current_user())
pet.put() #等於下面這句
db.put(pet)
2.查詢
資料庫可以查詢一個模型型別的實體。一條查詢可以用條件子句來過濾實體的屬性值,也能返回經過排序的結果集,還可以通過祖先來限制查詢結果的範圍(其實就相當於innerjoin)。
DatastoreAPI提供了2種查詢方式:一種是通過呼叫Model類的all方法,查詢所有該模型的物件,然後再呼叫filter、order和ancestor方法來限制和排序結果集;另一種是使用Gql查詢。
先說前者,例子如下:
複製內容到剪貼簿
程式碼:
class
Story(db.Model):
title =db.StringProperty()
date =db.DateTimeProperty()
query = Story.all()
query.filter('title =', 'Foo')
query.order('-date')
query.ancestor(key)
# 這些方法也可以鏈式呼叫
query.filter('title =','Foo').order('-date').ancestor(key)
再說後者,這個比前者多了個對結果集的個數限制以及偏移量指定。
它又有2種方式:
一種是使用GqlQuery類的建構函式來建立查詢物件:
複製內容到剪貼簿
程式碼:
#可用位置來繫結引數
query = db.GqlQuery("SELECT * FROM Story WHERE title = :1"
"AND ANCESTOR IS :2 "
"ORDER BY date DESC",
'Foo',key)
# 也可用名字來繫結引數
query = db.GqlQuery("SELECT * FROM Story WHERE title = :title"
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
title='Foo',parent=key)
# 字串、數字和Boolean值可以作為字面值(literal values)直接使用
query = db.GqlQuery("SELECT * FROM Story WHERE title = 'Foo'"
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
parent=key)
另一種是使用Model類的gql方法:
複製內容到剪貼簿
程式碼:
query=
Story.gql("WHERE title = :title "
"ANDANCESTOR IS :parent "
"ORDER BYdate DESC",
title='Foo',parent=key)
此外,還可用bind方法來重新繫結引數,以便重複使用一個查詢物件。
3.執行查詢並獲取結果(集)
在建立查詢物件時,應用程式並不會訪問資料庫。直到對結果集進行操作時,才會訪問資料庫。
獲取結果集有2種方式:使用fetch方法,和使用迭代介面(iterator interface)。
fetch方法一次最多查詢1000個結果,你也可以設定讓其返回指定個數(不超過1000)的結果集。
此外,fetch還可以設定偏移量,即從第幾個實體開始返回。但fetch查詢的結果並不受偏移量限制,僅是隻從偏移量個實體開始返回而已。所以假設偏移量為100,則最大隻能返回900個實體。所以應該用過濾器來限制返回條數,多呼叫幾次以得出全部結果。
這個限制非常討厭,但GAE的資料庫速度可以說是非常慢的,就算沒這個限制,也會超出執行時間。
例子:
複製內容到剪貼簿
程式碼:
results=
query.fetch(10)
for result in results:
print "Title: " +result.title
如果是取實體的數目,可以用count方法。但它也是取出所有記錄再統計數目,比關聯式資料庫的count操作慢很多,而且也受1000條的影響。應該只在結果集很小,或設定了個數限制時使用。
迭代方式則沒有查詢結果的個數限制,因為它相當於一次獲取一個實體,不過速度自然會比前者慢(訪問資料庫次數多很多)。受GAE的執行時間限制,實際上應該也不會超過1000條。
例子:
複製內容到剪貼簿
程式碼:
forresult
in query:
print "Title: " +result.title
還可以使用db.get或Model.get方法獲取一個實體。
例子:
複製內容到剪貼簿
程式碼:
entity.put()
key = entity.key()
# ...
entity = db.get(key)
4.刪除
你可以使用delete方法來刪除實體。
例子:
複製內容到剪貼簿
程式碼:
q=
db.GqlQuery("SELECT * FROM Message WHERE create_date< :1", earliest_date)
results = q.fetch(10)
for result in results:
result.delete()
# or...
q = db.GqlQuery("SELECT * FROM Message WHERE create_date< :1", earliest_date)
results = q.fetch(10)
db.delete(results)
看上去是先取出來,再進行刪除的,速度應該是很慢的。
此外,無法直接刪除一個模型。
五、Key
每個實體都有一個唯一的key,用於標識它。一個key有3個組成部分:描述它和其他實體之間的父子關係的路徑(path);實體型別(kind,即模型的類名);程式給實體設定的名字,或者資料庫給實體生成的數字ID。
每個實體都有一個唯一的識別符號。可以在程式中對其賦值,只需在構造時傳給key_name引數一個字串即可:
複製內容到剪貼簿
程式碼:
s=
Story(key_name="xzy123")
如果沒有指定key_name,資料庫會給它生成一個數字ID。在一般情況下,這個ID會根據實體的建立時間而增長,但資料庫並不保證它一定這樣,且增長幅度可能不為1。我就曾看過2個實體建立時間和他們的ID大小不是相對的;也曾有過ID從幾十突然增大到1001的情況;接著過了幾天,ID又從幾十開始增加了。這應該是因為同時有多個插入操作,資料庫就往後跳躍了一個較大的尺度;等應用完成後,GAE會在空閒時尋找浪費的ID,繼續在那插入。
注意:一旦建立,實體的名字或ID就不能更改。
六、實體組(EntityGroup)
每個實體都屬於一個實體組。單個事務可以操作一組實體。實體組的關係告訴GAE在分散式網路同一部分儲存幾個實體。一個事務為一個實體組建立的資料庫操作,要麼全部成功,要麼全部失敗。
當應用程式將一個實體賦值為另一個實體的父親,這個操作就把新的實體併到父親的同一組。
沒有父親的實體是根實體。父子關係可以多級。從根節點開始的鏈稱為實體的路徑。這條路徑上的實體都是它的祖先。實體建立時,父親就指定了,並且不能被改變。
通過指定繼承路徑,你可以在不建立父親的情況下,就建立一個帶祖先路徑的實體。為了實現它,你應該用型別(模型名)和keyname建立一個祖先的key(它並不對應一個真正的實體),然後用它作為新實體的父親。所有具備相同根祖先的實體都屬於同一組。無論這個根祖先是否是一個真正的實體。
提示:
-
只有需要事務的時候才使用實體組。其他實體間的關係請使用ReferenceProperty和key值,它們可以用於查詢。
-
實體組越多,根實體就越多,資料庫就能更有效率地使用節點(更好地實現分散式),以提供更佳的更新和插入效能。多使用者在試圖同時更新同組的實體時,會導致一些事務的重新執行,還可能導致失敗。不要把所有的實體放在同一個根下。
-
定義實體組的一個較好的規則是,使它小到只對單個使用者有價值,或者更小。
-
實體組不會對查詢有明顯的影響。
七、索引
每條資料庫的查詢都要用到索引,如果沒有相應的索引,查詢就不會成功。這是GAE的資料庫最大的限制,也是與其他資料庫最大的不同,雖然是個很討厭的限制,但也是為了避免全表掃描導致效能降低。
GAE使用index.yaml來定義索引。幸運的是,如果你是在開發伺服器上使用,第一次查詢時會自動幫你建立索引,你只需上傳到Google的伺服器就行了。
1.索引介紹
一次資料庫查詢可以指定結果集必須符合的條件,例如實體型別、屬性值的範圍、祖先,以及排列順序。查詢時會去查詢有沒有符合該條件的索引,只有當索引中定義了這些後,才能按索引進行相應的查詢。
指定屬性範圍可以用以下操作符:<、<=、=、>、>=、!=和IN。
!=操作實際上是將<和>操作的結果集合並;IN操作實際上是轉成其中所有元素的=操作的結果集合並,這可能造成很多次的資料庫訪問。
資料庫通過以下步驟執行一次查詢:
-
資料庫識別符合實體型別、過濾器屬性、過濾器操作符和排列順序的索引。
-
資料庫掃描索引,並找到第一條符合所有條件的實體。
-
資料庫繼續掃描索引,返回找到的每個實體,直到發現不符合條件的實體或索引結束。
索引表包含了所有使用了過濾器的屬性和排列順序。它的每行都以下列順序排列:
-
祖先
-
使用了=或IN過濾器的屬性
-
使用了不等於過濾器的屬性
-
使用了排列順序的屬性
此外,GAE的資料庫並不支援萬用字元查詢,但可以通過這種方式來查詢以某些字元開頭的字串:
複製內容到剪貼簿
程式碼:
db.GqlQuery("SELECT*
FROM MyModel WHERE prop >= :1 AND prop< :2", "abc", u"abc" + u"\ufffd")
其中u"\ufffd"是unicode中可能出現的最大的字元,通過這種方式,就能查詢abc開頭的字串。
要注意的是,在沒有索引的屬性上使用過濾器等是不會返回任何結果的,不在索引裡的屬性也是不會被返回的。所以如果想返回屬性值為None的實體,你可以在定義資料模型時,為這個屬性定義預設值(例如None)。
Text和Blob型別是沒有索引的,也不能在它們上查詢。
另外,屬性值的排序是先按屬性型別,再按屬性的值。這就意味著整型一定排在浮點型前,浮點型又一定排在字串前,即:37< 36.5 < "36"。
如果這不是你所期望的,可以讓其只能為相同型別。
2.使用index.yaml定義索引
GAE將為下列查詢自動在index.yaml中建立索引:
-
只使用了=、IN和祖先過濾器的查詢
-
只使用了不等於過濾器的查詢
-
只在一個屬性上使用了一次排序的查詢
其他的查詢需要手動定義索引:
-
有多個排序的查詢
-
在key上的降序排序查詢
-
在多個屬性上同時使用了不等於和=或IN過濾器的查詢
-
同時使用了不等於和祖先過濾器的查詢
不過在開發伺服器上,當找不到對應索引時,它會自動建立一個索引,保證查詢不會失敗。
你可以在dev_appserver.py啟動時加上--require_indexes引數,時它不會自動建立索引,以確保和生產伺服器是相同的。
更多關於定義索引的資訊,你可以檢視配置索引的文件。
3.查詢Key
你只需在查詢中指定__key__為查詢屬性即可。
使用key可以順序遍歷一個模型,例如:
複製內容到剪貼簿
程式碼:
classMainHandler(webapp.RequestHandler):
def get(self):
query = Entity.gql('ORDER BY__key__')
#使用1個查詢引數來記錄最後一條應該查詢的實體
last_key_str =self.request.get('last')
if last_key_str:
last_key = db.Key(last_key_str)
query = Entity.gql('WHERE __key__> :1 ORDER BY __key__', last_key)
#如果一次查詢20條,找找是否有第21條
entities =query.fetch(21)
new_last_key_str =None
if len(entities) ==21:
new_last_key_str =str(entities[19].key())
注意,如果你只需要查詢一個特定key對應的實體,用db.get方法會更快。
3.查詢限制
由於索引的限制,導致有以下的限制存在:
-
在屬性上使用過濾器和排序,則需要該屬性存在。不存在該屬性的實體不會被返回。
-
沒有過濾器會符合沒有屬性的實體。如果確實需要返回屬性為None的實體,需要建立一個帶None的過濾器。
-
只能在一個屬性上使用不等於過濾器。
例如:
複製內容到剪貼簿
程式碼:
SELECT* FROM Person WHERE birth_year >= :min
ANDbirth_year <= :max
SELECT * FROM Person WHERE birth_year >=:min_year
AND height>= :min_height #這是錯的
SELECT * FROM Person WHERE last_name = :last_name
AND city =:city
ANDbirth_year >= :min_year -
如果在一個屬性上使用不等於過濾器,那麼進行排序時,它必須在其他屬性前排序。
例如:
複製內容到剪貼簿
程式碼:
SELECT* FROM Person WHERE birth_year >=:min_year
ORDER BYlast_name #錯誤
SELECT * FROM Person WHERE birth_year >=:min_year
ORDER BYlast_name, birth_year #錯誤
SELECT * FROM Person WHERE birth_year >=:min_year
ORDER BYbirth_year, last_name -
對列表屬性排序很可能超出索引限制。
由於列表的排序是基於其元素的,所以它索引是這樣的:
-
如果對列表進行升序排列,則按列表中的最小元素進行排列
-
如果對列表進行降序排列,則按列表中的最大元素進行排列
-
其他元素和列表大小不影響排序
-
如果相同,則再使用key值進行排列
當在一個實體上定義了多個列表的索引時,索引的數目將會急速增長。例如這個索引:
複製內容到剪貼簿
程式碼:
indexes:
- kind: MyModel
properties:
- name: x
- name: y建立一個實體:
複製內容到剪貼簿
程式碼:
classMyModel(db.Expando):
pass
e2 = MyModel()
e2.x = ['red', 'blue']
e2.y = [1, 2]
e2.put()這會讓資料庫生成8條索引記錄,即在x、y上各有順序和倒序的2個,然後在x和y上又有4種順序組合。
當一個put操作需要作用在很多條索引上時,就可能超過限制,並丟擲BadRequestError異常。
解決這種情況,需要先將出錯的索引從index.yaml中去掉,再執行appcfg.pyvacuum_indexes,最後將移除的索引新增回來,並執行appcfg.pyupdate_indexes。
為了避免它發生,最好少在列表屬性上定義索引,並只給出一種排列順序。
-
八、事務
為保證一系列資料庫操作要麼都執行成功,要麼都不產生效果,我們需要用到事務。
我們將事物中的操作用db.run_in_transaction函式呼叫就行了。例如:
複製內容到剪貼簿
程式碼:
fromgoogle.appengine.ext
import db
class Accumulator(db.Model):
counter =db.IntegerProperty()
def increment_counter(key, amount):
obj = db.get(key)
obj.counter += amount
obj.put()
q = db.GqlQuery("SELECT * FROM Accumulator")
acc = q.get()
db.run_in_transaction(increment_counter, acc.key(), 5)
如果執行成功,會進行提交;如果產生異常,則會回滾。如果產生的是Rollback異常,函式會返回None;其他異常則會向外丟擲。
事務中還有些限制:
不能使用Query或GqlQuery查詢,但可以用key來獲取實體。
在一個事務中,不能對一個實體進行超過一次的建立或更新操作。
此外,如果同一時刻有多個事務對同一實體進行操作,可能會導致失敗。這種情況下,事務會自動重試幾次,如果仍失敗,將會丟擲TransactionFailedError異常。你可以用db.run_in_transaction_custom_retries函式來設定重試次數。
相關文章
- Google Earth Engine下載Sentinel2資料Go
- 部署 meteor 到 Google Compute EngineGo
- AIRVPS WEB APP ENGINEAIWebAPP
- NodeJs中資料庫的使用NodeJS資料庫
- Google Kubernetes Engine連續第三天服務中斷Go
- 初識分散式圖資料庫 Nebula Graph 2.0 Query Engine分散式資料庫
- 資料庫中AS的使用意義資料庫
- 分散式資料庫Google Spanner原理分析KP分散式資料庫Go
- Oracle資料庫中convert()函式,在瀚高資料庫中如何替換使用?Oracle資料庫函式
- MySQL資料庫中timediff()函式,在瀚高資料庫中如何替換使用?MySql資料庫函式
- 使用python中kivy庫生成安卓APPPython安卓APP
- 【Falsk 使用資料庫】---- 資料庫基本操作資料庫
- 資料庫中字串連線符的使用資料庫字串
- 在Docker中能使用資料庫嗎? | BaeldungDocker資料庫
- 與Google Spanner跨越資料庫世界的對話 - nextplatformGo資料庫Platform
- 如何使用DTM將App事件傳送到Google AnalyticsAPP事件Go
- Google釋出App Engine第二代執行時,提供Python 3.7和PHP 7.2支援GoAPPPythonPHP
- Clickhouse Engine kafka 將kafka資料同步clickhouseKafka
- [資料庫]資料庫中為什麼不推薦使用外來鍵約束資料庫
- mongodb資料庫中插入資料MongoDB資料庫
- Python中內建資料庫!SQLite使用指南! ⛵Python資料庫SQLite
- Google Play App Store API 採集谷歌安卓應用商城app的資料介面 - 2024最新GoAPPAPI谷歌安卓
- 【資料庫】使用DBever連線人大金倉資料庫資料庫
- Sqlserver資料庫使用 .bak 檔案還原資料庫SQLServer資料庫
- Python——Reflex(資料庫使用)PythonFlex資料庫
- 資料庫基礎使用資料庫
- Laravel 使用 Oracle 資料庫LaravelOracle資料庫
- MySQL資料庫使用(二)MySql資料庫
- Google Colab 現已支援直接使用 ? transformers 庫GoORM
- 如何在Nuxt3.0中使用MongoDB資料庫UXMongoDB資料庫
- 生產資料庫、開發資料庫、測試資料庫中的資料的區分資料庫
- 將資料庫中資料匯入至solr索引庫資料庫Solr索引
- iOS 輕鬆使用 App 資料統計iOSAPP
- 【譯】使用 Google TWA 技術將 PWA 打包成 Android AppGoAndroidAPP
- 資料庫——關係型資料庫MySQL--簡單使用資料庫MySql
- 使用 Cheat Engine 修改 Kingdom Rush 中的金錢、生命、星
- MongoDB資料庫中查詢資料(下)MongoDB資料庫
- 兩個將來可能很厲害的perl 專案:perl雲端計算及跟google app engine的結合GoAPP
- 查詢資料庫中的所有的普通使用者資料庫