> ## Documentation Index
> Fetch the complete documentation index at: https://docs.maia.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Table Update

export const ComponentMetadata = ({warehouses, unsupportedWarehouses = [], componentType, connectionInputs, connectionOutputs}) => {
  const allWarehouses = [...warehouses.map(w => ({
    name: w,
    supported: true
  })), ...unsupportedWarehouses.map(w => ({
    name: w,
    supported: false
  }))];
  return <div style={{
    background: 'var(--colors-background-light, #f9fafb)',
    border: '1px solid var(--colors-border-default, #e5e7eb)',
    borderRadius: '12px',
    padding: '20px 28px',
    marginBottom: '28px',
    boxShadow: '0 1px 4px rgba(0,0,0,0.10)'
  }}>
      <table style={{
    width: '100%',
    borderCollapse: 'collapse'
  }}>
        <tbody>
          <tr>
            <td style={{
    fontWeight: '600',
    paddingRight: '32px',
    paddingBottom: '14px',
    whiteSpace: 'nowrap',
    verticalAlign: 'middle',
    width: '180px'
  }}>Project Availability</td>
            <td style={{
    paddingBottom: '14px',
    verticalAlign: 'middle'
  }}>
              <div style={{
    display: 'flex',
    flexWrap: 'wrap',
    gap: '8px'
  }}>
                {allWarehouses.map((w, i) => <span key={i} style={{
    background: w.supported ? '#dcfce7' : '#fee2e2',
    color: w.supported ? '#15803d' : '#b91c1c',
    border: `1px solid ${w.supported ? '#bbf7d0' : '#fca5a5'}`,
    borderRadius: '9999px',
    padding: '3px 12px',
    fontSize: '0.85rem',
    fontWeight: '500',
    whiteSpace: 'nowrap'
  }}>
                    {w.name} {w.supported ? '✅' : '❌'}
                  </span>)}
              </div>
            </td>
          </tr>
          <tr>
            <td style={{
    fontWeight: '600',
    paddingRight: '32px',
    paddingBottom: '14px',
    whiteSpace: 'nowrap',
    verticalAlign: 'middle'
  }}>Component Type</td>
            <td style={{
    paddingBottom: '14px',
    verticalAlign: 'middle'
  }}>{componentType}</td>
          </tr>
          <tr>
            <td style={{
    fontWeight: '600',
    paddingRight: '32px',
    paddingBottom: '14px',
    whiteSpace: 'nowrap',
    verticalAlign: 'middle'
  }}>Connection Inputs</td>
            <td style={{
    paddingBottom: '14px',
    verticalAlign: 'middle'
  }}>{connectionInputs}</td>
          </tr>
          <tr>
            <td style={{
    fontWeight: '600',
    paddingRight: '32px',
    whiteSpace: 'nowrap',
    verticalAlign: 'middle'
  }}>Connection Outputs</td>
            <td style={{
    verticalAlign: 'middle'
  }}>{connectionOutputs}</td>
          </tr>
        </tbody>
      </table>
    </div>;
};

<ComponentMetadata warehouses={["Snowflake", "Databricks", "Amazon Redshift", "Google BigQuery"]} componentType="Transformation" connectionInputs="One" connectionOutputs="None" />

The **Table Update** transformation component lets you update a set of existing records in a target table. The component attempts to match rows in your input dataset with rows in the target table, and updates selected columns in the target row wherever a match is found. The match is based on matching keys in the input dataset and the target tables. It's very important that the keys uniquely identify the rows, and that the keys aren't NULL.

Successful validation of this component ensures the target table exists and the target columns have been found. However, data is only written to the table when the pipeline containing the table update is actually run. Most potential problems are avoided by a successful validation; however, run-time errors can still occur during execution.

This component is equivalent to using the SQL **MERGE INTO** statement. The **Join Expression** property defines the matching condition, **When Matched** defines the action on matched rows, **Update Mapping** maps which columns to overwrite, and **Include Not Matched** optionally inserts unmatched rows.

<Note>
  There are other components that can write data to a table and may better suit your use case:

  * To output data to a new table, use [Rewrite Table](/docs/components/rewrite-table).
  * To output data to an existing table, appending to or replacing the current contents, use [Table Output](/docs/components/table-output).
</Note>

### Use case

Table Update is typically used in pipelines when you're working with slowly changing dimensions, CDC (change data capture), or data harmonization. Some typical examples of use are:

* Overwriting existing attributes in a table row when changes are detected. For example, when a customer updates their email address or phone number, you need to update that single field without overwriting the rest of the data.
* Change data capture (CDC) handling. For example, ingesting change logs from an upstream system and reflecting those changes in your warehouse table.
* Synchronizing reference or lookup tables. For example, updating a product description based on a trusted source feed.

***

## Properties

<Tabs>
  <Tab title="Snowflake">
    <ResponseField name="Name" type="string" required>
      A human-readable name for the component.
    </ResponseField>

    {/* <!-- param-start:[warehouse] | warehouses: [snowflake] --> */}

    <ResponseField name="Warehouse" type="drop-down" required>
      The Snowflake warehouse used to run the queries. The special value `[Environment Default]` uses the warehouse defined in the environment. Read [Overview of Warehouses](https://docs.snowflake.com/en/user-guide/warehouses-overview.html) to learn more.
    </ResponseField>

    {/* <!-- param-start:[database] | warehouses: [snowflake] --> */}

    <ResponseField name="Database" type="drop-down" required>
      The Snowflake database. The special value `[Environment Default]` uses the database defined in the environment. Read [Databases, Tables and Views - Overview](https://docs.snowflake.com/en/guides-overview-db) to learn more.
    </ResponseField>

    {/* <!-- param-start:[schema] | warehouses: [snowflake] --> */}

    <ResponseField name="Schema" type="drop-down" required>
      The Snowflake schema. The special value `[Environment Default]` uses the schema defined in the environment. Read [Database, Schema, and Share DDL](https://docs.snowflake.com/en/sql-reference/ddl-database.html) to learn more.
    </ResponseField>

    {/* <!-- param-start:[targetTable] | warehouses: [snowflake] --> */}

    <ResponseField name="Target Table" type="drop-down" required>
      The table you want to update.
    </ResponseField>

    {/* <!-- param-start:[targetAlias] | warehouses: [snowflake] --> */}

    <ResponseField name="Target Alias" type="string" required>
      The alias for the target table. An alias allows a table to be referred to and is usually a name that is typically shorter and simpler than its actual name.
    </ResponseField>

    {/* <!-- param-start:[sourceAlias] | warehouses: [snowflake] --> */}

    <ResponseField name="Source Alias" type="string" required>
      The alias for the source table. An alias allows a table to be referred to and is usually a name that is typically shorter and simpler than its actual name.
    </ResponseField>

    {/* <!-- param-start:[joinExpression] | warehouses: [snowflake] --> */}

    <ResponseField name="Join Expression" type="column editor" required>
      The condition used to match rows in the source data to rows in the target table, determining which rows to update. This is equivalent to the `ON` clause in a SQL `MERGE INTO` statement. Each expression is evaluated as true or false—rows where the condition is true are considered a match.

      Reference columns using the aliases set in **Source Alias** and **Target Alias**. Keys must uniquely identify rows and must not be NULL, otherwise you'll get unexpected results.

      Examples:

      * Simple key match: `"input"."customer_id" = "target"."customer_id"`
      * Date range match: `"input"."event_date" BETWEEN "target"."start_date" AND "target"."end_date"`
    </ResponseField>

    {/* <!-- param-start:[whenMatched] | warehouses: [snowflake] --> */}

    <ResponseField name="When Matched" type="column editor">
      Select a case as previously defined in the Join Expression property. Add as many rows to the editor as you need, one per case. Choose an operation for when the corresponding case occurs according to the join expression.

      * **Delete:** Completely remove the row from the output table if the case is matched.
      * **Update:** Output the data as expected if a match is found.
    </ResponseField>

    {/* <!-- param-start:[updateMapping] | warehouses: [snowflake] --> */}

    <ResponseField name="Update Mapping" type="column editor" required>
      The input column name from the matched input flow. Add as many rows to the editor as you need, one per input column. Property only available when a matched case results in an update.

      The name of the output column that the corresponding matched input is written to. This can be the same name as the input column if desired.
    </ResponseField>

    {/* <!-- param-start:[includeNotMatched] | warehouses: [snowflake] --> */}

    <ResponseField name="Include Not Matched" type="boolean" required>
      If **Yes**, allow non-matched data to continue through to the output. The columns this data is written to are defined in the **Insert Mapping** property.
    </ResponseField>

    {/* <!-- param-start:[insertMapping] | warehouses: [snowflake] --> */}

    <ResponseField name="Insert Mapping" type="column editor" required>
      The input column name from the unmatched input flow. Add as many rows to the editor as you need, one per input column. Property only available when an unmatched case is included.

      The name of the output column that the corresponding unmatched input is written to. This can be the same name as the input column if desired.
    </ResponseField>
  </Tab>

  <Tab title="Databricks">
    <ResponseField name="Name" type="string" required>
      A human-readable name for the component.
    </ResponseField>

    {/* <!-- param-start:[catalog] | warehouses: [databricks] --> */}

    <ResponseField name="Catalog" type="drop-down" required>
      Select a [Databricks Unity Catalog](https://docs.databricks.com/en/data-governance/unity-catalog/index.html). The special value `[Environment Default]` uses the catalog defined in the environment. Selecting a catalog will determine which databases are available in the next parameter.
    </ResponseField>

    {/* <!-- param-start:[schema] | warehouses: [databricks] --> */}

    <ResponseField name="Schema (Database)" type="drop-down" required>
      The Databricks schema. The special value `[Environment Default]` uses the schema defined in the environment. Read [Create and manage schemas](https://docs.databricks.com/en/data-governance/unity-catalog/create-schemas.html) to learn more.
    </ResponseField>

    {/* <!-- param-start:[targetTable] | warehouses: [databricks] --> */}

    <ResponseField name="Target Table" type="drop-down" required>
      The table you want to update.
    </ResponseField>

    {/* <!-- param-start:[targetAlias] | warehouses: [databricks] --> */}

    <ResponseField name="Target Alias" type="string" required>
      The alias for the target table. An alias allows a table to be referred to and is usually a name that is typically shorter and simpler than its actual name.
    </ResponseField>

    {/* <!-- param-start:[sourceAlias] | warehouses: [databricks] --> */}

    <ResponseField name="Source Alias" type="string" required>
      The alias for the source table. An alias allows a table to be referred to and is usually a name that is typically shorter and simpler than its actual name.
    </ResponseField>

    {/* <!-- param-start:[joinExpression] | warehouses: [databricks] --> */}

    <ResponseField name="Join Expression" type="column editor" required>
      The condition used to match rows in the source data to rows in the target table, determining which rows to update. This is equivalent to the `ON` clause in a SQL `MERGE INTO` statement. Each expression is evaluated as true or false—rows where the condition is true are considered a match.

      Reference columns using the aliases set in **Source Alias** and **Target Alias**. Keys must uniquely identify rows and must not be NULL, otherwise you'll get unexpected results.

      Examples:

      * Simple key match: `"input"."customer_id" = "target"."customer_id"`
      * Date range match: `"input"."event_date" BETWEEN "target"."start_date" AND "target"."end_date"`
    </ResponseField>

    {/* <!-- param-start:[whenMatched] | warehouses: [databricks] --> */}

    <ResponseField name="When Matched" type="column editor">
      Select a case as previously defined in the Join Expression property. Add as many rows to the editor as you need, one per case. Choose an operation for when the corresponding case occurs according to the join expression.

      * **Delete:** Completely remove the row from the output table if the case is matched.
      * **Update:** Output the data as expected if a match is found.
    </ResponseField>

    {/* <!-- param-start:[updateMapping] | warehouses: [databricks] --> */}

    <ResponseField name="Update Mapping" type="column editor" required>
      The input column name from the matched input flow. Add as many rows to the editor as you need, one per input column. Property only available when a matched case results in an update.

      The name of the output column that the corresponding matched input is written to. This can be the same name as the input column if desired.
    </ResponseField>

    {/* <!-- param-start:[includeNotMatched] | warehouses: [databricks] --> */}

    <ResponseField name="Include Not Matched" type="boolean" required>
      If **Yes**, allow non-matched data to continue through to the output. The columns this data is written to are defined in the **Insert Mapping** property.
    </ResponseField>

    {/* <!-- param-start:[insertMapping] | warehouses: [databricks] --> */}

    <ResponseField name="Insert Mapping" type="column editor" required>
      The input column name from the unmatched input flow. Add as many rows to the editor as you need, one per input column. Property only available when an unmatched case is included.

      The name of the output column that the corresponding unmatched input is written to. This can be the same name as the input column if desired.
    </ResponseField>
  </Tab>

  <Tab title="Amazon Redshift">
    <ResponseField name="Name" type="string" required>
      A human-readable name for the component.
    </ResponseField>

    {/* <!-- param-start:[schema] | warehouses: [redshift] --> */}

    <ResponseField name="Schema" type="drop-down" required>
      Select the table schema. The special value `[Environment Default]` uses the schema defined in the environment. For more information on using multiple schemas, read [Schemas](https://docs.aws.amazon.com/redshift/latest/dg/r_Schemas_and_tables.html).
    </ResponseField>

    {/* <!-- param-start:[targetTable] | warehouses: [redshift] --> */}

    <ResponseField name="Target Table" type="drop-down" required>
      The table you want to update.
    </ResponseField>

    {/* <!-- param-start:[updateMapping] | warehouses: [redshift] --> */}

    <ResponseField name="Update Mapping" type="column editor" required>
      * **Source Column:** The input column name from the matched input flow. Add as many rows to the editor as you need, one per input column. Property only available when a matched case results in an update.
      * **Target Column:** The name of the output column that the corresponding matched input is written to. This can be the same name as the input column if desired.
    </ResponseField>

    {/* <!-- param-start:[updateMode] | warehouses: [redshift] --> */}

    <ResponseField name="Update mode" type="drop-down" required>
      Select how the table will update. Choose from:

      * **Update/Insert:** A traditional update statement, and an insert of incoming rows that don't match the target table (matching on Unique Keys). This is sometimes referred to as an upsert. The update counts reported will differ between the two strategies, even for the same datasets. This is because the database reports the number of affected rows. The first strategy will count all the deletes, plus all the inserts, which may overlap. The second strategy will count the number of updates, plus the number of rows added that weren't already updated.
      * **Delete/Insert:** Removes overlapping rows (matching on Unique Keys) and then inserts all incoming rows. This is effectively an update, and is very fast. However, if you don't have incoming values for all target columns, replaced rows will be NULL for those missing columns. Deleting rows in this way ideally requires a vacuum afterwards to recover space. This component does not arrange that vacuum for you, but there is a vacuum tables component available in an orchestration pipeline. For more information on vacuuming tables, read [Vacuuming tables](https://docs.aws.amazon.com/redshift/latest/dg/t_Reclaiming_storage_space202.html).
    </ResponseField>

    {/* <!-- param-start:[uniqueKeys] | warehouses: [redshift] --> */}

    <ResponseField name="Unique Keys" type="drop-down" required>
      Selected column(s) from the input table used as unique keys.
    </ResponseField>

    {/* <!-- param-start:[fixDataTypeMismatches] | warehouses: [redshift] --> */}

    <ResponseField name="Fix Data Type Mismatches" type="drop-down" required>
      * **Yes:** If the source column type does not match the target table type, attempt to CAST the value to the required target type.
      * **No:** Do not cast types. Amazon Redshift may still attempt to coerce the types in this case.
    </ResponseField>
  </Tab>

  <Tab title="Google BigQuery">
    <ResponseField name="Name" type="string" required>
      A human-readable name for the component.
    </ResponseField>

    {/* <!-- param-start:[gcpProjectId] | warehouses: [bigquery] --> */}

    <ResponseField name="GCP Project ID" type="drop-down" required>
      The Google Cloud project that owns the BigQuery dataset. The special value `[Environment Default]` uses the Google Cloud project defined in the environment. For more information, read [Creating projects](https://docs.cloud.google.com/resource-manager/docs/creating-managing-projects).
    </ResponseField>

    {/* <!-- param-start:[dataset] | warehouses: [bigquery] --> */}

    <ResponseField name="Dataset" type="drop-down" required>
      The Google BigQuery dataset where the table will be created or updated. The special value `[Environment Default]` uses the dataset defined in the environment.
    </ResponseField>

    {/* <!-- param-start:[targetTable] | warehouses: [bigquery] --> */}

    <ResponseField name="Target Table" type="drop-down" required>
      The table you want to update.
    </ResponseField>

    {/* <!-- param-start:[updateMapping] | warehouses: [bigquery] --> */}

    <ResponseField name="Update Mapping" type="column editor" required>
      * **Source Column:** The input column name from the matched input flow. Add as many rows to the editor as you need, one per input column. Property only available when a matched case results in an update.
      * **Target Column:** The name of the output column that the corresponding matched input is written to. This can be the same name as the input column if desired.
    </ResponseField>

    {/* <!-- param-start:[updateStrategy] | warehouses: [bigquery] --> */}

    <ResponseField name="Update strategy" type="drop-down" required>
      Select how the table will update. Choose from:

      * **Merge:** Combines insert and update operations into a single statement, and performs the operations in isolation (performs the operations separately from other operations that may be going on).
      * **Delete/Insert:** Removes overlapping rows (matching on Unique Keys) and then inserts all incoming rows. This is effectively an update, and is very fast. However, if you don't have incoming values for all target columns, replaced rows will be NULL for those missing columns.
      * **Update/Insert:** A traditional update statement, and an insert of incoming rows that don't match the target table (matching on **Unique Keys**). This is sometimes referred to as an upsert.
      * **Rewrite and Append:** Rewrites all rows not requiring updates, and then appends all rows that are updated. For large tables or small relative numbers of rows to be changed, this may be resource heavy.

      The update counts reported will differ between the **Delete/Insert** and **Update/Insert** strategies, even for the same datasets. This is because the database reports the number of affected rows. The first strategy will count all the deletes, plus all the inserts, which may overlap. The second strategy will count the number of updates, plus the number of rows added that weren't already updated.

      <Warning>
        The number of requests allowed by Google BigQuery is severely limited. Exceeding these limits will cause jobs to fail. For more information, read the BigQuery [Quotas and limits](https://docs.cloud.google.com/bigquery/quotas#data_manipulation_language_statements) documentation.
      </Warning>
    </ResponseField>

    {/* <!-- param-start:[uniqueKeys] | warehouses: [bigquery] --> */}

    <ResponseField name="Unique Keys" type="dual listbox" required>
      Select the columns from the input table used as unique keys. The associated target columns are found using the **Update Mapping**.
    </ResponseField>

    {/* <!-- param-start:[fixDataTypeMismatches] | warehouses: [bigquery] --> */}

    <ResponseField name="Fix data type mismatches" type="drop-down" required>
      * **Yes:** If the source column type does not match the target table type, attempt to CAST the value to the required target type.
      * **No:** Do not cast types. Google BigQuery may still attempt to coerce the types in this case.
    </ResponseField>
  </Tab>
</Tabs>
