2010-04-14 40 views
28

我有一個包含大量記錄(可能超過500 000或1 000 000)的表。我在這個表中添加了一個新列,並且我需要爲該列中的每一行填充一個值,使用此表中另一列的相應行值。更新表中所有行的有效方法

我嘗試使用單獨的事務來選擇100條記錄的每個下一個塊並更新它們的值,但是仍然需要花費數小時來更新Oracle10中的所有記錄。

什麼是最有效的方式來做到這一點在SQL中,沒有使用一些方言特有的功能,所以它無處不在(Oracle,MSSQL,MySQL,PostGre等)?

其他信息:沒有計算字段。有索引。使用生成的SQL語句逐行更新表。

+0

很少人後藤如果存在更新的/ instered列disabiling'INDEX' ES的極致,讓晚上的工作,對其進行分析。 – Guru 2010-04-14 07:50:51

+1

我們需要更多信息。告訴我們關於表格模式......任何「計算」列?任何索引? 500k - 1m行不是很多記錄。 – Timothy 2010-04-14 08:46:19

+0

謝謝大家的快速響應。我跳過了我正在使用生成的SQL語句的部分。現在我深入瞭解它,它看起來像生成的SQL逐行更新!因此,任何試圖以100個記錄塊分隔的嘗試都是沒有意義的......我將更改代碼以生成正確的SQL UPDATE語句,如同在接受的答案中一樣。 – 2010-04-14 09:35:50

回答

42

的常用方法是使用UPDATE:

UPDATE mytable 
    SET new_column = <expr containing old_column> 

你應該能夠做到這一點是一個單一的交易。

+0

這聽起來像OP知道如何在單個事務中執行此操作,但存在性能問題,因此他試圖將其分批處理爲單獨的事務。 – Timothy 2010-04-14 08:01:05

+1

這是可能的,但是非常特別的是,1 M行應該花費這麼長時間來更新單個列。由於缺乏對集合操作的理解,或者因爲他們試圖計算客戶端代碼中的新值(由於缺乏理解,出於必要性或者再次出現),OP也可能一次更新記錄, 。無論如何,如果OP指出上述哪些情況適用於他們,我將能夠更新我的答案。 – 2010-04-14 08:05:46

+0

夠公平的。我同意需要更多信息。 – Timothy 2010-04-14 08:21:49

2

您可以刪除表上的任何索引,然後執行插入操作,然後重新創建索引。

+0

+1。建議這是時間問題,但是,對於10M或更多行,只要您快速,快速地完成,就可以完成。 – Guru 2010-04-14 07:52:59

+4

對於任何你崇拜的神的愛,_在安靜的時間_這。否則,你的用戶會追蹤你,折磨你,殺了你,四分之一你,焦油和羽毛的遺體,然後燒他們,並吐在你的身體部位。最低限度。他們可能會做得更糟。 – paxdiablo 2010-04-14 08:24:04

+1

聽起來像是在性能祭壇上犧牲的DBA的呻吟聲...... – Timothy 2010-04-14 08:42:51

0

可能不適合你,但是在過去類似的情況下,我使用了一些技巧。

created created_ {table_name},然後選擇批量插入到此表中。一旦完成,這取決於Oracle(我不知道或使用)支持以原子方式重命名錶的能力。 updated_ {table_name}變爲{table_name},而{table_name}變爲original_ {table_name}。

最後一次我不得不這樣做是因爲一個有數百萬行的索引嚴重的表,絕對肯定無法在需要對其進行一些重大更改的持續時間內鎖定。

8

由於馬塞洛提示:

UPDATE mytable 
SET new_column = <expr containing old_column>; 

如果時間過長,未能因「快照太舊」錯誤(例如,如果表達式查詢另一種高活性表),如果爲新值列總是不爲NULL,您可以批量更新表:

UPDATE mytable 
SET new_column = <expr containing old_column> 
WHERE new_column IS NULL 
AND ROWNUM <= 100000; 

只需運行此語句,然後再運行它;沖洗,重複,直到它報告「0行更新」。這需要更長的時間,但每次更新都不太可能失敗。

編輯:

一個更好的選擇,應該是更有效的是使用DBMS_PARALLEL_EXECUTE API。

示例代碼(從Oracle文檔):

DECLARE 
    l_sql_stmt VARCHAR2(1000); 
    l_try NUMBER; 
    l_status NUMBER; 
BEGIN 

    -- Create the TASK 
    DBMS_PARALLEL_EXECUTE.CREATE_TASK ('mytask'); 

    -- Chunk the table by ROWID 
    DBMS_PARALLEL_EXECUTE.CREATE_CHUNKS_BY_ROWID('mytask', 'HR', 'EMPLOYEES', true, 100); 

    -- Execute the DML in parallel 
    l_sql_stmt := 'update EMPLOYEES e 
     SET e.salary = e.salary + 10 
     WHERE rowid BETWEEN :start_id AND :end_id'; 
    DBMS_PARALLEL_EXECUTE.RUN_TASK('mytask', l_sql_stmt, DBMS_SQL.NATIVE, 
           parallel_level => 10); 

    -- If there is an error, RESUME it for at most 2 times. 
    l_try := 0; 
    l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask'); 
    WHILE(l_try < 2 and l_status != DBMS_PARALLEL_EXECUTE.FINISHED) 
    LOOP 
    l_try := l_try + 1; 
    DBMS_PARALLEL_EXECUTE.RESUME_TASK('mytask'); 
    l_status := DBMS_PARALLEL_EXECUTE.TASK_STATUS('mytask'); 
    END LOOP; 

    -- Done with processing; drop the task 
    DBMS_PARALLEL_EXECUTE.DROP_TASK('mytask'); 

END; 
/

的Oracle文檔:https://docs.oracle.com/database/121/ARPLS/d_parallel_ex.htm#ARPLS67333

+0

我認爲這對於非常大且高度使用的桌子來說是個好主意!我還沒有這樣的失敗,但你有+1 :) – 2010-04-15 07:33:16

+1

有些事告訴我,這也避免了長時間鎖定整個表。在這種情況下,只有一個'小'塊會在單次運行期間被鎖定。 – 2013-11-21 09:36:06