ARRAY JOIN 子句
对于包含数组列的表,生成一个新表的操作是很常见的,该新表的每一行都包含初始列的每个单独数组元素,而其他列的值则被重复。这就是 ARRAY JOIN
子句的基本情况。
其名称源于可以将其视为与数组或嵌套数据结构执行 JOIN
。其意图与 arrayJoin 函数相似,但该子句的功能更广泛。
语法:
支持的 ARRAY JOIN
类型如下所示:
ARRAY JOIN
- 在基本 case 下,空数组不包含在JOIN
的结果中。LEFT ARRAY JOIN
-JOIN
的结果包含带有空数组的行。空数组的值设置为数组元素类型的默认值(通常为 0、空字符串或 NULL)。
基本 ARRAY JOIN 示例
ARRAY JOIN 和 LEFT ARRAY JOIN
下面的示例展示了 ARRAY JOIN
和 LEFT 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 嵌套数据结构中的每个元素是一个达成的目标,我们称之为转换)和会话数量。如果没有 ARRAY JOIN
,我们将会话数量计算为 sum(Sign)。但在此特定情况下,行数是由嵌套 Goals 结构乘以的,因此为了在这之后计算每个会话一次,我们对 arrayEnumerateUniq(Goals.ID)
函数的值应用条件。
使用别名
可以为 ARRAY JOIN
子句中的数组指定别名。在这种情况下,可以通过该别名访问数组项目,但数组本身仍通过原始名称访问。示例:
使用别名,您可以通过外部数组执行 ARRAY JOIN
。例如:
在 ARRAY JOIN
子句中,可以用逗号分隔多个数组。在这种情况下,会同时对它们执行 JOIN
(直接和,而不是笛卡尔积)。请注意,默认情况下,所有数组必须具有相同的大小。示例:
下面的示例使用 arrayEnumerate 函数:
可以通过使用:SETTINGS enable_unaligned_array_join = 1
将不同大小的多个数组连接起来。示例:
ARRAY JOIN 与嵌套数据结构
ARRAY JOIN
同样适用于 嵌套数据结构:
在 ARRAY JOIN
中指定嵌套数据结构的名称时,含义与其包含的所有数组元素的 ARRAY JOIN
相同。下面列出了示例:
这个变体也有意义:
可以为嵌套数据结构使用别名,以选择要么 JOIN
的结果,要么源数组。示例:
使用 arrayEnumerate 函数的示例:
实现细节
在运行 ARRAY JOIN
时,查询执行顺序是优化的。虽然 ARRAY JOIN
必须始终在查询中指定在 WHERE/PREWHERE 子句之前,但从技术上讲,它们可以以任何顺序执行,除非 ARRAY JOIN
的结果用于过滤。处理顺序由查询优化器控制。
与短路函数求值的不兼容性
短路函数求值 是一种优化特定函数(如 if
、multiIf
、and
和 or
)中复杂表达式执行的特性。它防止在执行这些函数的过程中出现潜在异常,例如除以零。
arrayJoin
始终被执行,并且不支持短路函数求值。这是因为它是一个独特的函数,在查询分析和执行中与所有其他函数分开处理,并且需要额外的逻辑,这与短路函数执行不兼容。原因在于,结果中的行数取决于 arrayJoin
的结果,懒惰地执行 arrayJoin
实现起来太复杂且成本太高。