Python数据库操作|sqlite、MongoDB、Redis
一、sqlite
1.sqlite介绍
sqlite是一款轻型、基于文件的关系型数据库,是遵守ACID的关系型数据库管理系统,支持多种开发语言,其中就包括Python,而Python 2.5以后的安装包就已经自带了SQLite3的安装包,直接导入就可以使用。关于在什么样的场景下使用sqlite,SQLite官网给出了一个判断是否适合使用SQLite标准的:
- 如果程序和数据分离且它们通过互联网连接,那么不适合用 SQLite
- 高并发写入,那么不适合用 SQLite
- 如果数据量非常大,那么不适合 SQLite
- 除此之外,选择 SQLite
2. .db文件在Pycharm中的可视化
sqlite文件生成.db文件,在Pycharm中需要安装插件可视化,Pycharm专业版自带database插件,而社区版可安装dbBrowser工具,具体操作:preferences–> plugins -->搜索栏中搜索:database --> 然后点击安装:Databases Navigator,安装成功后,点击:view–>tool windows–>DB Browser即可。具体操作见:社区版pycharm添加database管理工具dbBrowser
3.Python中操作sqlite3
(1)导入连接
import sqlite
con = sqlite3.connect(database_path) # database_path:db文件路径
cursor = con.cursor() # 创建游标
(2)CURD操作
# 创建一个名为:students的表,表中包括整数id、字符串name、浮点数score三个字段一个文件可创建多张表
sql = "create table students (id integer primary key, name text, score real)"
cursor.execute(sql_data)
con.commit()
# 向students表中添加一个id为99,name为“李华”、score为89.5的学生信息
sql = "insert into students (id, name, score) VALUES (?, ?, ?) "
cursor.execute(sql, (99, "李华", 89.5))
cursor.executemany(sql,[(99, "李华", 89.5),(90, "张三", 80.5)]) # 添加多条数据
con.commit()
# 更新数据:将id为99的score更新为88.0
sql = f"update student set score=? where id =? "
cursor.execute(sql, (88.0, 99))
con.commit()
# 或:
sql = f"update student set score=88.0 where id=99 "
cursor.execute(sql)
con.commit()
# 删除数据,两种方式:
cursor.execute("delete from student where id=?",(99,))
cursor.excute("delete from student where id=99")
con.commit()
# 查询所有数据
cursor.execute("select * from student")
count_info = cursor.fetchall() # count_info:[(99,"李华",89.5),(90, "张三", 80.5)]
count_info = cursor.fetchone() # 查询第一条数据
# 条件吗查询并对结果进行降序排序,返回第一页的20条数据,即前20条数据,page_index为1时返回第21-40条啥时间
page_index = 0
page_size = 20
score_thresh = 80.0
cursor.execute("select * from student where score>='{score_thresh}' order by id desc limit {page_size} offset {page_size}*{page_index}" )
(3)其它操作
- 事务提交:在对数据库做修改后,需要提交事务,不然对数据库的更改不会保留:
con.commit()
- 事务回滚:(暂时还没使用过该功能)
con.rollback()
- 断开会话连接,释放资源:
cursor.close() # 关闭游标
con.close() #断开数据库连接
二、MongoDB
1.MongoDB 与SQL数据库
MongoDB是文档型数据库,不同于关系型数据库SQL,这类数据库对字段要求不严格,可以随意增加、删除、修改字段,且不需要预先定义表结构,并发写入速度也远超传统关系型数据库,适用于各种网络应用。
MongoDB以“库–>集合–>文档–>字段”结构存储数据,适合储存大量关联性不强的数据。MongoDB 与SQL数据库名词对比图如下:
2.安装PyMongo
python3 -m pip install pymongo
3.连接数据库
- MongoDB运行在本地电脑上,且没有修改端口或者添加用户名和密码
from pymongo import MongoClient
client = MongoClient()
- 运行在服务器上,需用URL(mongodb://用户名:密码@服务器IP或域名:端口)指定连接地址:
from pymongo import MongoClient
client = MongoClient('mongodb://kingname:12345@172.168.1.11:80001)
#或
client = MongoClient('172....', 9090)
- 连接数据库和集合的两种方法
# 方法1
database = client.数据库名
collection = database.集合名
# 方法2 工程中常将需修改数据库存于列表中用于批量操作
database = client[数据库名]
collection = database[集合名]
4.常用命令
(1)MongoDB命令与PyMongo方法对照
MongoDB命令使用的是驼峰命名法,而MongoDB的Python API — PyMongo则使用小写字母加下划线命名方式,对比如下图:
注:Python 操作MongoDB,当使用的库或集合不存在,则在调用了插入方法后,PyMongo会自动创建对应的库或集合
(2) 插入数据
# 插入内容为一个字典格式,插入多条时将字典放到一个列表
collection.insert() #通用,正式环境中不推荐
collection.insert_one({'a':1})
collection.insert_many([{'a':1}, {"b":2}])
(3) 查询操作
- 条件查询
# 根据query_condition条件进行查询,查询内容也是一个字典的格式
query_condition={"年级":"九年级"}
collection.find(query_condition)
- 指定范围查询,范围操作符及其意义如下:
示例如下:
# 查询分数在[60,80]范围内的信息
collection.find({"score":{"$gte":60.0, "$lt":80}})
collection.find({"score":{"$gte":60.0, "$lt":80}}).count() # 查询并统计条数
collection.find({"score":{"$gte":60.0, "$lt":80}}).distinct("age") # 对"age"字段去重
还可指定返回内容,1表示返回该字段信息:
query_condition = {"score":{"$gte":60.0, "$lt":80}}
query_content = {"id":1, "name":1}
collection.find(query_condition, query_content)
更进一步,根据分数降序排序,且返回第1页的20条信息,其中:sort()方法,接收两个参数,第1个参数为字段名。第二个参数为-1(降序)或1(升序)
page_size = 20
page_index = 0
collection.find(query_condition, query_content).sort("score", -1).limit(page_size).skip(page_size*page_index) # -1为降序
更高级查询:多条件"或"查询,(按优先级将查询条件排列成一个列表):
collection.find({"$or":[{"age":{"$gt":18}}, {"score":{"$gt":60}}]})
(4) 更新操作
- 其中的upsert参数:数据存在则更新,不存在则创建,使用该参数时需注意:当数据不存在创建新文档时被插入的内容只有‘$set’设置的被更新的字段,为了使数据库内字段统一可将没有信息的字段内容设置为空。
update_condition = {"name":"李华"}
update_content = {"age":17, "身高":180}
collection.update_one(update_condition, update_content, upsert=True)
collection.update_many() # 批量更新
(5) 删除操作
collection.delete_many() # 可通过{}指定删除条件
(6) 高级操作
a. _id
- _id:每插入一条记录,MongoDB都会自动为该记录生成一个字段“_id”,该字段是一个ObjectId对象,它是由时间、机器码、进程pid和自增计数器构成的。“_id”始终递增,但绝不重复。
- _id的前8位字符转换为十进制就是时间戳,每次查询时都会默认返回该字段,可通过设置返回内容: {"_id":0} 选择不返回该字段。而"_id"默认返回的是一个对象类,可用过以下设置将其转换为字符串返回:
query_condition = {"name":"李华"}
query_content = {"_id":{"$toString":"$_id"}, "age":"$age"}
collection.find(query_condition, query_content)
返回:
{"_id":"5b2f2e24e0f42944105c81d2", "age":17}
相应的,可根据_id的字符串值来查询文档,但在查询前需要将字符串转换为ObjectId对象,在python中需从bson库中导入ObjectId(安装PyMongo时自动安装该库):
from bson import ObjectId
collection.find({'_id':ObjectId("5b2f2e24e0f42944105c81d2")})
b. 查询子文档或数组中的数据
- 查询嵌套字段,可使用点号来指定具体的字段名,格式为:嵌入式文档名.嵌套字段名,如查询嵌套字段user的子字段user_id为102的数据:
collection.find("user.user_id":102)
- 查询数组包含与不包含‘XX’数据(size为一个数组,查询所有size包含M的记录):
collection.find('size':'M') # 不包含:"size":{"$ne":"M"}
- 根据数组长度查询数据("$size"只能查询具体某一个长度的数组,不能查询长度大于或小于某一个数值的数组)
# 查询所有"price"字段长度为2的记录
collection.find("price":{"$size":2})
- 根据索引查询数据
# 查询size数组的第一个数据为S的记录
collection.find("size.0":"s")
c.聚合查询(aggregate)
- 聚合功能可以把数据像放入传送带一样,先把原始数据按照一定的规则进行筛选处理,然后通过多个不同的数据处理阶段来处理数据,最终输出一个汇总的结果。
- 聚合里面可以包含多个不同的阶段分别负责不同的事情,每一个阶段有一个关键字:
$match:负责筛选数据的阶段
$project:负责字段相关的阶段
$group:负责分组阶段
...
- 筛选关键字"$match"
collection.aggregate([{"$match":{和find完全一样的查询表达式}}])
- 只返回部分字段"$project"
与"find()"第2个参数完全相同,也是一个字典,字段名为Key,Value为1或0(需要的字段Value为1,不需要的字段的Value为0)
# 用'$project'为数据集添加新字段,原数据集中没有"hello"字段,该语句为数据集添加了一个“hello”字段,且其固定值为“world”
collection.aggregate([ {"$project":{"_id":0, "age":17, "hello":"world"}}])
#复制现有字段,把“age”字段的值复制到"hello"字段中
collection.aggregate([{"project":{"_id":0, "hello":"$age"}])
#该功能可用处理嵌套字段,用find时:
collection.find({}, {"user.name":1, "user.user_id":1})
#↑返回结果仍为一个嵌套字段,可用"$prject"把嵌套字段中的内容抽取出来:
collection.aggregate([{"$projrct":{"name":"$user.name", "user_id":"$user.user_id"}}])
#当特殊字段的值和"$project"的自身语法冲突了,导致所有以"$"开头的普通字符串和数字不能添加,可使用另一个关键字,如:
collection.aggregate([{"$project":{"_id":1, "hello":{"$literal":"$normalstring"}, ""abc":{"$literal":1}}}}])
- 分组"$group"
分组操作对应的关键字为"$group",它的作用是根据给出的字段Key,把所有Key的值相同的记录放在一起运算,这些运算包括:
$sum:求和
$avg:计算平均
$max:最大值
$min:最小值
$exists:(True\False)判断是|否存在
分组并计算统计值,"$sum"的值还可以使用数字“1”,这样查询语句就变成了统计每一个分组内有多少条记录
collection.aggregate([
{"$group":{"_id":"$被去重的字段名",
"max_score":{"$max":"$字段名"},
"min_score":{"$min":"$字段名"},
"average_score":{"$avg":"$字段名"},
"sum_score":{"$sum":"$字段名"}
}
}
])
注:还有" u n w i n d " 、 " unwind"、" unwind"、"vlookup"等其它功能,因为还没有使用到这些功能,暂时不记录。
- 综合操作实例
需要注意skip和limit的顺序
#先通过project过滤出个人信息的字段,再通过query_condition查询出满足条件的记录,再对这些记录通过"user_name"进行分组,并记录每个分组内的记录数,最后面加了分页两个限制
collection.aggregate([{"$project":{"user_name":1, "age":1 ,"score":1}}
{"$match":query_condition},
{"$group": {"_id": {"user_name":"$user_name"}, "num":{"$sum":1}}},
{"$skip": skip_num},
{"$limit": page_size}
])
三、Redis
1.简介
在程序开发中,如果需要频繁操作数据库中的一些数据,比较高级的做法是把这些数据先读出来,用一个或者多个变量来保存。程序只读一次数据库,之后直接操作变量,等到数据处理完成后,再将数据更新回原数据库或者插入新的数据库中。
Redis能做到在不同进程间、不同机器之间共享变量。
2.安装
python3 -m pip install redis
3.连接Redis数据库
import redis
client = redis.Redis()
#远程连接,db指定数据库
pool_collect = redis.ConnectionPool(host="ip", port=port, db=10, decode_responses=True)
不论是Python的字符串还是数字,一旦进入了Redis再出来就会变成bytes型的数据,因此需要注意做好格式转换,可如上代码在连接时设置decode_responses=True,也可在创建字符串时设置client.set(“key”,“value”,nx=True)
4.操作
(1)创建、获取字符串
client.set("key","value")
client.get("key")
(2)哈希创建、获取字符串
client.hmset("key",{}) # value可传一个字典
client.hgetall("key")
# 设置过期时间,单位为s
client.expire("key", ttl)
注:Redis用的不多,暂时只记录这些功能。
参考:
[1]社区版pycharm添加database管理工具dbBrowser
[2]Python3操作SQLite数据库
[3]微信阅读《左手MongoDB,右手Redis:从入门到商业实战》