Подключение OleDb к Excel; как выбрать фиксированную ширину, неограниченную высоту? - PullRequest
5 голосов
/ 16 июля 2009

Я использую 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 может читать только 65 535 строк (uint16), но Excel 2007 может читать намного больше (uint32), поэтому мне нужно написать код, который дает другой запрос для Excel 2003 и 2007 на основе расширения файла (.xls против. XLS?).

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

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

Ответы [ 5 ]

9 голосов
/ 20 июля 2009

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

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

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

ОБНОВЛЕНИЕ: Фактически отвечая на четкие вопросы:

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

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 голос
/ 16 июля 2009

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

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

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

EDIT

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

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

ВЫБРАТЬ СЧЕТ (*) ОТ NameOfServer ... Sheet1 $

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

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

0 голосов
/ 26 июля 2009

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

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

0 голосов
/ 23 июля 2009

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

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

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


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

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

0 голосов
/ 20 июля 2009

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

...