跳转到主要内容
对于包含数组列的表,一个常见操作是生成一张新表:原始列中的每个数组元素各自占据一行,同时其他列的值会被复制。这正是 ARRAY JOIN 子句最基本的用法。 它之所以得名,是因为可以将其视为对数组或嵌套数据结构执行 JOIN。其用途与 arrayJoin 函数类似,但子句的功能更为广泛。 语法:
下面列出了受支持的 ARRAY JOIN 类型:
  • ARRAY JOIN - 在默认情况下,空数组不会包含在 JOIN 结果中。
  • LEFT ARRAY JOIN - JOIN 结果中会包含带空数组的行。对于空数组,其值会被设置为该数组元素类型的默认值 (通常为 0、空字符串或 NULL) 。

ARRAY JOIN 基本示例

ARRAY JOIN 和 LEFT ARRAY JOIN

以下示例展示了 ARRAY JOINLEFT ARRAY JOIN 子句的用法。我们先创建一个包含 Array 类型列的表,并向其中插入值:
下面的示例使用了 ARRAY JOIN 子句:
下面的示例使用 LEFT ARRAY JOIN 子句:

ARRAY JOIN 与 arrayEnumerate 函数

此函数通常与 ARRAY JOIN 搭配使用。它可以在应用 ARRAY JOIN 后,对每个数组只统计一次。示例:
在此示例中,Reaches 表示转化次数 (即应用 ARRAY JOIN 后得到的字符串数量) ,而 Hits 表示页面浏览量 (即 ARRAY JOIN 之前的字符串数量) 。在这种情况下,你也可以用更简单的方式得到相同结果:

ARRAY JOIN 和 arrayEnumerateUniq

此函数在使用 ARRAY JOIN 并对数组元素进行聚合时非常有用。 在此示例中,会计算每个目标 ID 的转化次数 (Goals 嵌套数据结构中的每个元素都是一个已达成的目标,我们称其为一次转化) 以及 session 数量。如果不使用 ARRAY JOIN,我们会将 session 数量统计为 sum(Sign)。但在这个特定场景中,行被嵌套的 Goals 结构展开了,因此为了确保之后每个 session 只被计数一次,我们对 arrayEnumerateUniq(Goals.ID) 函数的返回值应用了条件。

使用别名

可以为 ARRAY JOIN 子句中的数组指定别名。在这种情况下,可以通过该别名访问数组元素,而数组本身仍使用原始名称访问。示例:
借助别名,您可以对外部数组执行 ARRAY JOIN。例如:
多个数组可以在 ARRAY JOIN 子句中用逗号分隔。在这种情况下,会同时对这些数组执行 JOIN (即直接和,而不是笛卡儿积) 。请注意,默认情况下,所有数组的大小都必须相同。示例:
以下示例使用 arrayEnumerate 函数:
可以使用 SETTINGS enable_unaligned_array_join = 1 对多个大小不同的数组进行 join。示例:

ARRAY JOIN 与嵌套数据结构配合使用

ARRAY JOIN 也可用于嵌套数据结构
ARRAY JOIN 中指定嵌套数据结构的名称时,其含义与对该结构所包含的所有数组元素执行 ARRAY JOIN 相同。示例如下:
这种写法也可以:
可以为嵌套数据结构指定别名,以便选择 JOIN 结果或源数组。示例:
arrayEnumerate 函数使用示例:

实现细节

执行 ARRAY JOIN 时,查询的执行顺序会被优化。虽然在查询中,ARRAY JOIN 必须始终写在 WHERE/PREWHERE 子句之前,但从技术上讲,它们实际上可以按任意顺序执行,除非过滤会用到 ARRAY JOIN 的结果。执行顺序由查询优化器控制。

与短路函数求值不兼容

短路函数求值 是一项功能,用于优化 ifmultiIfandor 等特定函数中复杂表达式的执行。它可以防止这些函数在执行过程中出现潜在异常,例如除以零。 arrayJoin 始终会被执行,因此不支持短路函数求值。这是因为它是一个特殊函数,在查询分析和执行期间会独立于其他所有函数单独处理,并且需要额外的逻辑,而这套逻辑无法与短路函数执行兼容。原因在于,结果中的行数取决于 arrayJoin 的结果,而为 arrayJoin 实现惰性执行过于复杂且代价过高。
最后修改于 2026年6月25日