Skip to content

Files

Latest commit

 

History

History
453 lines (336 loc) · 12.2 KB

9.3.md

File metadata and controls

453 lines (336 loc) · 12.2 KB

SQL 连接

原文:https://www.bookbookmark.ds100.org/ch/09/sql_joins.html

# HIDDEN
# Clear previously defined variables
%reset -f

# Set directory for data loading to work properly
import os
os.chdir(os.path.expanduser('~/notebooks/09'))
# HIDDEN
import warnings
# Ignore numpy dtype warnings. These warnings are caused by an interaction
# between numpy and Cython and can be safely ignored.
# Reference: https://stackoverflow.com/a/40846742
warnings.filterwarnings("ignore", message="numpy.dtype size changed")
warnings.filterwarnings("ignore", message="numpy.ufunc size changed")

import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
%matplotlib inline
import ipywidgets as widgets
from ipywidgets import interact, interactive, fixed, interact_manual
import nbinteract as nbi

sns.set()
sns.set_context('talk')
np.set_printoptions(threshold=20, precision=2, suppress=True)
pd.options.display.max_rows = 7
pd.options.display.max_columns = 8
pd.set_option('precision', 2)
# This option stops scientific notation for pandas
# pd.set_option('display.float_format', '{:.2f}'.format)
# HIDDEN
# Make names table
sql_expr = """
CREATE TABLE names(
    cat_id INTEGER PRIMARY KEY,
    name TEXT);
"""
result = sqlite_engine.execute(sql_expr)

# Populate names table
sql_expr = """
INSERT INTO names VALUES 
(0, "Apricot"),
(1, "Boots"),
(2, "Cally"),
(4, "Eugene");
"""

result = sqlite_engine.execute(sql_expr)
# HIDDEN
# Make colors table
sql_expr = """
CREATE TABLE colors(
    cat_id INTEGER PRIMARY KEY,
    color TEXT);
"""
result = sqlite_engine.execute(sql_expr)

# Populate colors table
sql_expr = """
INSERT INTO colors VALUES 
(0, "orange"),
(1, "black"),
(2, "calico"),
(3, "white");
"""

result = sqlite_engine.execute(sql_expr)
# HIDDEN
# Make ages table
sql_expr = """
CREATE TABLE ages(
    cat_id INTEGER PRIMARY KEY,
    age INT);
"""
result = sqlite_engine.execute(sql_expr)

# Populate ages table
sql_expr = """
INSERT INTO ages VALUES 
(0, 4),
(1, 3),
(2, 9),
(4, 20);
"""

result = sqlite_engine.execute(sql_expr)

pandas中,我们使用pd.merge方法在两个表的列中使用匹配的值连接两个表。例如:

pd.merge(table1, table2, on='common_column')

在本节中,我们将介绍 SQL 连接。SQL 连接用于组合关系数据库中的多个表。

假设我们是猫店的老板,有一个我们店里猫的数据库。我们有两个不同的表:namescolorsnames表包含列cat_id、分配给每个 cat 的唯一编号和 cat 的名称namecolors表包含列cat_idcolor,每只猫的颜色。

请注意,两个表中都有一些缺少的行-names表中缺少一行带有cat_id3,而colors表中缺少一行带有cat_id4。

**names**
猫科动物 名称
1 个 靴子
凯利
尤金
**colors**
cat_id 颜色
0 橙色
1 黑色
2 印花布
白色

要计算名为杏的猫的颜色,我们必须在两个表中使用信息。我们可以将 _ 连接到 _ 列上的表,用namecolor创建一个新表。

连接

连接通过在表的列中匹配值来组合表。

连接有四种主要类型:内部连接、外部连接、左侧连接和右侧连接。尽管这四个表都是组合表,但每个表对不匹配的值的处理方式不同。

内部连接

定义:在内部连接中,最终表只包含在两个表中具有匹配列的行。

Inner Join

**示例:**我们希望将namescolors表连接在一起,以匹配每只猫的颜色。因为这两个表都包含一个cat_id列,该列是 cat 的唯一标识符,所以我们可以在cat_id列上使用内部连接。

**sql:**要在 sql 中编写内部连接,我们修改了FROM子句以使用以下语法:

SELECT ...
FROM <TABLE_1>
    INNER JOIN <TABLE_2>
    ON <...>

例如:

SELECT *
FROM names AS N
    INNER JOIN colors AS C
    ON N.cat_id = C.cat_id;
cat_id name cat_id color
0 Apricot 0 orange
--- --- --- --- ---
1 个 1 Boots 1 black
--- --- --- --- ---
2 Cally 2 calico
--- --- --- --- ---

您可以验证每个 cat 名称是否与其颜色匹配。请注意,结果表中不存在带有cat_id3 和 4 的 cats,因为colors表没有带有cat_id4 的行,而names表没有带有cat_id3 的行。在内部连接中,如果一行在另一个表中没有匹配的值,则该行不包括在最终结果中。

假设我们有一个名为names的数据帧和一个名为colors的数据帧,我们可以通过编写以下内容在pandas中执行内部连接:

pd.merge(names, colors, how='inner', on='cat_id')

全/外接

**定义:**在完全连接(有时称为外部连接)中,两个表中的所有记录都包含在连接表中。如果一行在另一个表中没有匹配项,则用NULL填充缺少的值。

Full outer join

**示例:**和前面一样,我们将namescolors表连接在一起,以匹配每只猫的颜色。这一次,我们希望将所有行保留在两个表中,即使没有匹配的行。

**sql:**要在 sql 中编写外部连接,我们修改了FROM子句,以使用以下语法:

SELECT ...
FROM <TABLE_1>
    FULL JOIN <TABLE_2>
    ON <...>

For example:

SELECT name, color
FROM names N
    FULL JOIN colors C
    ON N.cat_id = C.cat_id;
cat_id name color
0 Apricot orange
1 Boots black
2 Cally calico
3 无效的 white
4 Eugene NULL

请注意,最终输出包含带有cat_id3 和 4 的条目。如果一行没有匹配项,它仍然包含在最终输出中,并且任何缺少的值都用NULL填充。

pandas中:

pd.merge(names, colors, how='outer', on='cat_id')

左连接

定义:在左连接中,来自左表的所有记录都包含在连接表中。如果行在右表中没有匹配项,则缺少的值将用NULL填充。

left join

**示例:**和前面一样,我们将namescolors表连接在一起,以匹配每只猫的颜色。这次,我们要保留所有的猫名,即使一只猫没有匹配的颜色。

**sql:**要在 sql 中编写左连接,我们修改了FROM子句以使用以下语法:

SELECT ...
FROM <TABLE_1>
    LEFT JOIN <TABLE_2>
    ON <...>

For example:

SELECT name, color
FROM names N
    LEFT JOIN colors C
    ON N.cat_id = C.cat_id;
cat_id name color
0 Apricot orange
1 Boots black
2 Cally calico
4 Eugene NULL

请注意,最终输出包括所有四个 cat 名称。names关系中的三个cat_ids 在colors表中与cat_ids 匹配,一个不匹配(eugene)。没有匹配颜色的猫名的颜色为NULL

In pandas:

pd.merge(names, colors, how='left', on='cat_id')

右连接

定义:在右连接中,来自右表的所有记录都包含在连接表中。如果左表中的行不匹配,则用NULL填充缺少的值。

right join

**示例:**和前面一样,我们将namescolors表连接在一起,以匹配每只猫的颜色。这一次,我们要保留所有的猫的颜色,即使一只猫没有匹配的名字。

**sql:**要在 sql 中编写正确的 join,我们修改了FROM子句以使用以下语法:

SELECT ...
FROM <TABLE_1>
    RIGHT JOIN <TABLE_2>
    ON <...>

For example:

SELECT name, color
FROM names N
    RIGHT JOIN colors C
    ON N.cat_id = C.cat_id;
cat_id name color
0 Apricot orange
1 Boots black
2 Cally calico
3 NULL white

这一次,观察最终输出包括所有四种 CAT 颜色。colors关系中的三个cat_ids 与names表中的cat_ids 匹配,一个不匹配(白色)。没有匹配名称的 cat 颜色的名称为NULL

您还可能注意到,右连接产生的结果与交换表顺序的左连接相同。即,names左接colorscolors右接names相同。因此,一些 SQL 引擎(如 sqlite)不支持右连接。

In pandas:

pd.merge(names, colors, how='right', on='cat_id')

隐式内部连接

在 SQL 中通常有多种方法来完成同一个任务,就像在 Python 中有多种方法来完成同一个任务一样。我们指出了另一种编写内部连接的方法,这种方法在实践中出现,称为 _ 隐式连接 _。回想一下,我们之前编写了以下内容来进行内部连接:

SELECT *
FROM names AS N
    INNER JOIN colors AS C
    ON N.cat_id = C.cat_id;

隐式内部连接的语法稍有不同。请特别注意,FROM子句使用逗号从两个表中进行选择,并且查询包含一个WHERE子句来指定连接条件。

SELECT *
FROM names AS N, colors AS C
WHERE N.cat_id = C.cat_id;

当在FROM子句中指定多个表时,SQL 将创建一个表,其中包含每个表中的每一行组合。例如:

sql_expr = """
SELECT *
FROM names N, colors C
"""
pd.read_sql(sql_expr, sqlite_engine)
cat_id name cat_id color
0 0 Apricot 0 orange
--- --- --- --- ---
1 0 Apricot 1 black
--- --- --- --- ---
2 0 Apricot 2 calico
--- --- --- --- ---
0 Apricot 3 white
--- --- --- --- ---
1 Boots 0 orange
--- --- --- --- ---
5 个 1 Boots 1 black
--- --- --- --- ---
1 Boots 2 calico
--- --- --- --- ---
1 Boots 3 white
--- --- --- --- ---
8 个 2 Cally 0 orange
--- --- --- --- ---
2 Cally 1 black
--- --- --- --- ---
10 个 2 Cally 2 calico
--- --- --- --- ---
11 个 2 Cally 3 white
--- --- --- --- ---
12 个 4 Eugene 0 orange
--- --- --- --- ---
十三 4 Eugene 1 black
--- --- --- --- ---
十四 4 Eugene 2 calico
--- --- --- --- ---
15 个 4 Eugene 3 white
--- --- --- --- ---

此操作通常称为 _ 笛卡尔积 _:第一个表中的每一行都与第二个表中的每一行成对出现。请注意,许多行包含的 cat 颜色与它们的名称不匹配。隐式连接中的附加WHERE子句筛选出没有匹配cat_id值的行。

SELECT *
FROM names AS N, colors AS C
WHERE N.cat_id = C.cat_id;
cat_id name cat_id color
0 0 Apricot 0 orange
--- --- --- --- ---
1 1 Boots 1 black
--- --- --- --- ---
2 2 Cally 2 calico
--- --- --- --- ---

连接多个表

若要连接多个表,请使用附加的JOIN运算符扩展FROM子句。例如,下表ages包括每只猫的年龄数据。

cat_id 年龄
0 4
1 3
2
4 20 个

要在namescolorsages表上执行内部连接,我们编写:

# Joining three tables

sql_expr = """
SELECT name, color, age
    FROM names n
    INNER JOIN colors c ON n.cat_id = c.cat_id
    INNER JOIN ages a ON n.cat_id = a.cat_id;
"""
pd.read_sql(sql_expr, sqlite_engine)
name color age
0 Apricot orange 4
--- --- --- ---
1 Boots black 3
--- --- --- ---
2 Cally calico 9
--- --- --- ---

摘要

我们已经介绍了四种主要的 SQL 连接类型:内部连接、完整连接、左连接和右连接。我们使用所有四个连接将信息组合在单独的关系中,并且每个连接只在处理输入表中不匹配行的方式上有所不同。