【问题标题】:OleDb connection to Excel; how do I select fixed width, unbounded height?OleDb 与 Excel 的连接;如何选择固定宽度,无限高度?
【发布时间】:2009-07-16 14:53:31
【问题描述】:

我正在使用 OleDb 从 Excel 电子表格中选择数据。每个电子表格可以包含许多小表格,可能还包含标题和标签等家具。所以它可能看起来像这样,我们有两个表格和一些标题;

A B C D 1. . . . 2. . . . 3 表 1。 . . 4 标头 1 标头 2。 . 5 小时休伊。 . 6天杜威。 . 7 l 路易斯。 . 8 年代史克鲁奇。 . 9. . . . 10. . . . 11. . . . 12. . . . 13.表 2。 . 14.头1 头2 头3 15 . 1 富 x 16. 2 巴 17. 3 巴兹 18. . . . 19. . . .

在上一步中,用户选择了他们感兴趣的表的标题;在这种情况下,查看表 2,他们将选择范围 B14:D14

这些设置已保存,然后我需要查询该表。随着电子表格数据的更新,它可能会一遍又一遍地发生;可以随时添加更多行,但标题始终是固定的。有一个标记(空白行)标记数据结束

要选择表中的数据,我正在编写这样的查询;

SELECT * FROM [Sheet1$B14:D65535]

选择表2中的数据,然后手动检查sentinel行,但这似乎并不令人满意。 Excel 2003 只能读取 65,535 行(uint16),但 excel 2007 可以读取更多(uint32),所以我必须编写代码,根据文件的扩展名(.xls vs . xls?)。

有谁知道写查询的方法;

  • '选择 B14 下方和右侧的所有内容'?
  • '选择列 B->D 中的所有内容'
  • 'select B12:D*' 其中 * 表示“尽你所能”

【问题讨论】:

  • 一个 Excel 2003 工作表可以包含 65536 行,内部编号为 0 到 65535,外部编号为 1 到 65536。

标签: c# excel oledb


【解决方案1】:

先决条件:您可以在代码中轻松确定最大行数是多少。

假设 (1) 每个 SELECT 的开销很大,因此一次选择一行很慢 (2) 选择 64K 或 8M 行(即使是空白)很慢......所以你想看看是否在中间可以更快。试试这个:

一次选择 CHUNKSIZE(例如 100 或 1000)行(否则会超出 MAX_ROWS)。扫描每个块以查找标记数据结束的空白行。

更新:实际回答明确的问题:

问:有谁知道写查询的方法;

Q1:'选择 B14 下方和右侧的所有内容'?

A1:select * from [Sheet1$B12:] 不起作用。您必须在 Excel 2003 和 Excel 2007 中执行 ...B12:IV 。但是您不需要这样做,因为您知道最右边的列是什么;见下文。

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()

【讨论】:

    【解决方案2】:

    几个可能的解决方案:

    1. 将表放在单独的工作表上,然后只需查询整个工作表。
    2. 为 Excel 中的每个表命名(在 Excel 2007 中,选择表,单击鼠标右键,然后选择命名范围...),然后在查询中使用此名称而不是“Sheet1$B14:D65535” .

    希望对您有所帮助。

    编辑

    这是第三个想法:

    我不确定您使用什么来查询数据库,但如果您的查询引擎支持变量(例如 Sql Server),您可以存储...的结果...

    从 NameOfServer 中选择 COUNT(*)...Sheet1$

    ...在一个名为@UsedRowCount 的变量中,它将为您提供工作表中实际使用的行数。所以,@UsedRowCount = LastRowUsed - InitialBlankRows。

    然后您可以使用字符串连接将“65535”替换为@UsedRowCount + @InitialBlankRows。您必须将 @InitialBlankRows 设置为一个常量(在您的示例中,它将是 3,因为第一个表的标题行位于第 4 行)。

    【讨论】:

    • 谢谢,丹。不幸的是,我无法控制工作表——它们是随意从用户那里进来的,我只需要处理给我的东西。
    • 明白了。我刚刚在我的答案中添加了第三个想法。
    【解决方案3】:

    您说在上一步中,用户已经选择了标题。谁会说在当前感兴趣的区域下方没有几个空白行,后面跟着另一个不相关的表?我建议你让他们选择他们感兴趣的整个范围——这应该可以解决这两个问题。

    【讨论】:

    • 截断额外的细节由应用程序处理;我从数据表中读取行,直到数据用完;即,我得到一个完整的空白行。然后我自己终止阅读。不幸的是,我不能总是要求用户选择整个范围。在某些情况下,用户正在为电子表格选择标题,其中数据可以更改,但格式不能。例如,他们可能有带有固定标题(公司、开盘价、收盘价、日期)的股票报价,但实际记录数量会随时间而变化。如果他们选择50行,那么当行数> 50时,导入会给出太少的数据。
    • 您的问题需要澄清(请编辑它):(1)“选择一次,运行多次”而不是“(选择然后运行)*多次”。 (2)有一个标记(空白行)标记数据结束(3)当您说“我从数据表中读取行直到数据用完”时,这是否意味着“我从SELECT * FROM [Sheet1$B14:D65535]的结果中读取直到”还是它的意思是“我做单行选择直到”? (4) 您在代码中确定最大行数是 2^16 还是 2^20 的难易程度?
    【解决方案4】:

    我会采用 John 的解决方案(一次读取 1000 行)。

    如果您安装了 Excel,您还可以使用 OLE 自动化。

    我在 Excel 中记录了一个简单的宏,它选择当前表格中的最后一个单元格。

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

    现在您只需要在 C# 中翻译它并读取活动单元格的地址。

    【讨论】:

      【解决方案5】:

      我们阅读整个电子表格(即:SELECT * FROM [Sheet1$])并处理我们应用程序代码中的所有其他内容。很容易通过生成的 OleDbDataReader 到达数据的起点并开始处理。

      这可能不是从 Excel 中提取数据绝对最快的方法,但它是可靠的。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-06-14
        • 2016-09-20
        • 2011-12-22
        • 2019-11-16
        • 2018-03-20
        • 2012-03-20
        相关资源
        最近更新 更多