2010-01-28 141 views
4

我正在尋找任何建議來優化SAS程序中的以下PROC SQL語句。涉及的兩個表格每個包含約500萬條記錄,運行時間約爲46個小時。優化SQL UPDATE語句所需的建議。使用兩百萬條記錄表

該聲明正在尋找更新「舊」表的「新」版本。如果對於「PK_ID」的「舊」表沒有列出「3RD_ID」和「CODE」的值,而在「新」表中列出了「PK_ID」,則記錄列「3RD_ID」和「CODE」的值。

感謝您的任何建議...(代碼格式化真的下面!對於某些原因,我的空間進行展示,縮進...)

PROC SQL _METHOD; 
UPDATE NEW_TABLE AS N 
    SET NEW_2ND_ID=(SELECT 2ND_ID FROM OLD_TABLE AS O 
       WHERE N.PK_ID=0.PK_ID 
        AND N.2ND_ID<>O.2ND_ID 
        AND O.3RD_ID IS NULL 
        AND O.CODE IS NULL 
        AND N.3RD_ID IS NOT NULL 
        AND N.CODE IS NOT NULL 
        AND N.2ND_ID IS NOT NULL) 
     WHERE N.3RD_ID IS NOT NULL 
      AND N.PK_ID IS NOT NULL 
      AND N.CODE IS NOT NULL 
      AND N.2ND_ID IS NOT NULL; 
QUIT; 
+0

在PK_ID,2ND_ID和3RD_ID上的兩個表上都有簡單的索引,以及兩者上都有一個複合索引,包含所有這三個索引。 – whitespy9 2010-01-28 14:35:07

+0

當您找到「工作」解決方案時讓我知道 – Pentium10 2010-01-28 17:35:20

回答

3

到目前爲止所有的答案都堅定地面向你的問題的SQL部分,但在某種程度上忽略了SAS部分。我強烈建議嘗試使用data step update/modify/merge而不是proc sql來進行這種更新。應該可以對兩個表進行排序,並應用SQL中類似的邏輯,以確保更新正確的行/列。

我見過類似的更新在2000萬或更多的行上在幾分鐘內運行。

此外,檢查出http://runsubmit.com,一個SAS特定的stackoverflow風格的網站,更多SAS特定的答案。

披露:我是SAS員工。我與獨立運行的runubmit無關。

+0

謝謝。我終於找到了子彈,並將這個waaaaaaaaaay分解爲儘可能小的數據集。合併,然後最終更新。它現在運行平穩。不幸的是,它不得不做這些類型的「額外」數據步驟,我不喜歡使用SAS。我的代碼現在在屏幕上長時間執行一個簡單的更新語句。如果我必須再一次重做,我會在整個程序中使用複合哈希。 – whitespy9 2010-01-29 16:20:11

0

我覺得現在是嵌套子查詢,所以Select語句將會被觸發與條件匹配的記錄數。

但是,我會建議你去SQL的更新與加入。可以在這裏找到解釋:http://bytes.com/topic/oracle/answers/65819-sql-update-join-syntax

當您使用JOIN進行更新時,應用適當的索引。

也不是說你不應該使用那些包含2nd_id的索引,它們應該被禁用,然後在更新後重建,因爲它可以是海量數據更新。

+0

並非每個DBMS都將相關子查詢視爲單個查詢。SQL Server 2005及以上版本將大多數相關的子查詢轉換爲常規聯接相當不錯。 – ErikE 2010-01-28 20:32:55

0
UPDATE (
    SELECT O.2ND_ID, N.2ND_ID 
    FROM OLD_TABLE AS O 
    INNER JOIN NEW_TABLE AS N on O.PK_ID=N.PK_ID 
    WHERE N.2ND_ID <> O.2ND_ID 
    AND O.3RD_ID IS NULL 
    AND O.CODE IS NULL 
    AND N.3RD_ID IS NOT NULL 
    AND N.CODE IS NOT NULL 
    AND N.2ND_ID IS NOT NULL 
) t 
set N.2ND_ID = O.2ND_ID 
+1

只有在'old_table'中的'PRIMARY KEY'或'UNIQUE'索引覆蓋'pk_id'時才能工作。 – Quassnoi 2010-01-28 14:51:11

+0

@Quassnoi:如果這是一個問題,那麼初始海報的查詢也是錯誤的。 – ErikE 2010-01-28 20:33:37

+0

@Emtucifor:如果'pk_id'值是唯一的,即使列沒有被聲明爲'UNIQUE',原始查詢也會工作。這個查詢要求'old_table.pk_id'被聲明爲'UNIQUE',否則它將會因臭名昭着的'ORA-01779'而失敗。 – Quassnoi 2010-01-28 23:52:51

1

不要使用update,創建一個類似的新表,並使用插入(字段)從兩個表中選擇字段。

  • 在運行查詢之前刪除索引。
  • 在運行查詢之前刪除觸發器。

喜歡的東西:完成查詢後

insert into NEW_TABLE (field1,field2,NEW_2ND_ID) 
select field1, field2, (SELECT 2ND_ID FROM OLD_TABLE....) from NEW_TABLE 
  • 重新創建索引。
  • 查詢完成後重新創建觸發器。

+0

我看到你正在發表對其他建議答案的評論,至今尚未發表。您是否嘗試過我提出的解決方案? – Pentium10 2010-01-28 17:50:53

+0

我已經使用了部分解決方案。我已經從「舊」表中需要的內容創建了一個子集表。如果我無法找到更新優化的解決方案,我會考慮創建一個插入。 – whitespy9 2010-01-28 18:03:12

+0

請考慮'更新'是慢的,因爲鎖使用,和IO尋找,相比'全選','插入所有'。有時最好將數據傳輸到一個單獨的表中。 – Pentium10 2010-01-28 18:12:14

3

我不熟悉您使用的SQL的變種(你到底將這個新表您現有的替換表)。但是,無論您是否獲得更好的性能,您都應該使用ANSI連接語法。下面是它看起來像在T-SQL,修改你的系統:

UPDATE N 
SET N.2ND_ID = O.2ND_ID 
FROM 
    NEW_TABLE AS N 
    INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID 
WHERE 
    N.2ND_ID <> O.2ND_ID 
    AND N.3RD_ID IS NOT NULL 
    AND O.3RD_ID IS NULL 
    AND N.CODE IS NOT NULL 
    AND O.CODE IS NULL 

注意,我刪除了額外的條件是沒有必要的,例如N.2ND_ID <> O.2ND_ID已確保了這兩列不爲空。

但是,在兩個500萬行的表格中,你會得到糟糕的表現。這裏有一些想法來加速它。我敢打賭,你可以通過正確的戰略組合將這一點降到一小時以內。

  1. 將更新拆分成批(小塊,循環遍及整個集合)。雖然這聽起來與「不循環,使用集合」的正常數據庫建議相反,但它確實不是:您只是使用較小的集合,而不是在行級循環。像這樣批量更新的最好方法是「走聚集索引」。我不確定這個術語在你使用的DBMS中是否有意義,但實質上它意味着在每個循環中選擇你更新的塊,這是基於它們將在你正在更新的表對象中找到的順序。 PK_ID聽起來像是要使用的候選者,但是如果原始表數據沒有按此列排序,那麼它將變得更加複雜。在T-SQL配料循環可能是這樣的:

    DECLARE 
        @ID int, 
        @Count int 
    
    SET @ID = 1 
    SET @Count = 1 
    
    WHILE @Count > 0 BEGIN 
        UPDATE N 
        SET N.2ND_ID = O.2ND_ID 
        FROM 
         NEW_TABLE AS N 
         INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID 
        WHERE 
         N.2ND_ID <> O.2ND_ID 
         AND N.3RD_ID IS NOT NULL 
         AND O.3RD_ID IS NULL 
         AND N.CODE IS NOT NULL 
         AND O.CODE IS NULL 
         AND N.PK_ID BETWEEN @ID AND @ID + 4999 
        SET @Count = @@RowCount 
        SET @ID = @ID + 5000 
    END 
    

    這個例子假設你的PK_ID列密密麻麻,每次更新將真正觸及5000行。如果情況並非如此,那麼切換到使用TOP 5000的方法,並將更新的PK_ID輸出到表中,或者在一步中找到下一次更新的@StartID和@EndID,然後執行它。

    根據我的經驗,好的批量大小一般在1000到20000行之間。在MS-SQL服務器中,最佳位置似乎低於一個數字,強制從查找切換到掃描(因爲最終,數據庫引擎認爲單次掃描比多次查找便宜,儘管它經常處理500萬行表格時出錯)。

  2. 首先選擇要更新到工作/臨時表中的ID和數據,然後加入到該表中。這個想法是通過一個簡單的INSERT語句來接受巨大的掃描,然後將索引添加到臨時表並執行更新,而不需要複雜的WHERE子句。一旦表僅包含要更新的行和所需的列,不僅WHERE子句可能會丟失其大部分條件,而且臨時表每頁的行數少且行數多(因爲它沒有多餘的列),這將大大提高性能。這甚至可以在創建New表的「陰影」,然後是Old表的「陰影」,然後是它們之間的連接,最後連接回New表來更新它的階段完成。雖然這聽起來像很多工作,但我認爲你會對完全可以提供的瘋狂速度感到驚訝。

  3. 任何你可以做的轉換從舊錶讀取,而不是掃描將有助於。你可以做的任何事情來減少用來存放臨時數據的磁盤數量(例如5百萬行的巨大散列表)將會有所幫助。

+0

+1批處理(分區)。更新非常大的集合可能會產生巨大的事務/鎖定開銷和/或I/O活動。 – Arvo 2010-01-28 19:07:21

+0

+1也用於分區。一個500萬行的單筆交易對數據庫引擎來說是一場噩夢。很可能內存緩衝區已滿,並且在知道Tx是否將被提交或回滾之前,更改會刷新到磁盤。 – 2010-01-28 21:03:52

+0

由於某些奇怪的原因,SAS Proc SQL不允許與更新進行連接。如此巨大的障礙。 – 2010-01-29 20:30:51

0

您也可以嘗試將新表放在與舊錶不同的設備中,以利用其並行性。當然,如果你能說服DBA。