【问题标题】:VBA function in Excel ADODB queryExcel ADODB 查询中的 VBA 函数
【发布时间】:2014-10-03 07:10:46
【问题描述】:

我在 Excel 2007 中打开 ADODB 连接以查询当前工作簿的工作表之一。尝试添加自定义 VBA 函数时,会引发错误“未定义的函数名称”。连接:

Dim connection As String
Dim records As ADODB.Recordset
Dim query As String
Dim fileName As String

fileName = ThisWorkbook.FullName
connection = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & fileName & ";Extended Properties=""Excel 12.0 Xml;HDR=YES;IMEX=1"";"
query = "select t.[Col1] from [Sheet1$] As t"

Set records = New ADODB.Recordset
records.Open query, connection

Sheets(2).Range("A1").CopyFromRecordset records

我想要实现的是在选择中有另一列,比如

query = "select t.[Col1], myFunc() from [Sheet1$] As t"

其中 myFunc 是工作簿中定义的函数。

我知道在 Access 中可以进行类似的操作(在查询中使用自定义 VBA 函数)。这在 Excel 中也可以吗?

此方案的最佳做法或解决方法是什么?

【问题讨论】:

  • 要弄清楚问题是什么:您说“在 Excel 2007 中打开 ADODB 连接以查询当前工作簿的工作表之一”。你为什么这样做?如果您在 Excel 中,为什么不直接访问工作表?但是:您的“查询”是一个字符串。因此,您可以根据需要通过连接来构建此字符串。如果函数 myFunc() 返回一个字符串,那么可能是:query = "select t.[Col1], " & myFunc() & " from [Sheet1$] As t" 是你想要的吗?如果不是,生成的查询字符串应该是什么样子?
  • 阿克塞尔,感谢您的提问! 1.) 当使用查询处理大量数据时,操作和维护比单独访问单元格更清洁、更高效和更快。尤其是在连接多个工作表(或表格)、按多个字段进行分组和排序时。 2.) 连接查询字符串不是一个选项,因为连接在传递给连接对象之前只对函数进行一次评估。我想要实现的是在每次运行时评估函数,就像它在任何数据库引擎中一样。
  • 如果您在 Access 中编写查询,则可以使用自定义 VBA 函数。查询字符串如下所示:“select field1, myFunc(field2) from table1”并且 myFunc 将在每一轮中使用实际的 field2 值进行评估。
  • 啊,我明白了。您尝试像存储过程一样调用 Excel VBA 函数。这是不可能的,我想。但也许其他人知道方法。
  • 没错。在Access中是可能的,这就是我问这个问题的原因。但如果在 Excel 中无法实现,是否有解决方法。

标签: vba excel ado adodb


【解决方案1】:

我认为您需要在这里做一些基本的解释,也许还需要回答这个问题:

SQL中的函数从何而来?

如果您向任何支持 ANSI 标准 SQL 的数据库服务器发送查询,数据库引擎将解析并运行 SQL 原生的“内联”函数:LEFT()、SUBSTRING()、SIN()、SQR () 等。这里有一个简短的列表:

http://www.smallsql.de/doc/sql-functions/index.html

Oracle 服务器将提供扩展 ANSI SQL 的附加功能,Microsoft SQL Server 也是如此; PL-SQL 和 T-SQL 都具有标准 SQL 中不可用的功能。它们都允许 DB Owner 创建自己的函数,这些函数驻留在服务器上,也可用于 SQL 查询。

Microsoft Jet SQL 与 ANSI SQL完全 不同,它的本地函数集相当有限;但是没有人会介意他们在 MS-Access 环境中运行 Jet SQL,因为几乎所有 Visual basic for Applications 功能都可以通过 MS-Access 提供给 SQL 引擎使用。

此外,他们编写并在本地 MS-Access 项目中可见的所有全局声明的 VBA 函数也可用于 SQL 引擎。

只要您从 Microsoft Access 运行查询。

这是最简单的部分......

一旦移出 MS-Access 环境,就看不到 VBA。

您可以使用 Jet SQL 查询数据(Microsoft.ACE.OLEDB.12.0 提供程序正是这样做的)但是,如果您从 Excel 运行它,您将无法享受 MS-Access 数据库引擎使用 VBA 的能力:您拥有本机 Jet SQL 函数列表,仅此而已

这里列出了函数,其他地方很少:

MS Access: Functions - Listed by Category

该列表现在包括IIF() - 内联“IF”函数 - 如果您想在 SELECT 子句中使用 CASE 语句,这就是您在 Jet SQL 中所拥有的一切;如果您在本机 Jet-SQL 中的第一课是查询中的所有 VBA NZ() 函数都停止工作,您会发现这很有用。

其中许多函数看起来很像熟悉的 VBA 函数:这会造成混淆,因为您不是在运行 VBA,而是在运行 SQL

很少有人知道,这个本机 Jet 函数列表与 MS-Access 提供给 SQL 的本机 VBA 函数列表不同,Microsoft 并没有在他们的文档中明确说明这一点。

这是一个完整的 PITA,因为查询 SQL 服务器或 Oracle DB 的每个人都希望服务器将运行他们的 SQL 查询中的所有和任何函数,这些函数被声明、导入或“本机”到该服务器上运行的 SQL 方言.您已经在 Access mdb 中声明了 VBA 函数,并且您希望它们对 SQL 也是可见的!

如何不解决此问题:

我见过一个 Sybase 服务器,其中聪明但误入歧途的数据库所有者从您肯定使用过的外部库中导入了函数,而没有意识到它存在于每个 MS-Access 数据库中:vbaen32.dll,VBA 函数枚举 dll。这需要大量的工作,而且从来没有相当成功:请不要试图复制这个天才的壮举。

所以简短的回答是“不”。

现在给出有用的答案:

Recordset.GetRows() 会将您的记录集作为二维 VBA 数组返回,您可以在 SQL 引擎完成排序、过滤和聚合的繁重工作后在其上运行 VBA 函数。

如果您在 Forward-Only 游标上按块顺序运行 vba,调用 Recordset.GetRows(Rows:=1024) 直到结束,您可以在大型数据集上高效地执行此操作,而不会占用过多内存数据。

尽管您可能想问:“我为什么要这样做?”,因为很难想到一个更好的分析和设计不会揭示更好解决方案的过程。我,我必须为一个团队实施该 hack,该团队依赖于 Excel 的进程在 csv 文件上运行,该文件不断增长……并且增长到只能由数据库驱动程序读取的 TB 大小。他们工作的环境中,获得合适的数据库服务器需要 2-3 年的持续管理努力。

如果您是在我退出后继承了该特定过程的幸运儿子,我建议您在从轨道上删除该站点后尝试我的 GetRows“解决方案”。



脚注:如果您找到更好的 Jet SQL 函数在线列表,请扩展此答案。

同时,我会敦促任何其他阅读本文的“堆栈贡献者”将自己的链接添加到 Jet SQL 原生函数的良好列表中——如果你能找到的话。大多数是错误的,没有一个是全面的,很少有人明确指出本机函数和导入的 VBA 之间存在差异。据我所知,Jet Engine 正在从 VBAEN32.dll 导入和运行一组受限的函数——但 ADODB 下的 Jet 绝对不是一个功能齐全的 VBA 主机应用程序,当你在在 MS-Access 之外使用它。

【讨论】:

    【解决方案2】:

    我在这里只看到 1 个选项:

    由于“myFunc()”函数没有任何参数,您可以将该函数输入到单独的工作表中(例如,在 A2 中的 Sheet2,在 A1 中放置像“A”这样的标题)并在 SQL 查询中引用单元格喜欢:

    select t.[Col1], myFunc.A from [Sheet1$] As t, [Sheet2$] as myFunc
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-05-01
      • 2021-07-06
      • 2017-03-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多