This is an automated email from the ASF dual-hosted git repository.
szehon-ho pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/spark.git
The following commit(s) were added to refs/heads/master by this push:
new 8e22e990994a [SPARK-57359][DOC] Document the MERGE INTO statement in
the SQL reference
8e22e990994a is described below
commit 8e22e990994a3e4bf35ddaebe0e06fa5b36e284f
Author: Szehon Ho <[email protected]>
AuthorDate: Wed Jun 10 17:19:47 2026 -0700
[SPARK-57359][DOC] Document the MERGE INTO statement in the SQL reference
### What changes were proposed in this pull request?
The SQL reference does not have a page for the `MERGE INTO` statement. This
PR adds one at `docs/sql-ref-syntax-dml-merge-into.md` and links it from the
DML statements list in `docs/sql-ref-syntax.md`.
The new page covers:
- Description, including that `MERGE INTO` requires a Data Source V2
connector that supports row-level operations (linked to the DSV2 reference).
- Syntax for the `ON` condition and the `WHEN MATCHED`, `WHEN NOT MATCHED [
BY TARGET ]`, and `WHEN NOT MATCHED BY SOURCE` clauses, with their allowed
actions (`DELETE`, `UPDATE SET *`, `UPDATE SET col = val`, `INSERT *`, `INSERT
(cols) VALUES (...)`).
- Parameters describing each clause and action.
- Notes on multiple clauses being evaluated in order, the requirement of at
least one `WHEN` clause, and the `MERGE_CARDINALITY_VIOLATION` behavior
(including when the cardinality check is skipped).
- Examples for update/insert, conditional delete/update/insert,
delete-not-in-source, and update-not-in-source.
The `WITH SCHEMA EVOLUTION` clause is intentionally left for a follow-up PR.
### Why are the changes needed?
`MERGE INTO` is a supported SQL statement but has no entry in the SQL
reference, so users have no documentation for its syntax and semantics.
### Does this PR introduce _any_ user-facing change?
No. Documentation-only update.
### How was this patch tested?
Docs-only change. Reviewed the rendered Markdown for correctness.
### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Cursor (Claude Opus 4.8)
Closes #56420 from szehon-ho/merge-schema-evolution-docs.
Authored-by: Szehon Ho <[email protected]>
Signed-off-by: Szehon Ho <[email protected]>
---
docs/sql-ref-syntax-dml-merge-into.md | 223 ++++++++++++++++++++++++++++++++++
docs/sql-ref-syntax.md | 1 +
2 files changed, 224 insertions(+)
diff --git a/docs/sql-ref-syntax-dml-merge-into.md
b/docs/sql-ref-syntax-dml-merge-into.md
new file mode 100644
index 000000000000..90731105d491
--- /dev/null
+++ b/docs/sql-ref-syntax-dml-merge-into.md
@@ -0,0 +1,223 @@
+---
+layout: global
+title: MERGE INTO
+displayTitle: MERGE INTO
+license: |
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+---
+
+### Description
+
+The `MERGE INTO` statement merges a source table or query into a target table.
Rows of the
+target table are matched against the source using a merge condition. Target
rows are then
+updated or deleted, and new rows are inserted from the source, according to
the clauses that
+apply. All of these row-level changes are performed as a single atomic
operation.
+
+`MERGE INTO` is supported on tables backed by
+[Data Source V2](sql-data-sources-v2.html#row-level-dml) connectors that
support row-level operations.
+
+### Syntax
+
+```sql
+MERGE INTO target_table [ [ AS ] target_alias ]
+ USING { source_table | ( source_query ) } [ [ AS ] source_alias ]
+ ON merge_condition
+ [ WHEN MATCHED [ AND matched_condition ] THEN matched_action ] [ ... ]
+ [ WHEN NOT MATCHED [ BY TARGET ] [ AND not_matched_condition ] THEN
not_matched_action ] [ ... ]
+ [ WHEN NOT MATCHED BY SOURCE [ AND not_matched_by_source_condition ] THEN
not_matched_by_source_action ] [ ... ]
+
+matched_action
+ { DELETE | UPDATE SET * | UPDATE SET { column = value } [ , ... ] }
+
+not_matched_action
+ { INSERT * | INSERT ( column [ , ... ] ) VALUES ( value [ , ... ] ) }
+
+not_matched_by_source_action
+ { DELETE | UPDATE SET { column = value } [ , ... ] }
+```
+
+### Parameters
+
+* **target_table**
+
+ Specifies the table to be merged into, which may be optionally qualified
with a database name.
+ An optional alias may be provided with or without the `AS` keyword.
+
+ **Syntax:** `[ database_name. ] table_name [ [ AS ] alias ]`
+
+* **source_table** / **source_query**
+
+ The source of the merge, specified either as a table or as a parenthesized
query. An optional
+ alias may be provided with or without the `AS` keyword.
+
+* **merge_condition**
+
+ A boolean expression, introduced by the `ON` keyword, that determines how
rows from the source
+ match rows in the target. It controls which `WHEN` clauses apply to each
row.
+
+* **WHEN MATCHED [ AND matched_condition ] THEN matched_action**
+
+ Applies to rows that exist in both the source and the target (according to
`merge_condition`).
+ The optional `matched_condition` further restricts which matched rows the
clause applies to.
+ The `matched_condition` and the `matched_action` values can reference
columns of both the
+ target and the source tables.
+ The `matched_action` can be one of:
+ - `DELETE`: deletes the matched target row.
+ - `UPDATE SET *`: updates the matched target row with all source columns,
matched by name.
+ - `UPDATE SET column = value [ , ... ]`: updates the listed target columns
with the given values.
+
+* **WHEN NOT MATCHED [ BY TARGET ] [ AND not_matched_condition ] THEN
not_matched_action**
+
+ Applies to source rows that have no matching row in the target. `BY
TARGET` is optional and is
+ the default interpretation of `WHEN NOT MATCHED`. The optional
`not_matched_condition` further
+ restricts which unmatched source rows the clause applies to. The
`not_matched_condition` and the
+ `not_matched_action` values can reference columns of the source table only.
+ The `not_matched_action` can be one of:
+ - `INSERT *`: inserts a new target row using all source columns, matched
by name.
+ - `INSERT ( column [ , ... ] ) VALUES ( value [ , ... ] )`: inserts a new
target row, assigning
+ the given values to the listed columns.
+
+* **WHEN NOT MATCHED BY SOURCE [ AND not_matched_by_source_condition ] THEN
not_matched_by_source_action**
+
+ Applies to target rows that have no matching row in the source. The
optional
+ `not_matched_by_source_condition` further restricts which such target rows
the clause applies to.
+ The `not_matched_by_source_condition` and the
`not_matched_by_source_action` values can reference
+ columns of the target table only.
+ The `not_matched_by_source_action` can be one of:
+ - `DELETE`: deletes the unmatched target row.
+ - `UPDATE SET column = value [ , ... ]`: updates the listed target columns
with the given values.
+
+**Note:**
+- A `MERGE INTO` statement must contain at least one `WHEN` clause.
+- Multiple clauses of the same type may be specified. For a given row, the
clauses of the relevant
+ type are evaluated in the order they are written, and the first clause whose
condition is satisfied
+ is applied. When multiple clauses of the same type are specified, only the
last one may omit its
+ `AND` condition.
+- If a single target row matches more than one source row, the statement fails
with a
+ `MERGE_CARDINALITY_VIOLATION` error, since the row would otherwise be
updated or deleted more than
+ once. This check is skipped when there are no `WHEN MATCHED` clauses, or
when the only `WHEN MATCHED`
+ action is an unconditional `DELETE`, because in those cases the outcome does
not depend on the number
+ of matching source rows.
+
+### Examples
+
+The following examples assume that the `target` and `source` tables have
already been created and
+populated as shown below. Each example starts from this initial state.
+
+```sql
+SELECT * FROM target;
++--+------+-------+
+|pk|salary| dep|
++--+------+-------+
+| 1| 300| hr|
+| 2| 160| eng|
+| 5| 100|finance|
++--+------+-------+
+
+SELECT * FROM source;
++--+------+-----+
+|pk|salary| dep|
++--+------+-----+
+| 1| 350| hr|
+| 2| 200| eng|
+| 3| 120|sales|
++--+------+-----+
+```
+
+#### Update Matched Rows and Insert New Rows
+
+```sql
+MERGE INTO target t
+ USING source s
+ ON t.pk = s.pk
+ WHEN MATCHED THEN UPDATE SET *
+ WHEN NOT MATCHED THEN INSERT *;
+
+SELECT * FROM target;
++--+------+-------+
+|pk|salary| dep|
++--+------+-------+
+| 1| 350| hr|
+| 2| 200| eng|
+| 3| 120| sales|
+| 5| 100|finance|
++--+------+-------+
+```
+
+#### Conditional Delete, Update, and Insert
+
+```sql
+-- Delete matched rows whose source salary exceeds 300, update the remaining
matched rows,
+-- and insert source rows that do not match any target row.
+MERGE INTO target t
+ USING source s
+ ON t.pk = s.pk
+ WHEN MATCHED AND s.salary > 300 THEN DELETE
+ WHEN MATCHED THEN UPDATE SET salary = s.salary
+ WHEN NOT MATCHED THEN INSERT (pk, salary, dep) VALUES (s.pk, s.salary,
s.dep);
+
+SELECT * FROM target;
++--+------+-------+
+|pk|salary| dep|
++--+------+-------+
+| 2| 200| eng|
+| 3| 120| sales|
+| 5| 100|finance|
++--+------+-------+
+```
+
+#### Delete Target Rows Not Present in the Source
+
+```sql
+MERGE INTO target t
+ USING source s
+ ON t.pk = s.pk
+ WHEN MATCHED THEN UPDATE SET *
+ WHEN NOT MATCHED THEN INSERT *
+ WHEN NOT MATCHED BY SOURCE THEN DELETE;
+
+SELECT * FROM target;
++--+------+-----+
+|pk|salary| dep|
++--+------+-----+
+| 1| 350| hr|
+| 2| 200| eng|
+| 3| 120|sales|
++--+------+-----+
+```
+
+#### Update Target Rows Not Present in the Source
+
+```sql
+MERGE INTO target t
+ USING source s
+ ON t.pk = s.pk
+ WHEN NOT MATCHED BY SOURCE THEN UPDATE SET dep = 'unassigned';
+
+SELECT * FROM target;
++--+------+----------+
+|pk|salary| dep|
++--+------+----------+
+| 1| 300| hr|
+| 2| 160| eng|
+| 5| 100|unassigned|
++--+------+----------+
+```
+
+### Related Statements
+
+* [INSERT TABLE statement](sql-ref-syntax-dml-insert-table.html)
+* [SELECT statement](sql-ref-syntax-qry-select.html)
diff --git a/docs/sql-ref-syntax.md b/docs/sql-ref-syntax.md
index 1e0ea4a2b8d6..222c507423f8 100644
--- a/docs/sql-ref-syntax.md
+++ b/docs/sql-ref-syntax.md
@@ -49,6 +49,7 @@ Data Manipulation Statements are used to add, change, or
delete data. Spark SQL
* [INSERT TABLE](sql-ref-syntax-dml-insert-table.html)
* [INSERT OVERWRITE
DIRECTORY](sql-ref-syntax-dml-insert-overwrite-directory.html)
+ * [MERGE INTO](sql-ref-syntax-dml-merge-into.html)
* [LOAD](sql-ref-syntax-dml-load.html)
### Data Retrieval Statements
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]