2009-07-16 32 views
5

Tôi đang sử dụng OleDb để chọn dữ liệu từ bảng tính excel. Mỗi bảng tính có thể chứa nhiều bảng nhỏ và có thể là đồ nội thất như tiêu đề và nhãn. Vì vậy, nó có thể trông như thế này, nơi chúng tôi có hai bảng và một số chức danh;Kết nối OleDb với Excel; làm cách nào để chọn chiều rộng cố định, chiều cao không bị chặn?

 
      A   B   C   D 
    1 .   .   .   . 
    2 .   .   .   . 
    3 Table1  .   .   . 
    4 Header1  HEADER2 .   . 
    5 h   huey  .   . 
    6 d   dewey  .   . 
    7 l   loius  .   . 
    8 s   scrooge .   . 
    9 .   .   .   . 
    10 .   .   .   . 
    11 .   .   .   . 
    12 .   .   .   . 
    13 .   Table 2 .   . 
    14 .   HEADER1 HEADER2 HEADER3 
    15 .   1   foo  x 
    16 .   2   bar  y 
    17 .   3   baz  z 
    18 .   .   .   . 
    19 .   .   .   . 

Trong bước trước, người dùng đã chọn tiêu đề của bảng mà họ quan tâm; trong trường hợp này, nhìn vào bảng 2 họ sẽ chọn phạm vi B14:D14.

Các cài đặt này được lưu và sau đó tôi cần truy vấn bảng đó. Nó có thể xảy ra hơn và hơn, khi dữ liệu bảng tính được cập nhật; nhiều hàng hơn có thể được thêm vào bất kỳ lúc nào, nhưng tiêu đề luôn cố định. Có một hàng (hàng trống) đánh dấu phần cuối của dữ liệu

Để chọn dữ liệu trong bảng, tôi đang viết một truy vấn như thế này;

SELECT * FROM [Sheet1$B14:D65535] 

để chọn dữ liệu trong bảng 2, sau đó kiểm tra thủ công hàng sentinel, nhưng điều này có vẻ không hài lòng. Excel 2003 chỉ có thể đọc 65.535 hàng (uint16), nhưng excel 2007 có thể đọc nhiều hơn (uint32), vì vậy tôi phải viết mã cung cấp truy vấn khác cho Excel 2003 và 2007 dựa trên phần mở rộng của tệp (.xls vs. xls?).

Có ai biết cách viết một truy vấn cho biết;

  • 'chọn mọi thứ ở bên phải và bên phải của B14'?
  • 'chọn tất cả mọi thứ trong cột B-> D'
  • 'chọn B12: D *' nơi * có nghĩa là 'tất cả mọi thứ bạn có thể'
+0

Trang tính Excel 2003 có thể chứa 65536 hàng, được đánh số từ 0 đến 65535 nội bộ và từ 1 đến 65536 bên ngoài. –

Trả lời

9

Điều kiện tiên quyết: bạn có thể dễ dàng xác định mã của mình số hàng tối đa là bao nhiêu.

Giả sử (1) có phí lớn trên mỗi SELECT, do đó, việc chọn một hàng tại một thời điểm chậm (2) CHỌN 64K hoặc 8M hàng (ngay cả khi trống) chậm ... vì vậy bạn muốn xem có ở đâu đó không giữa có thể nhanh hơn. Hãy thử điều này:

Chọn CHUNKSIZE (ví dụ: 100 hoặc 1000) hàng cùng một lúc (ít hơn khi bạn chạy quá MAX_ROWS). Quét từng đoạn cho hàng trống đánh dấu phần cuối của dữ liệu.

UPDATE: Trên thực tế trả lời các câu hỏi rõ ràng:

Q: Có ai biết một cách để viết một truy vấn mà nói một trong hai;

Q1: 'chọn mọi thứ ở bên phải và bên phải của B14'?

A1: select * from [Sheet1$B12:] không hoạt động. Bạn sẽ phải làm ...B12:IV trong Excel 2003 và bất cứ điều gì trong Excel 2007. Tuy nhiên bạn không cần điều đó vì bạn biết cột ngoài cùng bên phải của bạn là gì; xem bên dưới.

Q2: 'chọn tất cả mọi thứ trong cột B-> D'

A2: select * from [Sheet1$B:D]

Q3: 'chọn B12: D *' nơi * có nghĩa là 'tất cả mọi thứ bạn có thể'

A3: chọn * từ [Sheet1 $ B12: D]

Được thử nghiệm bằng Python 2.5 bằng cách sử dụng mã sau:

import win32com.client 
import sys 
filename, sheetname, range = sys.argv[1:4] 
DSN= """ 
    PROVIDER=Microsoft.Jet.OLEDB.4.0; 
    DATA SOURCE=%s; 
    Extended Properties='Excel 8.0;READONLY=true;IMEX=1'; 
    """ % filename 
conn = win32com.client.Dispatch("ADODB.Connection") 
conn.Open(DSN) 
rs = win32com.client.Dispatch("ADODB.Recordset") 
sql = (
    "SELECT * FROM [Excel 8.0;HDR=NO;IMEX=1;Database=%s;].[%s$%s]" 
    % (filename, sheetname, range) 
    ) 
rs.Open(sql, conn) 
nrows = 0 
while not rs.EOF: 
    nrows += 1 
    nf = rs.Fields.Count 
    values = [rs.Fields.Item(i).Value for i in xrange(nf)] 
    print nrows, values 
    if not any(value is not None for value in values): 
     print "sentinel found" 
     break 
    rs.MoveNext() 
rs.Close() 
conn.Close() 
1

Couple giải pháp khả thi:

  1. Đặt bảng của bạn trên các trang tính riêng biệt, sau đó chỉ cần truy vấn toàn bộ trang tính.
  2. Đặt cho mỗi bảng trong Excel một tên (trong Excel 2007, chọn bảng, nhấp chuột phải và chọn Đặt tên cho dải ô ...), trong truy vấn của bạn, sử dụng tên này thay vì "Sheet1 $ B14: D65535" .

Hy vọng điều đó sẽ hữu ích.

EDIT

Dưới đây là một ý tưởng thứ ba:

Tôi không chắc chắn những gì bạn đang sử dụng để truy vấn cơ sở dữ liệu của bạn, nhưng nếu động cơ truy vấn của bạn hỗ trợ các biến (như SQL Server, ví dụ) bạn có thể lưu trữ các kết quả của ...

SELECT COUNT (*) FROM NameOfServer ... Sheet1 $

... trong một biến gọi là @UsedRowCount, mà sẽ cung cấp cho bạn số lượng hàng thực sự được sử dụng trong các bảng tính. Vì vậy, @UsedRowCount = LastRowUsed - InitialBlankRows.

Sau đó, bạn có thể sử dụng nối chuỗi để thay thế "65535" bằng @UsedRowCount + @InitialBlankRows. Bạn sẽ phải đặt @InitialBlankRows thành hằng số (trong ví dụ của bạn, nó sẽ là 3, vì hàng tiêu đề của bảng đầu tiên được đặt tại Hàng 4).

+0

Cảm ơn, Dan. Thật không may, tôi không nhận được để kiểm soát các tờ - họ là tùy ý đến từ người dùng và tôi chỉ phải đối phó với những gì tôi đã được đưa ra. –

+0

Gotcha. Tôi vừa thêm một ý tưởng thứ ba vào câu trả lời của tôi. – devuxer

0

Bạn nói rằng trong bước trước, người dùng đã chọn tiêu đề. Ai nói rằng dưới khu vực quan tâm hiện tại không có một vài hàng trống theo sau bởi một bảng không liên quan khác? Tôi khuyên bạn nên đưa họ đến chọn toàn bộ phạm vi mà họ quan tâm đến - điều đó sẽ khắc phục cả hai vấn đề.

+0

Cắt giảm chi tiết bổ sung được ứng dụng xử lý; Tôi đọc các hàng từ bảng dữ liệu cho đến khi dữ liệu hết; tức là, tôi nhận được một hàng trống hoàn toàn. Sau đó tôi tự mình đọc bản thân. Thật không may, tôi không thể luôn yêu cầu người dùng chọn toàn bộ dải ô. Trong một số trường hợp, người dùng đang chọn tiêu đề cho bảng tính nơi dữ liệu có thể thay đổi, nhưng định dạng không thể. Ví dụ: họ có thể có dấu ngoặc kép với các tiêu đề cố định (công ty, giá mở cửa, giá đóng cửa, ngày) nhưng số lượng hồ sơ thực tế sẽ thay đổi theo thời gian. Nếu họ chọn 50 hàng, thì khi số lượng hàng> 50, quá trình nhập sẽ cung cấp quá ít dữ liệu. –

+0

Làm rõ yêu cầu trong câu hỏi của bạn (hãy chỉnh sửa nó): (1) "chọn một lần, chạy nhiều" không "(chọn sau đó chạy) * nhiều". (2) có một dấu (hàng trống) đánh dấu kết thúc dữ liệu (3) Khi bạn nói "Tôi đọc các hàng từ bảng dữ liệu cho đến khi dữ liệu hết", điều này có nghĩa là "Tôi đọc từ kết quả của' SELECT * FROM [Sheet1 $ B14: D65535] 'cho đến khi" hoặc có nghĩa là "Tôi làm một hàng SELECT cho đến khi"? (4) Làm thế nào bạn có thể dễ dàng xác định trong mã của bạn cho dù số lượng tối đa của hàng là 2^16 hoặc 2^20? –

0

Tôi sẽ đi với giải pháp từ John (đọc 1000 hàng cùng một lúc).

Nếu bạn đã cài đặt Excel, bạn cũng có thể sử dụng tự động hóa OLE.

Tôi đã ghi lại một macro đơn giản trong Excel, chọn ô cuối cùng trong bảng hiện tại.


Sub Macro2() 
    Range("B14").Select 
    Selection.End(xlDown).Select 
    //MsgBox ActiveCell.Address, vbOKOnly 
End Sub 
 

Bây giờ bạn chỉ cần dịch điều này trong C# và đọc địa chỉ của ô hiện hoạt.

0

Chúng tôi đọc toàn bộ bảng tính (ví dụ: SELECT * FROM [Sheet1 $]) và xử lý mọi thứ khác trong mã ứng dụng của chúng tôi. Thật dễ dàng để chạy đua qua OleDbDataReader kết quả để đến điểm bắt đầu của dữ liệu của bạn và bắt đầu xử lý.

Nó có thể không phải là cách hoàn toàn nhanh nhất để hút dữ liệu từ Excel, nhưng nó là đáng tin cậy.

Các vấn đề liên quan