为什么带有VBA功能的SQL查询运行得如此之慢?

时间:2011-01-17 17:30:33

标签: sql sql-server sql-server-2008 ms-access

我继承了一个应用程序,可以在很多地方执行以下类型的查询:

select foo.f1, foo.f2, foo.f3
from foo
where foo.f4 = getFooF4()

getFooF4看起来像这样

Public Function getFooF4()
Dim dbCurrent As Database
Dim rstBar As Recordset

    Set dbCurrent = CurrentDb
    Set rstBar = dbCurrent.OpenRecordset("Bar", _
                                            dbOpenDynaset, _
                                            dbSeeChanges)
    getFooF4 = rstBar![myF4]
    ''yes this appears broken... Bar only contains one row :-/

    rstBar.close
    Set rstBar = Nothing
    dbCurrent.close
    Set dbCurrent = Nothing    
End Function
'' Note: in my experimentation getFooF4 only runs once during the 
''       execution of the query.

这最终运行得相当慢。如果我使用常量从查询中删除getFooF4():

select foo.f1, foo.f2, foo.f3
from foo
where foo.f4 = 123456

或参数:

select foo.f1, foo.f2, foo.f3
from foo
where foo.f4 = [myFooF4]

或加入:

select foo.f1, foo.f2, foo.f3
from foo
INNER JOIN bar ON bar.myF4 = foo.f4

它运行得更快。

为什么?

规范:在MS Access 2003中编写并运行的应用程序,后端数据库是SQL Server 2008。

3 个答案:

答案 0 :(得分:4)

使用GetFooF4的示例既不能由Sql Server优化,也不能由Access优化。并且一直重新打开这个rs是非常低效的。作为一般规则,请避免在查询中使用Access特定功能或代码。这可以防止Acces“按原样”向Sql server发送查询。它必须下载全部数据并在本地处理,这意味着更多的流量和更低的速度 见http://msdn.microsoft.com/en-us/library/bb188204(v=sql.90).aspx#optaccsql_topic2

答案 1 :(得分:2)

提高效率的两件事(尽管只有一个或另一个适用于这样的特定情况):

  1. 为您的函数定义一个返回类型,即Public Function getFooF4()应该是Public Function getFooF4() As Long(或者任何适当的数据类型。如果没有明确的数据类型,它将返回一个变体。实际上,永远不会有一个VBA函数应该缺少一个返回类型声明 - 如果它返回一个变量(这是非常合理的,特别是当你需要在某些情况下返回Null时),用As Variant定义它。它是一些其他数据类型,明确定义它。

  2. 在SQL中声明一个参数,以便查询优化器可以在计算查询计划时使用该信息。当您的WHERE子句使用函数提供标准时,这不适用,但如果您使用对控件上的字段的引用,则替换为:

  3.   select foo.f1, foo.f2, foo.f3
      from foo
      where foo.f4 = Forms!MyForm!MyControl
    

    ......用这个:

      PARAMETERS [Forms]![MyForm]![MyControl] Long;
      select foo.f1, foo.f2, foo.f3
      from foo
      where foo.f4 = Forms!MyForm!MyControl
    

    现在,在任何一种情况下,由于函数/参数在WHERE子句中,它只需要解析一次,所以即使函数效率低下(如此情况,初始化数据库变量)并打开一个记录集),它实际上并没有太大的区别。

    要考虑的另一件事是用一个简单的DLookup()替换该函数,该设计就是为了这个目的而设计的。或者,由于值来自表,您应该能够将其加入到单行表中:

      select foo.f1, foo.f2, foo.f3
      from foo INNER JOIN Bar ON foo.f4 = Bar.MyF4
    

    这可以通过查询优化器进行最大程度的优化,因为它根本没有任何未知数 - 查询优化器将知道它需要了解的有关数据类型和表统计信息的所有内容,并且可以选择最有效的检索方法。 / p>

答案 2 :(得分:1)

它与以下相比如何:

r = getFooF4()

select foo.f1, foo.f2, foo.f3
from foo
where foo.f4 = r

如果这和原作一样慢,那么答案很简单:getFooF4()函数是缓慢的部分。