2008-12-10 19 views
56

Tôi đã tự hỏi tôi có thể làm gì để cải thiện hiệu suất của tự động hóa Excel, vì nó có thể khá chậm nếu bạn có rất nhiều thứ trong bảng tính ...Excel Interop - Hiệu quả và hiệu suất

Đây là một vài tôi thấy bản thân mình:

  • ExcelApp.ScreenUpdating = false - tắt vẽ lại màn hình

  • ExcelApp.Calculation = Excel.XlCalculation.xlCalculationManual - tắt động cơ tính toán để Excel không tự động tính toán lại khi thay đổi giá trị của ô (lần lượt nó trở lại sau khi bạn đã hoàn tất)

  • Giảm cuộc gọi đến Worksheet.Cells.Item(row, col)Worksheet.Range - Tôi phải thăm dò hàng trăm ô để tìm ô mà tôi cần. Thực hiện một số bộ nhớ đệm của các vị trí ô, giảm thời gian thực hiện từ ~ 40 đến ~ 5 giây.

Loại cuộc gọi interop nào có hiệu suất cao về hiệu suất và nên tránh? Bạn có thể làm gì khác để tránh việc xử lý không cần thiết đang được thực hiện?

+3

+1 Tôi cũng gặp vấn đề về hiệu suất khi sử dụng Excel Interop và tôi đã học được điều gì đó mới: 'ExcelApp.Calculation = Excel.XlCalculation.xlCalculationManual'. Cảm ơn! =) –

+0

Cảm ơn bạn đã chia sẻ những phát hiện hiện tại của mình trong câu hỏi, rất hữu ích. – Alex

+0

một số tùy chọn có thể có khác ở đây http://stackoverflow.com/documentation/excel-vba/1107/vba-best-practices/5925/switch-off-properties-during-macro-execution – Slai

Trả lời

2

Hiệu suất cũng phụ thuộc rất nhiều vào cách bạn tự động hóa Excel. VBA nhanh hơn tự động hóa COM nhanh hơn tự động hóa .NET. Và ràng buộc sớm (biên dịch thời gian) cũng nhanh hơn ràng buộc muộn.

Nếu bạn có vấn đề về hiệu suất nghiêm trọng, bạn có thể nghĩ đến việc chuyển các phần quan trọng của mã sang mô-đun VBA và gọi mã đó từ mã tự động COM/.NET của bạn.

Nếu bạn sử dụng .NET, bạn cũng nên sử dụng các hội đồng interop chính được tối ưu hóa có sẵn từ Microsoft và không sử dụng các hội đồng interop tùy chỉnh được xây dựng.

+4

tất cả đều đúng. nhưng nếu bạn làm theo lời khuyên của việc không thực hiện rất nhiều cuộc gọi đối tượng Range nhỏ để thiết lập tài sản Value2, và chỉ cần vượt qua trong một mảng Object hoặc có được một, bạn sẽ không thực sự cần phải sử dụng VBA. –

4

Sử dụng vượt trội chức năng được xây dựng trong bất cứ khi nào có thể, ví dụ: Thay vì tìm kiếm toàn bộ một cột trong một chuỗi cho trước, sử dụng find lệnh có sẵn trong giao diện bằng tổ hợp phím Ctrl-F:

Set Found = Cells.Find(What:=SearchString, LookIn:=xlValues, _ 
    SearchOrder:=xlByRows, SearchDirection:=xlNext, _ 
    MatchCase:=False, SearchFormat:=False) 

If Not Found Is Nothing Then 
    Found.Activate 
    (...) 
EndIf 

Nếu bạn muốn sắp xếp một số danh sách, sử dụng lệnh excel sort, đừng làm nó bằng tay trong VBA:

Selection.Sort Key1:=Range("A1"), Order1:=xlAscending, Header:=xlGuess, _ 
    OrderCustom:=1, MatchCase:=False, Orientation:=xlTopToBottom, _ 
    DataOption1:=xlSortNormal 
+1

Đây là VBA, không interop. – CompanyDroneFromSector7G

+0

@bukko ý tưởng là cùng một điểm và rất tốt. cũng giống như khi sử dụng Cơ sở dữ liệu SQL, bạn nên sử dụng Truy vấn để thực hiện tác vụ của mình (hoặc SP) thay vì thực hiện nhiều truy vấn. –

+0

Điểm hữu ích được thực hiện! – CompanyDroneFromSector7G

4

Nếu bạn giá trị bỏ phiếu của nhiều tế bào, bạn có thể nhận được tất cả các giá trị di động trong một phạm vi lưu trữ trong một mảng biến thể trong một ngã swoop:

Dim CellVals() as Variant 
CellVals = Range("A1:B1000").Value 

Có một sự cân bằng ở đây, xét về kích thước của phạm vi bạn nhận được giá trị. Tôi đoán nếu bạn cần một ngàn hoặc nhiều hơn các giá trị tế bào này có lẽ là nhanh hơn so với chỉ lặp qua các tế bào khác nhau và bỏ phiếu các giá trị.

+5

-1, không có hành vi phạm tội với Jon, nhưng làm thế nào điều này có được upvoted rất nhiều? Câu hỏi đặt ra là nói về Excel Interop không phải VBA/VB6. khá chắc chắn Variant thậm chí không tồn tại như một loại từ COM interop (sử dụng đối tượng). –

+0

Range.Value cũng hoạt động với COM. và do đó, nếu ví dụ của nó cho VBA/.NET là kinda không liên quan ở đây, và không thay đổi câu trả lời. tôi đồng ý rằng sẽ tốt hơn nếu Jon thay đổi mẫu. –

42

Khi sử dụng C# hoặc VB.Net hoặc là có được hoặc thiết lập một phạm vi, tìm ra những gì kích thước tổng của dãy núi này là gì, và sau đó nhận được một lượng lớn 2 mảng đối tượng chiều ...

//get values 
object[,] objectArray = shtName.get_Range("A1:Z100").Value2; 
iFace = Convert.ToInt32(objectArray[1,1]); 

//set values 
object[,] objectArray = new object[3,1] {{"A"}{"B"}{"C"}}; 
rngName.Value2 = objectArray; 

Note quan trọng là bạn biết những gì datatype Excel đang lưu trữ (văn bản hoặc số) vì nó sẽ không tự động làm điều này cho bạn khi bạn đang chuyển đổi kiểu trở lại từ mảng đối tượng. Thêm kiểm tra nếu cần thiết để xác thực dữ liệu nếu bạn không thể chắc chắn trước loại dữ liệu.

+0

+1 Để sử dụng mảng đối tượng 2D. Ngoài ra còn có 'shtName.UsedRange.get_Value (XlRangeValueDataType.XlRangeValueDefault)' có thể được nhập vào một mảng đối tượng hai chiều và sẽ lấy tất cả các giá trị của ô cùng một lúc. –

+2

@Will Marcouiller: Có nhưng vấn đề với việc sử dụng thuộc tính UsedRange là nó có tác động tiêu cực về hiệu suất. Tôi không chắc chắn rằng nó thậm chí còn nhanh hơn nhiều so với sử dụng các phương pháp tế bào/bù đắp. –

+0

Thật tuyệt vời, nhưng làm cách nào để thêm hình ảnh vào xls bằng cách sử dụng kỹ thuật tối ưu hóa này? Trong chèn hình ảnh ứng dụng của tôi là một nút cổ chai. Mỗi tệp XLSX được tạo có ít nhất 300-400 hình ảnh. Giải pháp hiện tại gọi phương thức 'worksheet.Shapes.AddPicture()' nhiều lần như số lượng ảnh. Điều này thực sự chậm. –

11

Điều này là dành cho bất kỳ ai tự hỏi cách tốt nhất là điền một bảng excel từ tập kết quả db. Đây không phải là một danh sách đầy đủ bằng bất kỳ phương tiện nào nhưng nó liệt kê một vài tùy chọn.

Một số số hiệu suất khi cố gắng điền một bảng excel với 155 cột và 4200 bản ghi trên hộp Pentium 4 3GHz cũ bao gồm thời gian truy xuất dữ liệu không bao giờ dài hơn 10 giây theo thứ tự từ chậm đến nhanh nhất như sau ...

  1. một tế bào tại một thời điểm - Chỉ cần dưới 11 phút

  2. Populating một tập dữ liệu bằng cách chuyển đổi sang HTML + Tiết kiệm html vào đĩa + Đang tải html vào excel và tiết kiệm bảng như xls/xlsx - 5 phút

  3. Một cột tại một thời điểm - 4 phút

  4. Sử dụng thủ tục sp_makewebtask bị phản đối trong SQL 2005 để tạo ra một tập tin HTML - 9 Giây + Tiếp theo là tải file html trong excel và tiết kiệm như XLS/XLSX - Khoảng 2 phút.

  5. Chuyển đổi bộ dữ liệu Net thành ADO RecordSet và sử dụng WorkSheet.Range [] .Chức năng CopyFromRecordset để điền excel - 45 giây!

Tôi đã kết thúc bằng tùy chọn 5. Hy vọng điều này sẽ hữu ích.

+0

Cách tiếp cận lai tốt đẹp hmmm trong (5) Tuy nhiên bạn đã thử nghiệm phương pháp thứ sáu, sử dụng OLEDbConnection vào sổ làm việc và điền bảng dưới dạng bảng? các hạn chế với cách tiếp cận này bao gồm cần phải biết sơ đồ của từng cột trước (để ngăn chặn chuyển đổi kiểu không chính xác bằng excel). –

+1

@AnonymousType - Tôi phải thừa nhận rằng tôi đã không thử điền bảng dưới dạng bảng. Tôi vẫn mong rằng Microsoft đã cung cấp một chức năng "CopyFromDataSet" cho .net devs :-). – Ritesh

+0

đó sẽ là một tính năng VSTO rất ngọt ngào. –

1

Khi loại ẩn danh cho biết: đọc/ghi các khối phạm vi rộng là rất quan trọng đối với hiệu suất.

Trong trường hợp chi phí COM-Interop vẫn quá lớn, bạn có thể muốn chuyển sang sử dụng giao diện XLL, giao diện Excel nhanh nhất.

Mặc dù giao diện XLL chủ yếu dành cho người dùng C++, cả XL DNA và Addin Express đều cung cấp khả năng cầu nối .NET đến XLL nhanh hơn đáng kể so với COM-Interop.

1

Một điều quan trọng khác bạn có thể làm trong VBA là sử dụng Option Explicit và tránh các biến thể bất cứ khi nào có thể. Các biến thể không thể tránh được 100% trong VBA, nhưng chúng làm cho trình thông dịch làm việc nhiều hơn trong thời gian chạy và bộ nhớ chất thải.

Tôi thấy bài viết này rất hữu ích khi tôi bắt đầu với VBA trong Excel.
http://www.ozgrid.com/VBA/SpeedingUpVBACode.htm

Và cuốn sách này

http://www.amazon.com/VB-VBA-Nutshell-Language-OReilly/dp/1565923588

Tương tự như

app.ScreenUpdates = false //and 
app.Calculation = xlCalculationManual 

bạn cũng có thể thiết lập

app.EnableEvents = false //Prevent Excel events 
app.Interactive = false //Prevent user clicks and keystrokes 

mặc dù họ dường như không làm cho người khi lớn diffe rence như là hai đầu tiên.

Tương tự như đặt giá trị Phạm vi thành mảng, nếu bạn đang làm việc với dữ liệu chủ yếu là bảng có cùng công thức trong mỗi hàng của cột, bạn có thể sử dụng ký hiệu công thức R1C1 cho công thức của mình và đặt toàn bộ cột bằng chuỗi công thức để đặt toàn bộ nội dung trong một cuộc gọi.

app.ReferenceStyle = xlR1C1 
app.ActiveSheet.Columns(2) = "=SUBSTITUTE(C[-1],"foo","bar")" 

Ngoài ra, tạo XLL add-ins sử dụng ExcelDNA & NET (hoặc cách cứng trong C) cũng là cách duy nhất bạn có thể nhận UDFs để chạy trên nhiều chủ đề. (Xem thuộc tính IsThreadSafe thuộc tính ExcelFunction của thuộc tính Excel).

Trước khi tôi chuyển sang Excel Excel hoàn toàn, tôi cũng đã thử nghiệm tạo các thư viện có thể nhìn thấy trong .NET để tham khảo trong các dự án VBA. Xử lý văn bản nặng hơn một chút so với VBA theo cách đó, như đang sử dụng các lớp .NET List thay vì Bộ sưu tập của VBA, nhưng Excel DNA thì tốt hơn.