

 Amazon Redshift 將不再支援從修補程式 198 開始建立新的 Python UDFs。現有 Python UDF 將繼續正常運作至 2026 年 6 月 30 日。如需詳細資訊，請參閱[部落格文章](https://aws.amazon.com/blogs/big-data/amazon-redshift-python-user-defined-functions-will-reach-end-of-support-after-june-30-2026/)。

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 更新和插入新資料
<a name="t_updating-inserting-using-staging-tables-"></a>

您可以透過使用 MERGE 命令有效地將新資料增加到現有的資料表中。建立臨時資料表，然後使用本節所述的其中一個方法從臨時資料表中更新目標資料表，即可執行合併操作。如需 MERGE 命令的相關資訊，請參閱 [MERGE](r_MERGE.md)。

[合併範例](merge-examples.md) 會使用名為 TICKIT 資料集的 Amazon Redshift 範例資料集。作為先決條件，您可以依照[開始使用一般資料庫任務](https://docs.aws.amazon.com/redshift/latest/gsg/database-tasks.html)中的指示，設定 TICKIT 資料表和資料。有關範例資料集的更多詳細資訊，請參閱[範例資料庫](https://docs.aws.amazon.com/redshift/latest/dg/c_sampledb.html)。

## 合併方法 1：取代現有資料列
<a name="merge-method-replace-existing-rows"></a>

如果您要覆寫目標資料表中的所有資料欄，執行合併最快的方式是取代現有的資料列。這只會掃描目標資料表一次，透過使用內部聯結來刪除將要更新的資料列。刪除資料列之後，這些資料列會透過臨時資料表中的單一插入操作被新資料列取代。

如果下列各項皆成立，請使用此方法：
+ 您的目標資料表和您的臨時資料表包含相同的資料欄。
+ 您想要將目標資料表資料欄中的所有資料取代為所有臨時資料表資料欄。
+ 您將在合併中使用臨時資料表的所有資料列。

如果任一條件不適用，請使用「合併方法 2：指定資料欄清單」，而不是使用 MERGE，如下一節所示。

如果您將不會使用臨時資料表中的所有資料列，請使用 WHERE 子句來篩選 DELETE 和 INSERT 陳述式，將未變更的資料列省略。不過，如果臨時資料表中的多數資料列將不會參與合併，我們建議在個別步驟中執行 UPDATE 和 INSERT，如本節稍後所述。

## 合併方法 2：指定欄位清單而不使用 MERGE
<a name="merge-method-specify-column-list"></a>

使用此方法來更新目標資料表中的特定資料欄，而不覆寫整個資料列。此方法需要的時間較先前的方法更久，因為它需要額外的更新步驟，並且不會使用 MERGE 命令。如果下列任一項成立，請使用此方法：
+ 並非目標資料表中的所有資料欄都是要更新的。
+ 臨時資料表中的多數資料列將不會用於更新。

**Topics**
+ [合併方法 1：取代現有資料列](#merge-method-replace-existing-rows)
+ [合併方法 2：指定欄位清單而不使用 MERGE](#merge-method-specify-column-list)
+ [建立暫時的預備資料表](merge-create-staging-table.md)
+ [取代現有資料列來執行合併操作](merge-replacing-existing-rows.md)
+ [指定欄位清單但不使用 MERGE 命令，以執行合併操作](merge-specify-a-column-list.md)
+ [合併範例](merge-examples.md)

# 建立暫時的預備資料表
<a name="merge-create-staging-table"></a>

*臨時資料表*為暫時的資料表，其中保存了將用來對*目標資料表*進行變更的所有資料，包括更新和插入。

合併操作需要臨時資料表和目標資料表之間的聯結。若要共置聯結資料列，請將臨時資料表的分佈索引鍵設定為與目標資料表的分佈索引鍵相同的資料欄。例如，如果目標資料表使用外部索引鍵資料欄做為其分佈索引鍵，請對臨時資料表的分佈索引鍵使用相同的資料欄。如果您使用 [CREATE TABLE LIKE](r_CREATE_TABLE_NEW.md#create-table-like) 陳述式建立臨時資料表，臨時資料表將從父資料表繼承分佈索引鍵。如果您使用 CREATE TABLE AS 陳述式，新資料表將不會繼承分佈索引鍵。如需詳細資訊，請參閱[分配資料以實現查詢最佳化](t_Distributing_data.md)

如果分佈索引鍵與主索引鍵不同，並且分佈索引鍵不會隨著合併操作更新，請在分佈索引鍵資料欄上新增備援聯結述詞以啟用共置聯結。例如：

```
where target.primarykey = stage.primarykey 
and target.distkey = stage.distkey
```

若要驗證查詢將使用共置聯結，請使用 [EXPLAIN](r_EXPLAIN.md) 執行查詢，並檢查所有聯結上的 DS\$1DIST\$1NONE。如需詳細資訊，請參閱[評估查詢計畫](c_data_redistribution.md)

# 取代現有資料列來執行合併操作
<a name="merge-replacing-existing-rows"></a>

當您執行程序中詳述的合併操作時，請將建立和卸除臨時資料表以外的所有步驟置於單一交易中。如果有任何步驟失敗，您可以復原交易。使用單一交易也會減少認可的數目，如此可節省時間和資源。

**取代現有資料列來執行合併操作**

1. 建立臨時資料表，然後將它填入要合併的資料，如下列虛擬程式碼所示。

   ```
   CREATE temp table stage (like target); 
   
   INSERT INTO stage 
   SELECT * FROM source 
   WHERE source.filter = 'filter_expression';
   ```

1.  您可以使用 MERGE 與臨時資料表執行內部聯結，以更新目標資料表中符合臨時資料表的資料列，然後將所有剩餘的資料列插入與臨時資料表不符的目標資料表中。

    我們建議您在單一 MERGE 命令中執行更新和插入操作。

   ```
   MERGE INTO target 
   USING stage [optional alias] on (target.primary_key = stage.primary_key)
   WHEN MATCHED THEN 
   UPDATE SET col_name1 = stage.col_name1 , col_name2= stage.col_name2, col_name3 = {expr}
   WHEN NOT MATCHED THEN
   INSERT (col_name1 , col_name2, col_name3) VALUES (stage.col_name1, stage.col_name2, {expr});
   ```

1. 捨棄臨時資料表。

   ```
   DROP TABLE stage;
   ```

# 指定欄位清單但不使用 MERGE 命令，以執行合併操作
<a name="merge-specify-a-column-list"></a>

當您執行程序中詳述的合併操作時，請將所有步驟置於單一交易中。如果有任何步驟失敗，您可以復原交易。使用單一交易也會減少認可的數目，如此可節省時間和資源。

**指定資料欄清單來執行合併操作**

1. 將整個操作放在單一交易區塊中。

   ```
   BEGIN transaction;
   … 
   END transaction;
   ```

1. 建立臨時資料表，然後將它填入要合併的資料，如下列虛擬程式碼所示。

   ```
   create temp table stage (like target); 
   insert into stage 
   select * from source 
   where source.filter = 'filter_expression';
   ```

1. 使用內部聯結搭配臨時資料表來更新目標資料表。
   + 在 UPDATE 子句中，明確列出要更新的資料欄。
   + 使用臨時資料表來執行內部聯結。
   + 如果分佈索引鍵與主索引鍵不同，並且分佈索引鍵將不會更新，請在分佈索引鍵上新增備援聯結。若要驗證查詢將使用共置聯結，請使用 [EXPLAIN](r_EXPLAIN.md) 執行查詢，並檢查所有聯結上的 DS\$1DIST\$1NONE。如需詳細資訊，請參閱[評估查詢計畫](c_data_redistribution.md)
   + 如果您的目標資料表是依時間戳記排序，請在目標資料表上新增述詞來利用受範圍限制的掃描。如需詳細資訊，請參閱[設計查詢的 Amazon Redshift 最佳實務](c_designing-queries-best-practices.md)。
   + 如果您將不會在合併中使用所有資料列，請新增子句來篩選要變更的資料列。例如，在一或多個資料欄上新增不相等篩選條件，以排除未變更的資料列。
   + 將更新、刪除和插入操作放置在單一交易區塊，如此一來若發生問題，各個項目都將能復原。

    例如：

   ```
   begin transaction;
   
   update target 
   set col1 = stage.col1, 
   col2 = stage.col2, 
   col3 = 'expression' 
   from stage 
   where target.primarykey = stage.primarykey 
   and target.distkey = stage.distkey 
   and target.col3 > 'last_update_time' 
   and (target.col1 != stage.col1 
   or target.col2 != stage.col2 
   or target.col3 = 'filter_expression');
   ```

1. 使用內部聯結搭配目標資料表從臨時資料表刪除不需要的資料列。目標資料表中的部分資料列已符合臨時資料表中的對應資料列，而其他資料列在先前的步驟中已更新。在任一情況下，插入不需要這些資料。

   ```
   delete from stage 
   using target 
   where stage.primarykey = target.primarykey;
   ```

1. 從臨時資料表插入其餘的資料列。在您於步驟二用於 UPDATE 陳述式的 VALUES 子句中使用相同的資料欄清單。

   ```
   insert into target
   (select col1, col2, 'expression'
   from stage);
   
   end transaction;
   ```

1. 捨棄臨時資料表。

   ```
   drop table stage;
   ```

# 合併範例
<a name="merge-examples"></a>

下列範例會執行合併以更新 SALES 資料表。第一個範例使用較簡易的方法，就是從目標資料表刪除，然後從臨時資料表插入所有資料列。第二個範例需要更新目標資料表中的特定資料欄，因此包括額外的更新步驟。

[合併範例](#merge-examples) 會使用名為 TICKIT 資料集的 Amazon Redshift 範例資料集。作為先決條件，您可以依照[開始使用一般資料庫任務](https://docs.aws.amazon.com/redshift/latest/gsg/database-tasks.html)指南中的指示，設定 TICKIT 資料表和資料。有關範例資料集的更多詳細資訊，請參閱[範例資料庫](https://docs.aws.amazon.com/redshift/latest/dg/c_sampledb.html)。

**範例合併資料來源**

本節中的範例需要包括更新和插入的樣本資料來源。針對範例，我們將建立名為 SALES\$1UPDATE 的樣本資料表，其使用來自 SALES 資料表的資料。我們將以代表 12 月新銷售活動的隨機資料填入新的資料表。我們將使用 SALES\$1UPDATE 樣本資料表在以下的範例中建立臨時資料表。

```
-- Create a sample table as a copy of the SALES table.

create table tickit.sales_update as
select * from tickit.sales;

-- Change every fifth row to have updates.

update tickit.sales_update
set qtysold = qtysold*2,
pricepaid = pricepaid*0.8,
commission = commission*1.1
where saletime > '2008-11-30'
and mod(sellerid, 5) = 0;

-- Add some new rows to have inserts.
-- This example creates a duplicate of every fourth row.

insert into tickit.sales_update
select (salesid + 172456) as salesid, listid, sellerid, buyerid, eventid, dateid, qtysold, pricepaid, commission, getdate() as saletime
from tickit.sales_update
where saletime > '2008-11-30'
and mod(sellerid, 4) = 0;
```

**根據相符索引鍵取代現有資料列的合併範例**

下列指令碼使用 SALES\$1UPDATE 資料表在 SALES 資料表上執行與 12 月銷售活動新資料的合併操作。此範例會在有更新的 SALES 資料表中取代資料列。針對此範例，我們將會更新 qtysold 和 pricepaid 資料欄，但將 commission 和 saletime 維持不變。

```
MERGE into tickit.sales 
USING tickit.sales_update sales_update  
on ( sales.salesid = sales_update.salesid
and sales.listid = sales_update.listid
and sales_update.saletime > '2008-11-30'
and (sales.qtysold != sales_update.qtysold 
or sales.pricepaid != sales_update.pricepaid))
WHEN MATCHED THEN
update SET qtysold = sales_update.qtysold,
pricepaid = sales_update.pricepaid
WHEN NOT MATCHED THEN 
INSERT (salesid, listid, sellerid, buyerid, eventid, dateid, qtysold , pricepaid, commission, saletime)
values (sales_update.salesid, sales_update.listid, sales_update.sellerid, sales_update.buyerid, sales_update.eventid, 
sales_update.dateid, sales_update.qtysold , sales_update.pricepaid, sales_update.commission, sales_update.saletime);

-- Drop the staging table.
drop table tickit.sales_update;

-- Test to see that commission and salestime were not impacted.
SELECT sales.salesid, sales.commission, sales.salestime, sales_update.commission, sales_update.salestime 
FROM tickit.sales 
INNER JOIN tickit.sales_update sales_update  
ON 
sales.salesid = sales_update.salesid
AND sales.listid = sales_update.listid
AND sales_update.saletime > '2008-11-30'
AND (sales.commission != sales_update.commission 
OR sales.salestime != sales_update.salestime);
```

**指定資料欄清單而不使用 MERGE 的合併範例**

下列範例會執行合併操作，以 12 月銷售活動的新資料更新 SALES。我們需要包括更新和插入的樣本資料，以及沒有變更的資料列。針對此範例，我們想要更新 QTYSOLD 和 PRICEPAID 資料欄，例將 COMMISSION 和 SALETIME 維持不變。下列指令碼使用 SALES\$1UPDATE 資料表在 SALES 資料表上執行合併操作。

```
-- Create a staging table and populate it with rows from SALES_UPDATE for Dec
create temp table stagesales as select * from sales_update
where saletime > '2008-11-30';

-- Start a new transaction
begin transaction;

-- Update the target table using an inner join with the staging table
-- The join includes a redundant predicate to collocate on the distribution key –- A filter on saletime enables a range-restricted scan on SALES

update sales
set qtysold = stagesales.qtysold,
pricepaid = stagesales.pricepaid
from stagesales
where sales.salesid = stagesales.salesid
and sales.listid = stagesales.listid
and stagesales.saletime > '2008-11-30'
and (sales.qtysold != stagesales.qtysold 
or sales.pricepaid != stagesales.pricepaid);
 
-- Delete matching rows from the staging table 
-- using an inner join with the target table

delete from stagesales
using sales
where sales.salesid = stagesales.salesid
and sales.listid = stagesales.listid;

-- Insert the remaining rows from the staging table into the target table
insert into sales
select * from stagesales;

-- End transaction and commit
end transaction;

-- Drop the staging table
drop table stagesales;
```