上一讲:HTML、XML和JSON¶
- 每种都提供了一种不同的存储数据的方式
- JSON的关键动机:自我描述
- 但我们发现JSON很快就会变得相当笨重……
一个更复杂的JSON对象的例子¶
- 如果我有数百种不同类型的蛋糕或甜甜圈怎么办?
- JSON对象的嵌套性使它们变得有点复杂。
- 通常,JSON适用于传递(少量的)数据,
- 但对于存储和操作大量、复杂的数据集合,有更好的工具,即数据库。
- 注意:还有安全和软件工程方面的原因,更倾向于使用数据库而不是JSON来存储数据。
为什么要使用数据库?¶
- 数据库软件处理数据的能力
- 索引:允许数据库快速定位到数据行,减少查询时间
- 日志记录:数据库的日志记录用于跟踪所有的事务和更改,以便进行恢复和审计
- 归档自动处理:将不再频繁访问的数据移动到更低成本的存储中,以释放主数据库的空间。
- 允许快速、并发(即多个用户)访问数据
- 通过Web访问
- 数据库可以运行在服务器上
- 同样,JSON/XML/HTML等适用于传递数据,数据库适用于存储
数据库(DB)¶
- 示例:人口普查信息、产品库存、图书馆目录
- 关系数据库
- https://en.wikipedia.org/wiki/Relational_database
- 之所以这样命名,是因为它们捕获了实体之间的关系,自20世纪70年代以来一直存在,至今仍是主导模型
- 图数据库
- https://en.wikipedia.org/wiki/Graph_database
- 适用于复杂的关系数据模型,例如社交网络、电子商务、物流网络等
- 本课程范围之外:其他模型(例如,面向对象)
关系数据库的优缺点¶
- 优点:
- 自然适用于绝大多数应用
- 有许多管理查询工具
- 缺点:
- 不适合某些数据(例如,网络、非结构化文本)
- 一般采用固定的表结构(即,很难添加列)
ACID:原子性、一致性、隔离性、持久性¶
- 原子性:
- 确保一个事务要么完全执行,要么完全不执行。如果事务中的任何部分失败,该事务将被回滚,数据库将恢复到事务开始之前的状态。
- 一致性:
- 一致性确保在事务开始之前和结束之后,数据库都必须处于一致的状态。即如果某个事务完成,所有数据库应满足所有规则(如约束、外键等)。
- 隔离性:
- 隔离性确保并发事务的执行不会相互干扰。也就是说,一个事务的执行不应受其他事务的影响。所有并发事务都应表现得像是在独立执行。
- 持久性:
- 持久性确保一旦事务被提交,它所做的更改将被永久保存到数据库中,即使系统崩溃或出现故障,这些更改也不会丢失。
- 注意:一些RDBMS为了更快的性能,牺牲了上述一个或多个特性
关系数据库的基本单位:记录¶
- 数据库中的每个实体都有一个相应的记录
- 记录的特征存储在字段中
- 具有相同“类型”字段的记录收集到表中
- 每条记录是一行,每个字段是一列
- 字段可以包含不同的数据类型
- 整数,浮点数,字符串,布尔型,日期和时间
- 一些数据库软件允许类型有所差别。
SQL(最初由IBM称为SEQUEL)¶
- 结构化查询语言(Structured English QUEry Language)
- 用于与关系数据库交互的语言
- 不是唯一的方法,但绝对是最受欢迎的
- 各个平台之间有轻微的变化(“SQL方言”)
- 好的教程:https://www.w3schools.com/sql/sql_intro.asp
关系数据库管理系统(RDBMS)¶
- 促进与数据库交互的程序称为RDBMS
- 公共/开源选项:
- MySQL、PostgreSQL、SQLite
- 专有:
- IBM Db2、Oracle、SAP、SQL Server(Microsoft)
- 我们将展示如何使用SQLite,因为它内置于Python中。
SQLite数据库¶
- 管理工具
- VS Code: SQLTools
- PyCharm: Database Tools and SQL for WebStorm
- 管理工具安装相应数据库驱动
- 新建数据库,并建立连接
Python sqlite3包实现了SQLlite¶
Connection
对象代表数据库Connection
对象可以用来创建Cursor
对象 游标促进与数据库的交互
conn = sqlite3.connect(‘example.db’)
- 建立与给定数据库文件的连接(如果需要,则创建它)
- 并返回一个
Connection
对象
cursor = conn.cursor()
- 为与数据库交互创建并返回
Cursor
对象
- 为与数据库交互创建并返回
cursor.execute ([SQL命令])
- 运行给定的命令;游标现在包含查询结果
conn.commit()
- 将对数据库的更改提交到数据库
In [1]:
import sqlite3
conn = sqlite3.connect('example.db')
cursor = conn.cursor()
添加/删除表:创建新表或删除现有表¶
- 创建新表:
CREATE TABLE
CREATE TABLE table_name [col1 datatype, col2 datatype, …]
- 删除表:
DROP TABLE
DROP TABLE table_name;
- 删除表时要格外小心!
In [14]:
cursor.execute('''CREATE TABLE IF NOT EXISTS student
(id, name, major, score);''')
conn.commit()
In [13]:
cursor.execute("DROP TABLE IF EXISTS student;")
conn.commit()
添加/删除记录:在表中插入新行或删除现有行¶
- 向表中插入一行:
INSERT INTO
INSERT INTO table_name [col1, col2, col3, …] VALUES value1, value2, value3, …
- 注意:如果为所有列添加值,只需要指定值。
- 从表中删除行:
DELETE
DELETE FROM table_name WHERE condition
In [15]:
import pandas as pd
def print_student_details():
sql_select = "SELECT * FROM student;"
cursor.execute(sql_select)
# 在执行数据库查询后,游标对象将访问查询返回的数据。
# fetchall() 方法会返回查询结果的所有行,通常是以列表的形式
result = cursor.fetchall()
result = pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
return(result)
In [16]:
sql = "INSERT INTO student (id, name, major, score) VALUES (5, 'PJ', 'CS', 85);"
cursor.execute(sql)
conn.commit()
print_student_details()
Out[16]:
id | name | major | score | |
---|---|---|---|---|
0 | 5 | PJ | CS | 85 |
In [17]:
sql_delete = "DELETE FROM student WHERE name='PJ';"
cursor.execute(sql_delete)
conn.commit()
print_student_details()
Out[17]:
id | name | major | score |
---|
- 插入多行
cursor.executemany(sql, parameters)
: 对于参数中的每个项,重复执行参数化的SQL 语句 sql。
In [19]:
students = [
(0, 'PJ', 'CS', 85),
(1, 'John', 'Math', 90),
(2, 'Jane', 'Math', 85),
(3, 'Bob', 'English', 95),
(4, 'Alice', 'History', 80),
(5, 'Tom', 'Art', None),
(6, 'Mary', 'Music', 98)
]
# Insert data into table
cursor.executemany('INSERT INTO student VALUES (?,?,?,?)', students)
conn.commit()
print_student_details()
Out[19]:
id | name | major | score | |
---|---|---|---|---|
0 | 0 | PJ | CS | 85.0 |
1 | 1 | John | Math | 90.0 |
2 | 2 | Jane | Math | 85.0 |
3 | 3 | Bob | English | 95.0 |
4 | 4 | Alice | History | 80.0 |
5 | 5 | Tom | Art | NaN |
6 | 6 | Mary | Music | 98.0 |
修改行¶
- 修改表中的一行:
UPDATE
UPDATE table_name SET col1=value1,col2=value2, WHERE condition
In [20]:
cursor.execute("UPDATE student SET score = 90 WHERE name = 'PJ'")
conn.commit()
print_student_details()
Out[20]:
id | name | major | score | |
---|---|---|---|---|
0 | 0 | PJ | CS | 90.0 |
1 | 1 | John | Math | 90.0 |
2 | 2 | Jane | Math | 85.0 |
3 | 3 | Bob | English | 95.0 |
4 | 4 | Alice | History | 80.0 |
5 | 5 | Tom | Art | NaN |
6 | 6 | Mary | Music | 98.0 |
检索记录:在表中查找所有行¶
- SQL SELECT语句的基本形式:
SELECT [列名] FROM [表]
- 示例:
- 检索所有学生名称,出生年份:
SELECT name, score FROM steudent;
- 检索所有学生名称,出生年份:
cursor.fetchall():
- 返回查询结果的所有(剩余)行,作为一个列表。如果没有可用的行,则返回一个空列表。
In [21]:
cursor.execute("SELECT * FROM student;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
Out[21]:
id | name | major | score | |
---|---|---|---|---|
0 | 0 | PJ | CS | 90.0 |
1 | 1 | John | Math | 90.0 |
2 | 2 | Jane | Math | 85.0 |
3 | 3 | Bob | English | 95.0 |
4 | 4 | Alice | History | 80.0 |
5 | 5 | Tom | Art | NaN |
6 | 6 | Mary | Music | 98.0 |
In [22]:
cursor.execute("SELECT name, score FROM student;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['name', 'score'])
Out[22]:
name | score | |
---|---|---|
0 | PJ | 90.0 |
1 | John | 90.0 |
2 | Jane | 85.0 |
3 | Bob | 95.0 |
4 | Alice | 80.0 |
5 | Tom | NaN |
6 | Mary | 98.0 |
过滤记录:SQL WHERE语句¶
- 通过WHERE语句对返回的记录进一步过滤:
SELECT [列名] FROM [表] WHERE [过滤器]
- 示例:
- 检索分数大于等于90的学生信息:
SELECT * FROM student WHERE score >= 90
- 检索分数大于等于90的学生信息:
In [23]:
cursor.execute("SELECT * FROM student WHERE score > 90;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
Out[23]:
id | name | major | score | |
---|---|---|---|---|
0 | 3 | Bob | English | 95 |
1 | 6 | Mary | Music | 98 |
关于WHERE语句的更多信息¶
- WHERE关键字支持所有自然比较操作
- (数值)操作符/关键字
- 等于
=
不等于<>
- 小于
<
小于等于<=
- 大于
>
大于等于>=
- 在范围内
BETWEEN ... AND ...
- 等于
In [24]:
cursor.execute("SELECT * FROM student WHERE score <> 80;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
Out[24]:
id | name | major | score | |
---|---|---|---|---|
0 | 0 | PJ | CS | 90 |
1 | 1 | John | Math | 90 |
2 | 2 | Jane | Math | 85 |
3 | 3 | Bob | English | 95 |
4 | 6 | Mary | Music | 98 |
In [25]:
cursor.execute("SELECT * FROM student WHERE score BETWEEN 80 AND 95;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
Out[25]:
id | name | major | score | |
---|---|---|---|---|
0 | 0 | PJ | CS | 90 |
1 | 1 | John | Math | 90 |
2 | 2 | Jane | Math | 85 |
3 | 3 | Bob | English | 95 |
4 | 4 | Alice | History | 80 |
更多关于WHERE语句的信息¶
WHERE
关键字还允许(有限的)正则表达式支持和集合成员- 集合成员运算符
IN
和NOT IN
- 正则表达式运算符
LIKE
- 使用LIKE关键字进行正则匹配,通配符
_
和%
_
匹配单个字符%
匹配任意字符(包括0个字符)
- 使用LIKE关键字进行正则匹配,通配符
- 集合成员运算符
In [26]:
cursor.execute("SELECT * FROM student WHERE major in ('English', 'Math')")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'age','major'])
Out[26]:
id | name | age | major | |
---|---|---|---|---|
0 | 1 | John | Math | 90 |
1 | 2 | Jane | Math | 85 |
2 | 3 | Bob | English | 95 |
In [27]:
cursor.execute("SELECT * FROM student WHERE name LIKE 'J%';")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'age', 'gender'])
Out[27]:
id | name | age | gender | |
---|---|---|---|---|
0 | 1 | John | Math | 90 |
1 | 2 | Jane | Math | 85 |
In [28]:
cursor.execute("SELECT * FROM student WHERE name LIKE 'J_hn';")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'age', 'gender'])
Out[28]:
id | name | age | gender | |
---|---|---|---|---|
0 | 1 | John | Math | 90 |
NULL匹配空字符串,¶
- 匹配字段NULL的情况。
- 注意,如果字段包含,比如说,‘ ’,那么NULL将不匹配那一行!
In [29]:
cursor.execute("SELECT * FROM student WHERE score is NULL;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score'])
Out[29]:
id | name | major | score | |
---|---|---|---|---|
0 | 5 | Tom | Art | None |
排序记录:SQL ORDER BY语句¶
- 对SELECT语句返回的记录进行排序:
SELECT [列] FROM [表] ORDER BY [列] [ASC|DESC]
- 示例
- 按分数检索ID和姓名:
SELECT id, name FROM student ORDER BY score DESC
- 注意:大多数实现默认按升序排序,但最好总是指定!
In [30]:
cursor.execute("SELECT id, name, score FROM student ORDER BY score DESC;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'score'])
Out[30]:
id | name | score | |
---|---|---|---|
0 | 6 | Mary | 98.0 |
1 | 3 | Bob | 95.0 |
2 | 0 | PJ | 90.0 |
3 | 1 | John | 90.0 |
4 | 2 | Jane | 85.0 |
5 | 4 | Alice | 80.0 |
6 | 5 | Tom | NaN |
更多过滤:DISTINCT¶
- 从返回的结果集中删除重复项:
SELECT DISTINCT [列] FROM [表]
- 示例:检索所有专业:
SELECT DISTINCT major FROM student
In [31]:
cursor.execute("SELECT DISTINCT major FROM student;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['Major'])
Out[31]:
Major | |
---|---|
0 | CS |
1 | Math |
2 | English |
3 | History |
4 | Art |
5 | Music |
聚合结果:GROUP BY¶
- 我想知道每个专业学生的平均成绩是多少?
In [32]:
cursor.execute("SELECT major, AVG(score) FROM student GROUP BY major;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['Major', 'Mean Score'])
Out[32]:
Major | Mean Score | |
---|---|---|
0 | Art | NaN |
1 | CS | 90.0 |
2 | English | 95.0 |
3 | History | 80.0 |
4 | Math | 87.5 |
5 | Music | 98.0 |
更多关于GROUP BY
的信息¶
GROUP BY
支持除AVG
之外的其他操作:COUNT
,SUM
,MIN
,MAX
称为聚合函数- 可以使用
HAVING
子语句在GROUP BY
之后过滤结果
In [33]:
cursor.execute("SELECT major, AVG(score) AS avg_score FROM student \
Group by major HAVING COUNT(*) > 1")
result = cursor.fetchall()
pd.DataFrame(result, columns=['major', 'AVG(score)'])
Out[33]:
major | AVG(score) | |
---|---|---|
0 | Math | 87.5 |
- 注意:
HAVING
关键字和WHERE
关键字的区别在于HAVING
在应用GROUP BY
之后操作。 AS
关键字只是让我们给聚合字段一个更好的名字。
合并表:JOIN¶
- (INNER) JOIN:返回两个表中都有匹配值的记录
SELECT * FROM student INNER JOIN st_info ON student.name=st_info.name;
In [34]:
cursor.execute("SELECT * FROM st_info;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['name', 'age', 'gender'])
Out[34]:
name | age | gender | |
---|---|---|---|
0 | Alice | 20 | Female |
1 | Bob | 22 | Male |
2 | Charlie | 21 | Male |
3 | David | 23 | Male |
4 | Eve | 22 | Female |
5 | Frank | 21 | Male |
In [35]:
cursor.execute("SELECT * FROM student INNER JOIN st_info ON student.name=st_info.name;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score', 'name', 'age', 'gender'])
Out[35]:
id | name | major | score | name | age | gender | |
---|---|---|---|---|---|---|---|
0 | 3 | Bob | English | 95 | Bob | 22 | Male |
1 | 4 | Alice | History | 80 | Alice | 20 | Female |
其他合并表的方式:OUTER JOIN¶
LEFT (OUTER) JOIN
:返回左表的所有记录和右表中匹配的记录RIGHT (OUTER) JOIN
:返回右表的所有记录和左表中匹配的记录FULL (OUTER) JOIN
:当左表或右表中有匹配时返回所有记录- https://www.w3schools.com/sql/sql_join.asp
In [36]:
cursor.execute("SELECT * FROM student LEFT OUTER JOIN st_info ON student.name=st_info.name;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score', 'name', 'age', 'gender'])
Out[36]:
id | name | major | score | name | age | gender | |
---|---|---|---|---|---|---|---|
0 | 0 | PJ | CS | 90.0 | None | NaN | None |
1 | 1 | John | Math | 90.0 | None | NaN | None |
2 | 2 | Jane | Math | 85.0 | None | NaN | None |
3 | 3 | Bob | English | 95.0 | Bob | 22.0 | Male |
4 | 4 | Alice | History | 80.0 | Alice | 20.0 | Female |
5 | 5 | Tom | Art | NaN | None | NaN | None |
6 | 6 | Mary | Music | 98.0 | None | NaN | None |
In [37]:
cursor.execute("SELECT * FROM student FULL OUTER JOIN st_info ON student.name=st_info.name;")
result = cursor.fetchall()
pd.DataFrame(result, columns=['id', 'name', 'major', 'score', 'name', 'age', 'gender'])
Out[37]:
id | name | major | score | name | age | gender | |
---|---|---|---|---|---|---|---|
0 | 0.0 | PJ | CS | 90.0 | None | NaN | None |
1 | 1.0 | John | Math | 90.0 | None | NaN | None |
2 | 2.0 | Jane | Math | 85.0 | None | NaN | None |
3 | 3.0 | Bob | English | 95.0 | Bob | 22.0 | Male |
4 | 4.0 | Alice | History | 80.0 | Alice | 20.0 | Female |
5 | 5.0 | Tom | Art | NaN | None | NaN | None |
6 | 6.0 | Mary | Music | 98.0 | None | NaN | None |
7 | NaN | None | None | NaN | Charlie | 21.0 | Male |
8 | NaN | None | None | NaN | David | 23.0 | Male |
9 | NaN | None | None | NaN | Eve | 22.0 | Female |
10 | NaN | None | None | NaN | Frank | 21.0 | Male |
In [38]:
cursor.close()
conn.close()
SQLite和PostgreSQL对比¶
- SQLite
- SQLite 是一个自包含的、无服务器的、零配置的嵌入式数据库引擎,
- 可以直接集成到应用程序中,不需要单独的数据库服务器。
- PostgreSQL
- PostgreSQL 是基于客户端-服务器架构的数据库系统,
- 这意味着数据库服务器和客户端应用程序可以运行在不同的机器上,支持多用户同时访问。
- 相较于 SQLite,PostgreSQL 能够处理更多的并发连接和事务,因此在要求高并发性能的应用场景中更为常见。
- PostgreSQL
psycopg2¶
psycopg2
是一个用于连接和操作 PostgreSQL 数据库的 Python 库。- 提供了与数据库进行交互的高效和灵活的方法
- 安装
pip install psycopg2
conda install anaconda::psycopg2
In [39]:
# sign in
import psycopg2
db_params = {
'dbname': 'data_science',
'user': 'postgres',
'password': '12345678',
'host': 'localhost', # 数据库主机地址
'port': '5432' # PostgreSQL默认端口
}
conn = psycopg2.connect(**db_params)
cursor = conn.cursor()
In [47]:
cursor.execute("DROP TABLE IF EXISTS scores;")
conn.commit()
In [48]:
cursor.execute("CREATE TABLE IF NOT EXISTS scores (id INT, name VARCHAR(50), score INT)")
conn.commit()
In [49]:
cursor.execute("INSERT INTO scores (id, name, score) VALUES (1, 'John', 89);")
conn.commit()
In [50]:
import pandas as pd
cursor.execute("SELECT * FROM scores;")
results = cursor.fetchall()
pd.DataFrame(results, columns=['id', 'name', 'score'])
Out[50]:
id | name | score | |
---|---|---|---|
0 | 1 | John | 89 |
In [51]:
data = [(2, 'PJ', 78),
(3, 'AJ', 85)]
cursor.executemany("INSERT INTO scores (id, name, score) VALUES (%s, %s, %s)", data)
conn.commit()
In [52]:
cursor.execute("SELECT * FROM scores;")
results = cursor.fetchall()
pd.DataFrame(results, columns=['id', 'name', 'score'])
Out[52]:
id | name | score | |
---|---|---|---|
0 | 1 | John | 89 |
1 | 2 | PJ | 78 |
2 | 3 | AJ | 85 |
In [53]:
cursor.close()
conn.close()