> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# join_* 会话设置

> 自动生成的 join_* 分组中的 ClickHouse 会话设置。

export const VersionHistory = ({rows = []}) => {
  if (rows.length === 0) {
    return null;
  }
  const headers = ["版本", "默认值", "注释"];
  const border = "1px solid rgba(128, 128, 128, 0.3)";
  const cell = {
    border,
    padding: "0.25rem 0.5rem",
    textAlign: "start",
    verticalAlign: "top"
  };
  return <details className="not-prose" style={{
    border,
    borderRadius: "0.5rem",
    margin: "0.5rem 0",
    padding: "0.5rem 0.75rem",
    fontSize: "0.8125rem",
    lineHeight: "1.125rem"
  }}>
      <summary style={{
    cursor: "pointer",
    fontWeight: 600,
    opacity: 0.72
  }}>
        版本历史
      </summary>
      <table style={{
    borderCollapse: "collapse",
    width: "100%",
    margin: "0.5rem 0 0"
  }}>
        <thead>
          <tr>
            {headers.map(header => <th key={header} style={{
    ...cell,
    fontWeight: 600,
    opacity: 0.72
  }}>
                {header}
              </th>)}
          </tr>
        </thead>
        <tbody>
          {rows.map((row, row_index) => <tr key={row.id ?? row_index}>
              {(row.items ?? []).map((item, item_index) => <td key={item_index} style={{
    ...cell,
    overflowWrap: "anywhere"
  }}>
                  {item?.label}
                </td>)}
            </tr>)}
        </tbody>
      </table>
    </details>;
};

export const SettingsInfoBlock = ({type, default_value, changeable_without_restart}) => {
  return <div className="not-prose" style={{
    display: "flex",
    flexWrap: "wrap",
    alignItems: "baseline",
    columnGap: "0.5rem",
    rowGap: "0.125rem",
    margin: "0.375rem 0",
    fontSize: "0.8125rem",
    lineHeight: "1.125rem"
  }}>
      <div style={{
    fontWeight: 600,
    opacity: 0.72
  }}>类型</div>
      <div style={{
    overflowWrap: "anywhere"
  }}>{type}</div>
      <div style={{
    fontWeight: 600,
    opacity: 0.72,
    marginInlineStart: "0.5rem"
  }}>默认值</div>
      <div style={{
    overflowWrap: "anywhere"
  }}>{default_value}</div>
      {changeable_without_restart && <div style={{
    fontWeight: 600,
    opacity: 0.72,
    marginInlineStart: "0.5rem"
  }}>
          无需重启即可更改
        </div>}
      {changeable_without_restart && <div style={{
    overflowWrap: "anywhere"
  }}>
          {changeable_without_restart}
        </div>}
    </div>;
};

这些设置可在 [system.settings](/zh/reference/system-tables/settings) 中查看，且由 [source](https://github.com/ClickHouse/ClickHouse/blob/master/src/Core/Settings.cpp) 自动生成。

## join\_algorithm

<SettingsInfoBlock type="JoinAlgorithm" default_value="direct,parallel_hash,hash,ie_join" />

<VersionHistory rows={[{"id": "row-1","items": [{"label": "26.8"},{"label": "direct,parallel_hash,hash,ie_join"},{"label": "将 `ie_join` 追加到默认列表，因此 `ON` 部分仅包含不等式条件的连接将使用 IEJoin 执行，而非使用带过滤器的 `CROSS JOIN`。由于它位于最后，仅在其他算法不适用时才会使用。"}]}, {"id": "row-2","items": [{"label": "24.12"},{"label": "direct,parallel_hash,hash"},{"label": "已弃用 'default'，改为显式指定 join 算法，同时现在优先使用 parallel_hash 而不是 hash"}]}]} />

指定使用哪种 [JOIN](/zh/reference/statements/select/join) 算法。

可以指定多种算法，系统会根据具体查询的 kind/严格性 和表引擎选择可用的算法。

基于哈希的算法是否落盘不属于此选择的一部分：[`max_bytes_before_external_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_before_external_join) / [`max_bytes_ratio_before_external_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_ratio_before_external_join) 是所有这些算法的落盘阈值 (且其中任一项非零后，`enable_adaptive_memory_spill_scheduler` 可在内存压力下更早地将 join 落盘) ，而 [`max_rows_in_join`](/zh/reference/settings/session-settings/max-rows#max_rows_in_join) / [`max_bytes_in_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_in_join) 是所有这些算法的硬上限，除非 `legacy_join_size_limits_trigger_spilling` 将这两个上限重新变为磁盘落盘触发条件。您选择的值决定 join 如何落盘：`grace_hash` 从第一个块起就对右表进行分区，`hash` 和 `parallel_hash` 则先在内存中收集数据，并在超过阈值后切换。

大多数算法仅在被选用于查询时才会影响查询。不过，有些算法仅因被列出就会改变规划 — 即使它们只是最终未被选中的低优先级回退选项 — 因为决策会在选择算法之前作出。此类影响有两种：

* 连接键类型推断会变得更严格 (例如，merge join 无法连接不同类型的键，如 `String` 和 `Nullable(String)`) 。这可能会更改 `USING` 列的结果类型，并可能导致与 `Join` 引擎表的连接因 `TYPE_MISMATCH` 而失败。由 `full_sorting_merge` 和 `parallel_full_sorting_merge` 触发。
* 连接中保留侧的 `ORDER BY ... LIMIT` 会执行显式排序，而不是按主键顺序读取，因为假定连接会破坏有序读取 (merge join 会插入自己的连接前排序；partial merge join 会重新排序左侧块；能够生成延迟块的 join 也不会传播有序读取) 。结果相同，但计划效率较低。由 `full_sorting_merge`、`parallel_full_sorting_merge`、`partial_merge`、`prefer_partial_merge`、`grace_hash` 和 `auto` 触发；非零的 `max_bytes_before_external_join` / `max_bytes_ratio_before_external_join` 也会触发。

即使查询最终使用 `hash` 或其他算法运行，两者仍会生效。如果不希望如此，请勿在受影响查询的 `join_algorithm` 中列出上述算法。

可能的值：

* grace\_hash

使用 [Grace hash join](https://en.wikipedia.org/wiki/Hash_join#Grace_hash_join)。Grace hash 提供了一种算法选项，可在限制内存使用的同时，高效处理复杂连接。

`grace_hash` 从第一个块起即采用外部算法：它会立即对右表进行分区，而 `hash` 和 `parallel_hash` 会先在内存中收集数据，仅在超过落盘阈值后才进行分区。如果您已知右侧数据无法容纳于内存中，并希望跳过内存阶段，请选择它。落盘阈值本身与所有哈希算法使用的阈值相同，即 [`max_bytes_before_external_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_before_external_join) / [`max_bytes_ratio_before_external_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_ratio_before_external_join)，并且两者之一必须非零，除非启用了 `legacy_join_size_limits_trigger_spilling`。如果没有阈值，`grace_hash` 会被跳过并使用列表中的下一个算法；如果它是唯一算法，则会被拒绝。

grace join 的第一阶段会读取右表，并根据键列的哈希值将其拆分为 N 个桶 (初始时，N 为 `grace_hash_join_initial_buckets`) 。这样可确保每个桶都能独立处理。第一个桶中的行会添加到内存中的哈希表，其他桶则保存到磁盘。如果哈希表增长超过溢写阈值，则会增加桶的数量，并重新分配每行所属的桶。任何不属于当前桶的行都会被刷新并重新分配。

支持 `INNER/LEFT/RIGHT/FULL ALL/ANY JOIN`。

* hash

使用 [哈希连接算法](https://en.wikipedia.org/wiki/Hash_join)。这是最通用的实现，支持所有类型和严格性的组合，以及在 `JOIN ON` 部分通过 `OR` 组合的多个连接键。

使用 `hash` 算法时，`JOIN` 的右侧部分会加载到 RAM 中。

* parallel\_hash

`hash` join 的一种变体，会将数据拆分为多个桶，并同时构建多个哈希表而非一个哈希表，以加快此过程。

使用 `parallel_hash` 算法时，`JOIN` 的右侧部分会加载到 RAM 中。

* partial\_merge

[sort-merge algorithm](https://en.wikipedia.org/wiki/Sort-merge_join) 的一种变体，其中仅右表会被完全排序。

`RIGHT JOIN` 和 `FULL JOIN` 仅支持 `ALL` 严格性 (`SEMI`、`ANTI`、`ANY` 和 `ASOF` 不受支持)。

使用 `partial_merge` 算法时，ClickHouse 会对数据进行排序并将其转储到磁盘。ClickHouse 中的 `partial_merge` 算法与经典实现略有不同。首先，ClickHouse 会按连接键对右表数据进行分块排序，并为已排序的块创建 min-max 索引。然后，它会按 `连接键` 对左表的分片进行排序，并将其与右表连接。min-max 索引还用于跳过不需要的右表块。

* direct

`direct` (也称为 nested loop) 算法会使用左表中的行作为键，在右表中执行 lookup。
它适用于 [Dictionary](/zh/reference/engines/table-engines/special/dictionary)、[EmbeddedRocksDB](/zh/reference/engines/table-engines/integrations/embedded-rocksdb) 和 [MergeTree](/zh/reference/engines/table-engines/mergetree-family/mergetree) 表等特殊存储。

对于 MergeTree 表，该算法会将连接键过滤条件直接下推到存储层。如果该键可以利用表的主键索引进行 lookup，这种方式会更高效；否则，它会针对左表的每个块对右表执行全表扫描。

支持 `INNER` 和 `LEFT` join，并且仅支持不带其他条件的单列等值连接键。

* auto

设置为 `auto` 时，会先尝试 `hash` join；如果超出内存限制，则会动态切换到其他算法。

* full\_sorting\_merge

[Sort-merge algorithm](https://en.wikipedia.org/wiki/Sort-merge_join)，会在连接前对参与连接的表进行完全排序。

* ie\_join

基于排序的 [IEJoin](https://vldb.org/pvldb/vol8/p2074-khayyat.pdf) 算法，适用于其 `ON` 部分包含两个参与连接表表达式之间不等比较 (`<`、`<=`、`>`、`>=`) 的 `JOIN`。支持 `ALL INNER/LEFT/RIGHT/FULL JOIN` 和 `SEMI`/`ANTI` `LEFT/RIGHT JOIN`。

在列表中的位置决定优先级：若列在其他算法之后，仅当其他算法不适用时才使用 IEJoin (即 `ON` 部分没有等值条件)；若列在首位，只要 `ON` 部分包含两个不等式条件便会使用它。对于 `ALL INNER JOIN`，其余条件 (包括等值条件) 会作为过滤器应用于连接结果；对于其他 kind，则会在 operator 内部作为影响匹配的残余条件进行评估。当 `ON` 部分包含超过两个符合条件的不等式条件时，算法使用的两个条件会根据列 min/max 统计信息估算的选择性来选定 (参见 [Column statistics](/zh/reference/engines/table-engines/mergetree-family/mergetree#column-statistics) 中的 `basic` 类型)；当估算不可用时 (没有统计信息，或禁用了 [`use_statistics`](/zh/reference/settings/session-settings/use-statistics#use_statistics))，则使用语法顺序中的前两个条件。如果列表中没有 `ie_join`，仅含不等条件的 `INNER JOIN` 会作为带过滤器的 `CROSS JOIN` 执行，其他 kind 不受支持。

两个输入都会在连接前累积到内存中：[`max_rows_in_join`](/zh/reference/settings/session-settings/max-rows#max_rows_in_join) 和 [`max_bytes_in_join`](/zh/reference/settings/session-settings/max-bytes#max_bytes_in_join) 限制两侧累积输入的总量 (而非仅限右侧) ，溢出时的处理方式由 [`join_overflow_mode`](/zh/reference/settings/session-settings/join#join_overflow_mode) 设置；operator 在累积输入上构建的排序索引不计入该限制。join operator 本身以单线程运行；仅输入在 join 前的排序会并行执行。

* parallel\_full\_sorting\_merge

与 `full_sorting_merge` 相同，但哈希兼容的等值 join 会根据连接键的哈希分片为相互独立的每分片 merge join，并行运行 (最多使用 `max_threads`) ，而非作为单个 merge join 运行。这在使用所有线程的同时保留了 merge join 较低的流式内存使用量，且结果不保证有序。

仅对键类型的哈希与 merge join 比较结果一致的普通等值 join 应用基于连接键的哈希分片，并且仅在两侧均未排序时应用。以下情况会跳过：

* `ASOF` join，以及浮点数 / `JSON` / `Object` / `Dynamic` 键类型：其哈希与 merge join 比较结果不一致，因此相等的键可能落在不同分片中。
* 已排序的侧 (按顺序读取的 MergeTree，或任何预排序输入)：保序地将数据分散到每分片 merge join 中可能会导致管道死锁。因此会保留按顺序读取及其 `read_in_order_use_virtual_row` 优化。
* 当 initiator 构建分布式查询计划 (`make_distributed_plan`) 时，因为分散排序无法序列化以供远程执行。本地单片段计划和每个 worker 的片段会在禁用该设置后重新优化，因此仍可进行分片。

跳过它只会禁用这一 rewrite，而不会禁用整体的 parallelism：join 会作为单个 `full_sorting_merge` 运行；当启用 `query_plan_join_shard_by_pk_ranges` 时，按顺序读取的 MergeTree 侧仍可在数据源处按主键 ranges 进行分片 (这些 ranges 按与 join 相同的比较方式排序，因此相等的键会保持在一起)。

* prefer\_partial\_merge

ClickHouse 总是尽可能使用 `partial_merge` join，否则使用 `hash`。*已弃用*，等同于 `partial_merge,hash`。

* default (已弃用)

遗留值，请不要再使用。
等同于 `direct,hash`，即依次尝试使用 direct join 和 hash join。

## join\_any\_take\_last\_row

<SettingsInfoBlock type="Bool" default_value="0" />

当右表中某个键对应多于一条匹配行时，此设置会更改具有 `ANY` 严格性 的 JOIN 操作行为。

<Note>
  此设置适用于 [`Join`](/zh/reference/engines/table-engines/special/join) 引擎表以及基于哈希的 JOIN 算法。

  如果 join 是并行构建的，行的顺序可能是非确定性的。这意味着对于 `ANY JOIN` 查询，`join_any_take_last_row = 1` 可能会返回非确定性的行。
</Note>

可能的值：

* 0 — 如果右表中某个键对应多于一条匹配行，则仅关联找到的第一行。
* 1 — 如果右表中某个键对应多于一条匹配行，则仅关联找到的最后一行。

另请参阅：

* [JOIN clause](/zh/reference/statements/select/join)
* [Join 表引擎](/zh/reference/engines/table-engines/special/join)
* [join\_default\_strictness](/zh/reference/settings/session-settings/join#join_default_strictness)

## join\_default\_strictness

<SettingsInfoBlock type="JoinStrictness" default_value="ALL" />

设置 [JOIN 子句](/zh/reference/statements/select/join)的默认严格性。

可能的值：

* `ALL` — 如果右表有多行匹配，ClickHouse 会根据匹配行创建[笛卡尔积](https://en.wikipedia.org/wiki/Cartesian_product)。这是标准 SQL 中 `JOIN` 的常规行为。
* `ANY` — 如果右表有多行匹配，则只会连接找到的第一行。如果右表只有一行匹配，则 `ANY` 和 `ALL` 的结果相同。
* `ASOF` — 用于连接匹配关系不确定的序列。
* `Empty string` — 如果查询中未指定 `ALL` 或 `ANY`，ClickHouse 会抛出异常。

## join\_on\_disk\_max\_files\_to\_merge

<SettingsInfoBlock type="UInt64" default_value="64" />

限制 MergeJoin 操作在磁盘上执行时，并行排序可使用的文件数量。

该设置的值越大，占用的 RAM 越多，对磁盘 I/O 的需求越少。

可能的值：

* 任意大于等于 2 的正整数。

## join\_output\_by\_rowlist\_perkey\_rows\_threshold

<SettingsInfoBlock type="UInt64" default_value="5" />

<VersionHistory rows={[{"id": "row-1","items": [{"label": "24.9"},{"label": "5"},{"label": "在 hash join 中，用于判断是否按行列表输出的右表每个键平均行数下限。"}]}]} />

在 hash join 中，用于判断是否按行列表输出的右表每个键平均行数下限。

## join\_overflow\_mode

<SettingsInfoBlock type="OverflowMode" default_value="throw" />

定义了当 join 达到以下任一限制时，ClickHouse 将执行的操作：

* [max\_bytes\_in\_join](/zh/reference/settings/session-settings/max-bytes#max_bytes_in_join)
* [max\_rows\_in\_join](/zh/reference/settings/session-settings/max-rows#max_rows_in_join)

所有基于哈希的 [`join_algorithm`](/zh/reference/settings/session-settings/join#join_algorithm)
取值都会遵循此设置，包括那些会落盘的算法：达到限制时会停止查询，而不是触发落盘。例外是
`legacy_join_size_limits_trigger_spilling`：启用它后，join 中已经在磁盘上运行的部分会继续落盘，而不按此设置执行。
`ie_join` 同样遵循此设置，作用于它从两侧累积的输入。`partial_merge` 则仍然通过切换策略来处理这些限制——请参见
[`join_algorithm`](/zh/reference/settings/session-settings/join#join_algorithm)。

可能值：

* `THROW` — ClickHouse 抛出异常并停止查询。
* `BREAK` — ClickHouse 停止查询，但不抛出异常。

默认值：`THROW`。

**另请参见**

* [JOIN 子句](/zh/reference/statements/select/join)
* [Join 表引擎](/zh/reference/engines/table-engines/special/join)

## join\_use\_nulls

<SettingsInfoBlock type="Bool" default_value="0" />

设置 [JOIN](/zh/reference/statements/select/join) 的行为方式。合并表时，可能会出现空单元。ClickHouse 会根据此设置采用不同方式填充这些空单元。

可能的值：

* 0 — 空单元将使用相应字段类型的默认值填充。
* 1 — `JOIN` 的行为与 standard SQL 相同。相应字段的类型会转换为 [Nullable](/zh/reference/data-types/nullable)，空单元则会填充为 [NULL](/zh/reference/syntax)。
