2009-07-16 5 views
5

Я использую OleDb для выбора данных из таблиц Excel. Каждая таблица может содержать множество небольших таблиц и, возможно, мебель, например, заголовки и ярлыки. Таким образом, это может выглядеть так, где у нас есть две таблицы и некоторые заголовки;OleDb подключение к Excel; как выбрать фиксированную ширину, неограниченную высоту?

 
      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 .   .   .   . 

На предыдущем этапе пользователь выбрал заголовки таблицы, в которой они заинтересованы; в этом случае, глядя на таблицу 2, они будут выбирать диапазон B14:D14.

Эти настройки сохраняются, и тогда мне нужно запросить эту таблицу. Это может повторяться снова и снова, по мере обновления данных электронных таблиц; в любое время может быть добавлено больше строк, но заголовки всегда фиксируются. Существует дозорный (пустой ряд), обозначающий конец данных

Чтобы выбрать данные в таблице, я пишу такой запрос;

SELECT * FROM [Sheet1$B14:D65535] 

выбрать данные в таблице 2, а затем вручную проверки для дозорной строки, но это кажется неудовлетворительно. Excel 2003 может читать только 65535 строк (uint16), но excel 2007 может читать еще много (uint32), поэтому мне нужно написать код, который дает другой запрос для Excel 2003 и 2007 на основе расширения файла (.xls vs. XLS?).

Кто-нибудь знает способ написать запрос, который говорит либо;

  • 'выбрать все, что находится внизу и справа от B14'?
  • 'выбрать все в столбцах B-> D'
  • 'выберите B12: D *' где * означает, что 'все, что можно'
+0

Лист Excel 2003 может содержать 65536 строк, пронумерованных от 0 до 65535 внутри и от 1 до 65536 снаружи. –

ответ

9

Предварительное условие: вы можете легко определить в своем коде количество максимальных номеров строк.

Предполагая, что в SELECT есть большие накладные расходы, поэтому выбор строки за один раз медленный (2) ВЫБОР 64K или 8M строк (даже если пустой) медленный ... поэтому вы хотите увидеть, где-то в средний может быть быстрее. Попробуйте следующее:

Выберите строки CHUNKSIZE (например, 100 или 1000) за раз (меньше, если вы в противном случае превысили бы MAX_ROWS). Сканирование каждого фрагмента для пустой строки, которая отмечает конец данных.

UPDATE: На самом деле, отвечая на явные вопросы:

Q: Кто-нибудь знает, как написать запрос, который говорит, что либо;

Q1: «выбрать все в порядке и вправо от B14»?

A1: select * from [Sheet1$B12:] не работает. Вам нужно будет сделать ...B12:IV в Excel 2003 и независимо от того, что находится в Excel 2007. Однако вам это не нужно, потому что вы знаете, что такое ваш самый правый столбец; Смотри ниже.

Q2: 'выбрать все, что в столбцах B-> D'

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

Q3: 'выберите B12: D *' где * означает 'все, что можно'

A3: выберите * из [Sheet1 $ B12: D]

Протестировано с помощью Python 2.5, используя следующий код:

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

Пара возможных решений:

  1. Положите таблицы на отдельных листах, а затем просто запросите весь рабочий лист.
  2. Дайте каждой таблице в Excel имя (в Excel 2007, выберите таблицу, щелкните правой кнопкой мыши и выберите «Название диапазона ...»), затем в вашем запросе используйте это имя вместо «Sheet1 $ B14: D65535», ,

Надеюсь, что это поможет.

EDIT

Вот третья идея:

Я не уверен, что вы используете для запроса базы данных, но если ваш двигатель запрос поддерживает переменные (например, SQL Server, например) вы можете сохранить результат ...

SELECT COUNT (*) FROM NameOfServer ... Лист1 $

... в переменной называется @UsedRowCount, что даст вам количество строк, фактически используемых вРабочий лист. Итак, @UsedRowCount = LastRowUsed - InitialBlankRows.

Затем вы можете использовать конкатенацию строк, чтобы заменить «65535» на @UsedRowCount + @InitialBlankRows. Вам нужно будет установить @InitialBlankRows на константу (в вашем примере это будет 3, так как строка заголовка первой таблицы находится в строке 4).

+0

Спасибо, Дэн. К сожалению, я не могу контролировать листы - они произвольно поступают от пользователей, и мне просто нужно иметь дело с тем, что мне дано. –

+0

Gotcha. Я только что добавил к моему ответу третью мысль. – devuxer

0

Вы говорите, что на предыдущем шаге пользователи выбрали заголовки. Кто скажет, что под областью текущего интереса нет нескольких пустых строк, за которыми следует другая несвязанная таблица? Я предлагаю вам получить их до выбрать весь интересующий их диапазон - это должно исправить обе проблемы.

+0

Отрезание дополнительной информации обрабатывается приложением; Я читаю строки из таблицы данных до тех пор, пока данные не исчерпываются; т.е. я получаю полный пустой ряд. Затем я прекращаю чтение. К сожалению, я не всегда могу попросить пользователя выбрать весь диапазон. В некоторых случаях пользователь выбирает заголовок для электронной таблицы, где данные могут меняться, но формат не может. Например, они могут иметь котировки акций с фиксированными заголовками (компания, цена открытия, цена закрытия, дата), но фактическое количество записей будет меняться со временем. Если они выбирают 50 строк, тогда, когда количество строк> 50, импорт будет давать слишком мало данных. –

+0

Пояснения, требуемые в вашем вопросе (пожалуйста, отредактируйте его): (1) «выберите один раз, запустите много« не »(выберите, затем запустите) * many». (2) есть дозорный (пустой ряд), обозначающий конец данных (3) Когда вы говорите: «Я читаю строки из таблицы данных до тех пор, пока данные не закончились» означает ли это «Я читаю результат« SELECT * FROM » [Sheet1 $ B14: D65535] 'до" или это означает, что я делаю однострочные SELECT до "? (4) Насколько легко вы можете определить в своем коде, будет ли максимальное количество строк 2^16 или 2^20? –

0

Я бы пошел с решением от Джона (чтение 1000 строк за раз).

Если у вас установлен Excel, вы также можете использовать автоматизацию OLE.

Я записал простой макрос в Excel, который выбирает последнюю ячейку в текущей таблице.


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

Теперь вам просто нужно перевести это на C# и прочитать адрес активной ячейки.

0

Мы читаем всю электронную таблицу (т.е.: SELECT * FROM [Sheet1 $]) и обрабатываем все остальное в нашем коде приложения. Достаточно легко пройти через результирующий OleDbDataReader, чтобы перейти к исходной точке ваших данных и начать обработку.

Возможно, это не самый быстрый способ сосать данные из Excel, но он надежен.

Смежные вопросы