跳转到主要内容
ClickHouse 完全支持标准 SQL JOIN,可实现高效的数据分析。 在本指南中,你将借助维恩图以及基于归一化 IMDB 数据集的示例查询,了解一些常见的 JOIN 类型及其用法。该数据集源自关系型数据集存储库

测试数据和资源

有关如何创建并加载这些表的说明,请参见此处。 如果你不想在本地创建并加载这些表,也可以在 playground 中使用该数据集。 你将使用示例数据集中的以下四个表: 这四个表中的数据表示电影,一部电影可以对应一个或多个类型。 电影中的角色由演员扮演。 上图中的箭头表示外键与主键之间的关系。例如,genres 表中某一行的 movie_id 列包含 movies 表中某一行的 id 值。 电影和演员之间存在多对多关系。 通过使用 roles 表,这种多对多关系被归一化为两个一对多关系roles 表中的每一行都包含 movies 表和 actors 表中 id 列的值。

ClickHouse 支持的 JOIN 类型

ClickHouse 支持以下 JOIN 类型: 在接下来的各节中,你将为上述每种 JOIN 类型编写示例查询。

INNER JOIN

INNER JOIN 会针对每一对在连接键上匹配的行,返回由左表中该行的列值与右表中该行的列值组合而成的结果。 如果某一行有多个匹配项,则会返回所有匹配项 (这意味着对于连接键匹配的行,会产生笛卡尔积) 。 此查询通过将 movies 表与 genres 表进行连接,找出每部电影对应的类型:
可以省略 INNER 关键字。
使用以下其他 JOIN 类型,可以扩展或改变 INNER JOIN 的行为。

(LEFT / RIGHT / FULL) OUTER JOIN

LEFT OUTER JOIN 的行为类似于 INNER JOIN;此外,对于左表中没有匹配项的行,ClickHouse 会为右表的列返回默认值 RIGHT OUTER JOIN 查询与之类似,也会返回右表中没有匹配项的行的值,以及左表各列的默认值。 FULL OUTER JOIN 查询结合了 LEFTRIGHT OUTER JOIN,会返回左表和右表中没有匹配项的行的值,并分别附带右表和左表各列的默认值。
可以将 ClickHouse 配置为返回 NULL 而非默认值 (不过,出于性能原因,通常不建议这样做) 。
此查询会找出所有没有类型的电影:它查询 movies 表中在 genres 表里没有匹配项的所有行,因此这些行会在 movie_id 列中得到默认值 0 (在查询时) :
可省略 OUTER 关键字。

CROSS JOIN

CROSS JOIN 会生成两个表的完整笛卡尔积,不考虑连接键。 左表中的每一行都会与右表中的每一行相组合。 因此,下面的查询会将 movies 表中的每一行与 genres 表中的每一行组合:
虽然前面的示例查询本身意义不大,但可以通过添加 WHERE 子句进一步扩展,将匹配的行关联起来,模拟 INNER JOIN 的行为,以找出每部电影的类型:
CROSS JOIN 的另一种语法是在 FROM 子句中用逗号分隔多个表。 如果查询的 WHERE 子句中包含连接表达式,ClickHouse 会将 CROSS JOIN 重写INNER JOIN 你可以通过 EXPLAIN SYNTAX 查看该示例查询的这一点 (它会返回查询在被执行之前重写后的语法优化版本) :
在经过语法优化的 CROSS JOIN 查询版本中,INNER JOIN 子句包含显式添加的 ALL 关键字,以确保即使将其重写为 INNER JOIN,仍能保留 CROSS JOIN 的笛卡尔积语义;而对于 INNER JOIN,笛卡尔积是可以被禁用的。
而且,正如上文所述,对于 RIGHT OUTER JOINOUTER 关键字可以省略,也可以再加上可选的 ALL 关键字,因此你可以写成 ALL RIGHT JOIN,而且它同样能正常工作。

(LEFT / RIGHT) SEMI JOIN

LEFT SEMI JOIN 查询会返回左表中在右表里至少有一个连接键匹配的每一行的列值。 只返回找到的第一个匹配项 (笛卡尔积被禁用) 。 RIGHT SEMI JOIN 查询与之类似,会返回右表中在左表里至少有一个匹配项的所有行的值,但同样只返回找到的第一个匹配项。 此查询会找出所有在 2023 年参演过电影的男演员/女演员。 请注意,使用普通的 (INNER) 连接时,如果同一位男演员/女演员在 2023 年出演了多个角色,则会出现多次:

(LEFT / RIGHT) ANTI JOIN

LEFT ANTI JOIN 返回左表中所有未匹配行的列值。 同样,RIGHT ANTI JOIN 返回右表中所有未匹配行的列值。 前一个 OUTER JOIN 示例查询也可以改写为使用 ANTI JOIN,以查找数据集中没有流派的电影:

(LEFT / RIGHT / INNER) ANY JOIN

LEFT ANY JOINLEFT OUTER JOIN + LEFT SEMI JOIN 的组合,也就是说,ClickHouse 会返回左表中每一行的列值:如果能在右表中找到匹配行,就将其与该匹配行的列值组合;如果找不到匹配行,则与右表的默认列值组合。 如果左表中的某一行在右表中有多个匹配项,ClickHouse 只会返回与找到的第一个匹配项组合后的列值 (笛卡尔积已禁用) 。 同样,RIGHT ANY JOINRIGHT OUTER JOIN + RIGHT SEMI JOIN 的组合。 INNER ANY JOIN 则是禁用了笛卡尔积的 INNER JOIN 下面的示例使用两个通过 values 表函数 构造的临时表 (left_tableright_table) ,以一个抽象示例演示 LEFT ANY JOIN
这是使用 RIGHT ANY JOIN 的相同查询:
以下是使用 INNER ANY JOIN 的查询:

ASOF JOIN

ASOF JOIN 提供非精确匹配能力。 如果左表中的某一行在右表中没有精确匹配项,则会改用右表中最接近的一行作为匹配结果。 这对于时间序列分析尤其有用,并且可以显著降低查询复杂度。 下面的示例展示了如何对股票市场数据进行时间序列分析。 quotes 表包含股票代码在一天中各个特定时刻的报价。 在示例数据中,价格每 10 秒更新一次。 trades 表列出了股票代码的交易记录——某个代码的特定成交量在特定时刻被买入: 为了计算每笔交易的实际成本,我们需要将交易与时间上最接近的报价相匹配。 使用 ASOF JOIN 可以轻松而简洁地实现这一点:使用 ON 子句指定精确匹配条件,使用 AND 子句指定最接近匹配条件——对于某个特定代码 (精确匹配) ,需要从 quotes 表中找到时间“最接近”、且时间等于或早于该代码某笔交易时间的那一行 (非精确匹配) :
ASOF JOINON 子句是必需的,用于指定一个精确匹配条件,与 AND 子句中的非精确匹配条件配合使用。

摘要

本指南介绍 ClickHouse 如何支持所有标准 SQL JOIN 类型,以及专为分析查询设计的特殊 JOIN。 有关 JOIN 的更多信息,请参阅 JOIN 语句文档。
最后修改于 2026年6月12日