支援的版本:最新 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10 / 9.6 / 9.5 / 9.4 / 9.3 / 9.2 / 9.1 / 9.0 / 8.4 / 8.3 / 8.2 / 8.1 / 8.0 / 7.4 / 7.3 / 7.2 / 7.1

UPDATE

UPDATE — 更新資料表的列

概要

[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table_name [ * ] [ [ AS ] alias ]
    SET { column_name = { expression | DEFAULT } |
          ( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
          ( column_name [, ...] ) = ( sub-SELECT )
        } [, ...]
    [ FROM from_item [, ...] ]
    [ WHERE condition | WHERE CURRENT OF cursor_name ]
    [ RETURNING { * | output_expression [ [ AS ] output_name ] } [, ...] ]

描述

UPDATE 變更符合條件的所有列中指定欄位的值。 只需要在 SET 子句中提及要修改的欄位; 未明確修改的欄位將保留其先前的值。

有兩種方法可以使用資料庫中其他資料表包含的資訊來修改資料表: 使用子查詢,或在 FROM 子句中指定其他資料表。 哪種技術更合適取決於具體情況。

可選的 RETURNING 子句會使 UPDATE 計算並傳回基於每個實際更新的列的值。 可以計算任何使用資料表欄位和/或 FROM 中提及的其他資料表欄位的表達式。 使用資料表欄位的新(更新後)值。 RETURNING 清單的語法與 SELECT 的輸出清單的語法相同。

您必須具有資料表的 UPDATE 權限,或至少具有要更新的欄位的權限。 您還必須具有在 expressionscondition 中讀取其值的任何欄位的 SELECT 權限。

參數

with_query

WITH 子句允許您指定一個或多個子查詢,這些子查詢可以在 UPDATE 查詢中按名稱引用。 有關詳細信息,請參閱第 7.8 節SELECT

table_name

要更新的資料表的名稱(可選擇使用結構描述限定)。 如果在資料表名稱之前指定 ONLY,則僅在指定的資料表中更新符合條件的列。 如果未指定 ONLY,則也會在從指定的資料表繼承的任何資料表中更新符合條件的列。 (可選)可以在資料表名稱後指定 * 以明確表示包含子代資料表。

alias

目標資料表的替代名稱。 提供別名時,它會完全隱藏資料表的實際名稱。 例如,給定 UPDATE foo AS fUPDATE 語句的其餘部分必須將此資料表稱為 f,而不是 foo

column_name

table_name 命名的資料表中的欄位名稱。 如果需要,欄位名稱可以用子欄位名稱或陣列下標限定。 請勿在目標欄位的規格中包含資料表的名稱 — 例如,UPDATE table_name SET table_name.col = 1 無效。

expression

要賦予欄位的表達式。 該表達式可以使用資料表中此欄位和其他欄位的舊值。

DEFAULT

將欄位設定為其預設值(如果未為其分配任何特定的預設表達式,則為 NULL)。 識別欄位將被設定為由關聯的序列產生的新值。 對於產生的欄位,允許指定此項,但僅指定從其產生表達式計算欄位的正常行為。

sub-SELECT

SELECT 子查詢,它產生的輸出欄位數與其前面帶括號的欄位清單中列出的欄位數一樣多。 執行時,子查詢不得產生超過一列。 如果它產生一列,則其欄位值將分配給目標欄位; 如果它不產生任何列,則 NULL 值將分配給目標欄位。 子查詢可以引用正在更新的資料表當前列的舊值。

from_item

一個資料表表達式,允許其他資料表的欄位出現在 WHERE 條件和更新表達式中。 這使用與 FROM 語句的 SELECT 子句相同的語法; 例如,可以指定資料表名稱的別名。 除非您打算進行自連接(在這種情況下,它必須以別名出現在 from_item 中),否則不要重複將目標資料表作為 from_item

condition

一個傳回 boolean 類型值的表達式。 只有此表達式傳回 true 的列才會被更新。

cursor_name

要在 WHERE CURRENT OF 條件中使用的游標名稱。要更新的列是最近從此游標提取的列。游標必須是對 UPDATE 的目標表格進行的非分組查詢。請注意,WHERE CURRENT OF 不能與布林條件一起指定。有關使用游標與 WHERE CURRENT OF 的更多資訊,請參閱 DECLARE

output_expression

一個表達式,將在每次更新列後由 UPDATE 命令計算並返回。該表達式可以使用由 table_name 命名的表格或在 FROM 中列出的表格的任何欄位名稱。寫入 * 以返回所有欄位。

output_name

用於返回欄位的名稱。

輸出

成功完成時,UPDATE 命令會返回一個如下格式的命令標籤

UPDATE count

count 是更新的列數,包括已匹配但值未更改的列。請注意,當更新被 BEFORE UPDATE 觸發器抑制時,此數字可能小於符合 condition 的列數。如果 count 為 0,則表示查詢未更新任何列(這不被視為錯誤)。

如果 UPDATE 命令包含 RETURNING 子句,則結果將類似於包含 RETURNING 列表中定義的欄位和值的 SELECT 語句,這些欄位和值是根據命令更新的列計算得出的。

注意事項

當存在 FROM 子句時,本質上發生的情況是目標表格與 from_item 列表中提到的表格進行聯接,並且聯接的每個輸出列代表目標表格的更新操作。使用 FROM 時,應確保聯接最多為每個要修改的列產生一個輸出列。換句話說,目標列不應與來自其他表格的多個列聯接。如果這樣做,則只會使用其中一個聯接列來更新目標列,但是將使用哪個聯接列並不容易預測。

由於這種不確定性,僅在子查詢中引用其他表格更安全,儘管通常比使用聯接更難讀懂且速度更慢。

在分割表格的情況下,更新列可能會導致它不再滿足包含分割區的分割約束。在這種情況下,如果分割樹中存在另一個分割區,此列滿足其分割約束,則該列將被移動到該分割區。如果沒有這樣的分割區,則會發生錯誤。在幕後,列移動實際上是 DELETEINSERT 操作。

正在移動的列上同時進行 UPDATEDELETE 操作可能會導致序列化失敗錯誤。假設會話 1 正在分割鍵上執行 UPDATE,同時,會話 2 對於此列是可見的,它對該列執行 UPDATEDELETE 操作。在這種情況下,會話 2 的 UPDATEDELETE 將檢測到列移動並引發序列化失敗錯誤(總是返回 SQLSTATE 代碼 '40001')。如果發生這種情況,應用程式可能希望重試事務。在表格未分割或沒有列移動的通常情況下,會話 2 將識別新更新的列,並在此新列版本上執行 UPDATE/DELETE

請注意,雖然列可以從本機分割區移動到外部表格分割區(前提是外部資料包裝器支援元組路由),但它們不能從外部表格分割區移動到另一個分割區。

如果發現外鍵直接引用源分割區的祖先,但該祖先與 UPDATE 查詢中提到的祖先不同,則將列從一個分割區移動到另一個分割區的嘗試將會失敗。

範例

將表格 filmskind 欄位中的單字 Drama 更改為 Dramatic

UPDATE films SET kind = 'Dramatic' WHERE kind = 'Drama';

調整 weather 表格中一行中的溫度輸入,並將降水量重設為其預設值

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03';

執行相同的操作並返回更新的條目

UPDATE weather SET temp_lo = temp_lo+1, temp_hi = temp_lo+15, prcp = DEFAULT
  WHERE city = 'San Francisco' AND date = '2003-07-03'
  RETURNING temp_lo, temp_hi, prcp;

使用替代的欄位列表語法來執行相同的更新

UPDATE weather SET (temp_lo, temp_hi, prcp) = (temp_lo+1, temp_lo+15, DEFAULT)
  WHERE city = 'San Francisco' AND date = '2003-07-03';

使用 FROM 子句語法,增加管理 Acme Corporation 帳戶的銷售人員的銷售計數

UPDATE employees SET sales_count = sales_count + 1 FROM accounts
  WHERE accounts.name = 'Acme Corporation'
  AND employees.id = accounts.sales_person;

使用 WHERE 子句中的子查詢執行相同的操作

UPDATE employees SET sales_count = sales_count + 1 WHERE id =
  (SELECT sales_person FROM accounts WHERE name = 'Acme Corporation');

更新帳戶表格中的聯絡人姓名,以符合目前指定的銷售人員

UPDATE accounts SET (contact_first_name, contact_last_name) =
    (SELECT first_name, last_name FROM employees
     WHERE employees.id = accounts.sales_person);

類似的結果可以使用聯接來完成

UPDATE accounts SET contact_first_name = first_name,
                    contact_last_name = last_name
  FROM employees WHERE employees.id = accounts.sales_person;

但是,如果 employees.id 不是唯一鍵,則第二個查詢可能會產生意外的結果,而如果有多個 id 匹配,則第一個查詢保證會引發錯誤。此外,如果特定 accounts.sales_person 條目沒有匹配項,則第一個查詢會將相應的姓名欄位設定為 NULL,而第二個查詢根本不會更新該列。

更新摘要表格中的統計資料,以符合目前的資料

UPDATE summary s SET (sum_x, sum_y, avg_x, avg_y) =
    (SELECT sum(x), sum(y), avg(x), avg(y) FROM data d
     WHERE d.group_id = s.group_id);

嘗試插入新的庫存項目以及庫存數量。如果該項目已存在,則改為更新現有項目的庫存數量。若要執行此操作而不導致整個事務失敗,請使用儲存點

BEGIN;
-- other operations
SAVEPOINT sp1;
INSERT INTO wines VALUES('Chateau Lafite 2003', '24');
-- Assume the above fails because of a unique key violation,
-- so now we issue these commands:
ROLLBACK TO sp1;
UPDATE wines SET stock = stock + 24 WHERE winename = 'Chateau Lafite 2003';
-- continue with other operations, and eventually
COMMIT;

變更游標 c_films 目前所指行的 films 表格的 kind 欄位

UPDATE films SET kind = 'Dramatic' WHERE CURRENT OF c_films;

影響多個列的更新可能會對系統效能產生負面影響,例如表格膨脹、複寫延遲增加和鎖定爭用增加。在這種情況下,以較小的批次執行操作可能是有意義的,可能在批次之間對表格執行 VACUUM 操作。雖然 UPDATE 沒有 LIMIT 子句,但可以透過使用 Common Table Expression 和自聯接來獲得類似的效果。使用標準 PostgreSQL 表格存取方法,對系統欄位 ctid 進行自聯接非常有效率

WITH exceeded_max_retries AS (
  SELECT w.ctid FROM work_item AS w
    WHERE w.status = 'active' AND w.num_retries > 10
    ORDER BY w.retry_timestamp
    FOR UPDATE
    LIMIT 5000
)
UPDATE work_item SET status = 'failed'
  FROM exceeded_max_retries AS emr
  WHERE work_item.ctid = emr.ctid;

此指令需要重複執行,直到沒有需要更新的列為止。使用 ORDER BY 子句可以讓指令優先更新特定的列;如果其他的更新操作使用相同的排序,它也可以避免死鎖。如果擔心鎖定競爭,可以將 SKIP LOCKED 加入到CTE中,以防止多個指令更新同一列。然而,那樣的話,最終需要一個沒有 SKIP LOCKEDLIMITUPDATE 指令,以確保沒有遺漏任何符合條件的列。

相容性

此指令符合SQL標準,除了 FROMRETURNING 子句是 PostgreSQL 擴充功能,以及使用 WITHUPDATE 的能力也是。

某些其他的資料庫系統提供了 FROM 選項,在該選項中,目標表格應該再次列在 FROM 裡面。這不是 PostgreSQL 解釋 FROM 的方式。移植使用此擴充功能的應用程式時要小心。

根據標準,目標欄位名稱的括號子列表的來源值可以是任何產生正確欄位數量的列值表達式。PostgreSQL 只允許來源值為列建構子或子 SELECT。在列建構子的情況下,單個欄位的更新值可以指定為 DEFAULT,但在子 SELECT 內部則不行。

提交更正

如果您在文件中發現任何不正確、與您特定功能的經驗不符或需要進一步澄清的地方,請使用此表格報告文件問題。