VBA的寫作技巧與增進效能(轉載)

(轉載:將由網上找到的認為有用的東西留起,方便日後自己使用及學習。作者如不滿,請告知,以便刪除不放在網誌上。)
*****************************************************************************************************
VBA的寫作技巧與增進效能

經由錄製產生的巨集,通常程式碼都會含有很多 Select,甚至往後自己寫的程式也習慣用一堆 Select。寫程式的人以為必須 Select 一個物件後才能對它做處理,但這是 [錄製巨集] 誤導的錯誤觀念 (自己也沒有徹底了解語法),而且是造成巨集執行效率不佳的原因之一。

一、數數看你的程式裡有多少 "Select" ?
除非程式就是要依使用者選取的物件來做動作,否則 Select 和 Selection 都是多餘的.
◎ 標準的物件控制語法:
  物件.方法 (例如 Range("A1").Copy)
  物件.屬性 = 值 (例如 Range("A1").ColorIndex = 15)
而不是一定要先 Select 物件然後再對 Selection 做動作.

舉例而言,你要複製 Sheet1.A1 的值到 Sheet2.B1 --
 Range("A1").Copy
 Sheets(2).Select
 Range("B1").Select
 Range("B1").PasteSpecial xlPasteValues
其實可以這麼寫 --
 Sheets(2).Range("B1") = Sheets(1).Range("A1")
如果內容與格式都要複製,可以這麼寫 --
 Sheets(1).Range("A1").Copy Sheets(2).Range("B1")

不要看這沒什麼,你的VBA觀念和程度能否更進一步,這是很重要的一點。

二、關閉螢幕更新 (Application.ScreenUpdating)
程式裡做的動作越多,螢幕更新的問題就越明顯。例如選取了儲存格、選取物件、複製、貼上、切換工作表... Excel 都會改變焦點 (Focus). 每改變一次,就是一次螢幕更新。想想看,在一連串的螢幕更新之中,不但令使用者眼花撩亂,程式執行的整體效能也會下降。

這與減少 Select 是一體兩面的事,其實很多選取儲存格、選取物件、複製、貼上、切換工作表... 的動作都是不必要的。只要技巧用的好,ScreenUpdating 幾乎可以束之高閣。

三、過多/不必要的迴圈也會降低執行效率
迴圈 (如 For...Next、Do...Loop等等) 是很重要的寫作技巧之一,它能大幅簡化程式中重複的動作,而且是錄製不出來的。
這裡所謂不必要的迴圈是指處理的範圍太大,浪費過多時間。例如
For Each cell In Columns(1)
 ......
Next
For Each cell In [A1:A65536]
 ......
Next
以上兩個迴圈都是處理 A 欄 6 萬多個儲存格。
說實在的,連幾千個Cell我都有點擔心了,何況幾萬個 -- 有必要嗎??
何不判斷好資料的範圍再來做迴圈 --
For Each cell In Range([A1], [A65536].End(xlUp))
 ......
Next

參考: 如何判斷資料範圍
http://gb.twbts.com/index.php/topic,315.0.html
http://gb.twbts.com/index.php/topic,584.0.html

四、釋放物件變數佔用的記憶體空間
在這裡尤指對應用程式(Appliation)的引用與存取,下例從Word表格取回資料至Excel工作表 --

Sub get_word_table( )
Dim wrdApp As Object
Set wrdApp = CreateObject("Word.Application") '建立引用Word應用程式的物件
Set wrdDoc = wrdApp.Documents.Open("D:\Temp\ole_test.doc") '引用Word文件
With wrdDoc.Tables(1)
 For r = 1 To .Rows.Count
  For c = 1 To .Columns.Count
  Cells(r, c) = .Cell(r, c)
  Next c
 Next r
End With
wrdDoc.Close 'close the document
wrdApp.Quit 'close Word
Set wrdDoc = Nothing '釋放物件變數
Set wrdApp = Nothing
End Sub

初學者常常會忽略最後兩句,如果不寫雖然不會影響程式的運行,但從記憶體管理和效能控制的角度而言,這是個很不好的習慣。

當省則省,省的是多餘重複的程式碼;
當用則用,用的是不可或缺的程式碼。
請問一下:
 Sheets(2).Range("B1") = Sheets(1).Range("A1")
這種寫法如果要針對某範圍例如從sheet1的A1:a10做陣列轉換到sheet2的a1:j1
要如何編寫程式碼?
***
sheet2.[A1:J1]=application.transpose(sheet1.[A1:A10])
***
一、數數看你的程式裡有多少 "Select" ?
select 真的是不大需要, 我目前已經很少使用了, 大部分用在要取得特定位址, 以作為下一個指令參考之用o

三、過多/不必要的迴圈也會降低執行效率
我以前會用 do while or if 指令來判斷後繼續執行, 後來碰到 i=i+1 時會碰到溢位問題, 後來就改用 find 指令,
我感覺執行速度是快了很多, 這是否比較好?
***
Cells(r, c) = .Cell(r, c)

這行,究竟幾時用單數的物件,例如cell,worksheet等字眼,有單數眾數,比較混亂
***
cells並無所謂擔負數之分,在EXCEL中cells就是全部儲存格,而括號中的2個引數,分別是列號與欄號
Cells(1,1)就是指到A1儲存格,他就是單一儲存格,若CELLS則會指向所有儲存格。
WorkSheet是工作表物件,這唯一會造成單數現象是發生在變數宣告時,當變數要宣告成工作表物件型態時
Dim Sh As WorkSheet
這就表示Sh變數是一個工作表
那麼,當我們在眾多工作表中,取得單一工作表就是在複數工作表中指名工作表
Set Sh=WorkSheets(index)
***
在EXCEL2003裡寫了如下:
01 Range("D6:E6").Copy
02 Range("h3").PasteSpecial xlPasteValues
因為各有一個值分別在D6&E6, 所以直接複製到H3時, 就會變成複製到H3 & I3

想要依照謝大的方式簡短, 分別試了如下:
Sheet1.[H2:I2] = Application.Transpose(Sheet1.[D6:E6])
結果僅會把D6的值複製到H2&I2, E6並不會複製到I2.

另外也試了:
Range("H2:I2") = Range("D6:E6")
結果都沒有動作.
請問如果要把同一個Sheet的D6 & E6複製到H3 & I3應該如何寫語法呢 ?
***
Range("H2:I2") = Range("D6:E6").Value
***
以下兩個寫法的效果是一樣的:
Range("H2:I2").Value = Range("D6:E6").Value
Range("H2:I2") = Range("D6:E6").Value
****
請教一下,當我的程式需要參考好幾個excel檔案的內容時,我以前的做法就是 worksheets("data.xls")....Range(xxx)這樣取得或更新資料,可是當資料內容龐大時,速度變的好慢好慢,就算是程式內 部創造一個陣列把資料先讀進來,也是得面臨那段緩慢的讀取時間。
請問VBA中是不是有甚麼正規做法可以解決大量儲存格資料存取的問題呢?
***
試試下列程式
在開啟的所有活頁簿中,將除了作用中活頁簿 (ActiveWorkbook) , 之外的Sheets(1).UsedRange ,收集到作用中活頁簿中的ActiveSheet
  1. Sub Ex()
  2.     Dim Book As Workbook, Rng(1 To 2) As Range
  3.     With ActiveWorkbook.ActiveSheet
  4.         Set Rng(1) = .Range("A" & Rows.Count).End(xlUp)
  5.         For Each Book In Workbooks
  6.             If Book.Name <> .Parent.Name Then
  7.                 Set Rng(2) = Book.Sheets(1).UsedRange
  8.                 Rng(1).Resize(Rng(2).Rows.Count, Rng(2).Columns.Count) = Rng(2).Value
  9.                 Set Rng(1) = .Range("A" & Rows.Count).End(xlUp).Offset(1)
  10.             End If
  11.         Next
  12.     End With
  13. End Sub
****
從此文章學到精華,這些簡化方式在各先進提供解答中
都可以學到,經由此篇更精華吸收,感恩
總是利用巨集錄製,再依學習到的VBA語法
想想怎麼簡化,來到這看看其他人的寫法
再看看以前寫的,總是可以學到一些
像是單純現存數據,以前總是犯了選取cells
再處理,近日學到一些,針對利用處理數據
寫了一段處理格式的語法
目前處理資料尚可,但一直持續學習簡化增進效能技巧中
關於Application.ScreenUpdating 學到,正在運用簡化中
目前正在學習如何簡化,如果有簡化idea 煩請提供供學習,感恩

KRowEnd = Cells(Rows.Count, 1).End(xlUp).Row '以 A欄資料為基礎 =1 判斷範圍
kcolend = Cells(1, Columns.Count).End(xlToLeft).Column '以 第一列資料為基礎 =1判斷判斷範圍

MsgBox "列數KRowEnd=" & KRowEnd
MsgBox "欄數kcolend=" & kcolend

Range(Cells(1, 1), Cells(1, kcolend)).Select

    With Selection.Interior
        .ColorIndex = 36 '淺黃色
        .Pattern = xlSolid
    End With
    Selection.Font.ColorIndex = 3


Range(Cells(2, 1), Cells(KRowEnd, kcolend)).Select

    ActiveWindow.FreezePanes = True
    Selection.FormatConditions.Delete
    Selection.FormatConditions.Add Type:=xlExpression, Formula1:= _
        "=MOD(ROW(),2)"
    Selection.FormatConditions(1).Interior.ColorIndex = 15
Range(Cells(1, 1), Cells(1, kcolend)).Select
    Selection.AutoFilter
    Cells.EntireColumn.AutoFit

沒有留言:

張貼留言