2010-10-29 83 views
3

當面臨在數據庫中複製記錄並修改少量值的任務時,我傾向於使用臨時表來避免寫出所有未更改的列。有誰知道這將如何影響大規模系統的性能?值得使用臨時表複製行的開銷?

一個簡單的例子(這也說明了爲什麼我喜歡的臨時表法):

比方說,我有一個表mytbl 50列col1 ... col50。我想插入一個新記錄,該記錄與col5 = 'Some Value'的行完全相同,但col45將設置爲'Some other value'

方法1

CREATE GLOBAL TEMPORARY TABLE tmptbl AS 
    SELECT * FROM myschema.mytbl; 

INSERT INTO tmptbl 
    (SELECT * 
    FROM myschema.mytbl 
    WHERE mytbl.col5 = 'Some Value'); 

UPDATE tmptbl 
SET col45 = 'Some Other Value'; 

INSERT INTO myschema.mytbl 
    (SELECT * FROM tmptbl); 

DROP TABLE tmptbl; 

方法2

INSERT INTO myschema.mytbl (col1, 
          col2, 
          col3, 
          col4, 
          col5, 
          col6, 
          col7, 
          col8, 
          col9, 
          col10, 
          col11, 
          col12, 
          col13, 
          col14, 
          col15, 
          col16, 
          col17, 
          col18, 
          col19, 
          col20, 
          col21, 
          col22, 
          col23, 
          col24, 
          col25, 
          col26, 
          col27, 
          col28, 
          col29, 
          col30, 
          col31, 
          col32, 
          col33, 
          col34, 
          col35, 
          col36, 
          col37, 
          col38, 
          col39, 
          col40, 
          col41, 
          col42, 
          col43, 
          col44, 
          col45, 
          col46, 
          col47, 
          col48, 
          col49, 
          col50) 
SELECT col1, 
     col2, 
     col3, 
     col4, 
     col5, 
     col6, 
     col7, 
     col8, 
     col9, 
     col10, 
     col11, 
     col12, 
     col13, 
     col14, 
     col15, 
     col16, 
     col17, 
     col81, 
     col19, 
     col20, 
     col21, 
     col22, 
     col23, 
     col24, 
     col25, 
     col26, 
     col27, 
     col28, 
     col29, 
     col30, 
     col31, 
     col32, 
     col33, 
     col34, 
     col35, 
     col36, 
     col37, 
     col38, 
     col39, 
     col40, 
     col41, 
     col42, 
     col43, 
     col44, 
     'Some Other Value', 
     col46, 
     col47, 
     col48, 
     col49, 
     col50 
FROM myschema.mytbl 
WHERE col5 = 'Some Value'; 

多少開銷不創建/刪除臨時表介紹?例如,如果這是生產規模系統的日常流程的一部分,額外的開銷是否會顯着?我意識到這很大程度上取決於系統的具體情況,但總體思路會很棒。

+6

當你說「當面臨在數據庫中複製記錄的任務時」,你真的指*一個記錄*嗎?因爲使用一個臨時表來處理這些數據是一個瘋狂的開銷。 – APC 2010-10-29 22:06:37

回答

10

首先, ,這不是Oracle在臨時表中的工作方式。全局臨時表是永久結構,它只是它們中的臨時數據。所以不需要爲每個操作創建和刪除表。這隻會增加開銷。其次,臨時表是合理的情況實際上是非常罕見的。在大多數情況下,一個簡單的變量就足夠了。此語法(插入單行)從9iR2開始有效:

declare 
    lrec emp%rowtype; 
begin 
    select * 
    into lrec 
    from emp 
    where empno = 1234; 

    lrec.empno = 9999; 
    lrec.sal = 5000; 

    insert into emp values lrec; 
end; 

請注意,在此公式中不需要括號。

+0

在這種情況下,甚至不需要變量。看到我的答案。 – 2010-11-05 17:23:59

4

我不明白這是如何可能更快與臨時表。

即使臨時表創建(和銷燬)放在一邊(兩個操作),您正在執行三個獨立的數據庫操作,而不是一個合併的操作。此外,您正在執行兩項操作,這些操作涉及您的非臨時表上的單獨鎖定,導致潛在併發性低於單個組合操作。當然,這對於獲取哪個實際的鎖具有一定的數據庫特定性。

最終,我認爲方法1的比方法2的開銷多,甚至沒有考慮臨時表創建/銷燬的肯定不可忽略的成本。

+1

*寫*方法1會更快,並且維護看起來像方法1的代碼可能更容易,但用戶(和系統購買者)需要*執行得最好的方法。 – 2010-10-29 17:47:24

+0

好吧,至少可以將它包裝到一個存儲過程中,這樣用戶就不必如此冗長。但是,我同意方法2存在維護問題,這是由於「描述」這種操作時SQL語法的缺陷。現在,需要考慮的一件事情是如何將一列表與許多列歸一化。也許有必要重新評估這種模式並以這種方式解決問題。 – 2010-10-29 17:48:43

+0

我知道方法1有更多的開銷。我承認在OP中。我的問題是開銷是否足以對系統產生負面影響。即如果這是日常過程的一部分,是否會增加大量的處理時間?我意識到這很大程度上取決於系統的具體情況,但我想要一個大概的想法。 – dpatchery 2010-10-29 17:52:06

1

如果您的過程使用的行少於250行,則可能需要考慮使用表變量而不是臨時表,因爲表變量使用內存而不是物理寫入tempdb。

3

我擔心這可能是一個過早優化的情況。看起來,這種方法「解決了」可能存在或可能不存在的問題,但肯定會給系統增加一定程度的複雜性。我的建議是

  1. 做最簡單的事情可能工作(在這種情況下,可能是INSERT-SELECT組合)。
  2. 衡量結果。
  3. 如果解決方案#1工作,沒問題。你做了最簡單的事情,它可以解決你的問題。
  4. 如果解決方案#1不起作用(或者工作不夠好),也可以 - 移動到下一個最簡單的可能有用的東西。
  5. 迭代,直到問題得到充分「解決」或時間或金錢的預算用盡。

分享和享受。

+0

+1用於測量,測量,測量。 – DCookie 2010-10-29 19:53:33

2

我假設你的方法的主要目標是避免必須將所有50列寫出兩次,如果將另一列添加到表中,我認爲你有不得不做出更改的缺點。還有人評論源表的結構,臨時表的開銷和過早的優化等,這是不是真的你的問題,但這個假設,我可以看到一個可能的工作第三種方法:

declare 
    type tmptbl_type is table of mytbl%rowtype; 
    cursor c is 
     select * from mytbl where col5 = 'Some Value'; 
    tmptbl tmptbl_type; 
begin 
    open c; 
    loop 
     fetch c bulk collect into tmptbl limit 1000; 
     for i in 1..tmptbl.count loop 
      tmptbl(i).col45 := 'Some Other Value'; 
     end loop; 
     forall i in 1..tmptbl.count 
      insert into mytbl values tmptbl(i); 
     exit when c%notfound; 
    end loop; 
    close c; 
end; 
/
2

你可以這樣做。

INSERT INTO myschema.mytbl (SELECT * FROM myschema.mytbl WHERE mytbl.col5 ='Some Value');

UPDATE myschema.mytbl SET col45 ='Some Other Value' WHERE col5 ='Some Value'AND rownum < = 1;

這比使用臨時表還要短,並且沒有臨時表的缺點。當然,如果你的實際問題比你所說的更復雜,這可能不起作用。

+3

如果MYTBL有主鍵或唯一約束,此解決方案將不起作用。另外兩個DML語句是一個DML語句太多了。 – APC 2010-11-05 17:39:37

+0

@APC非常真實,但從方法1看來,沒有主鍵或唯一約束。此外,似乎代碼簡潔性優於性能,否則方法2將優於這些答案中的任何一個。 – 2010-11-08 16:39:37