Access 2007 用户自定义函数过加入慢

Posted

技术标签:

【中文标题】Access 2007 用户自定义函数过加入慢【英文标题】:Access 2007 user-defined function over join slow 【发布时间】:2013-08-07 18:17:21 【问题描述】:

为什么使用用户定义函数的查询比具有多个连接(包括外连接)的查询慢?该函数的原因是修改一个字符串,使其按数字排序。对字符串进行排序意味着100 < 99。该函数重新格式化99 as 099。所以,099 < 100。它让其他非数字值保持不变。

问题查询使用函数而不是带有连接的查询。返回 100 行需要 27 秒。相同的查询,带有一个函数,但超过一个表需要亚秒级。对带有连接的查询的查询的 SQL 替换是亚秒级的。没有函数的查询优于有连接的查询是亚秒级的。主表是 tblTests,有 517 行。该函数操作的列是一个文本列 fldPurity。

tblTests
fldTestsID    autonumber
fldPurity     Text.  field size 50. Indexed (Duplicates OK). Zero Length No. 

这里是功能代码。注意不同的输入。

Public Function SortablePercent(ByVal pVar As Variant) As String
'------------------------------------------------------------------
' Purpose:   Formats a string that may contain numbers or text values.
'            The string percent may contain % or + characters. Ignore
'            those characters during comparison. A string may start with numeric
'            characters, but end with alpha characters. Compute the length of the resulting
'            numeric characters. Length 3 is 100, no change. Prepend leading zeros to length 2
'            or 1 numerics. Do not add prepend to values starting with text.

' Coded by:  2013-08-05 Henry Helgen
' Arguments: pVar: The string to be formatted.
' To Test:   From the debug (immediate) window:
'            X = "97+%"
'            ? SortablePercent(X)
'            097
'            X = "98"
'            ? SortablePercent(X)
'            098
'            X = "99.9"
'            ? SortablePercent(X)
'            099.9
'            X = "100"
'            Print SortablePercent(X)
'            100
'            X = "Reagent Grade"
'            ? SortablePercent(X)
'            Reagent Grade
'            X = "85% & 15% H2O"
'            ? SortablePercent(X)
'            085 & 15 H2O
'------------------------------------------------------------------
Dim strHold As String 'working string
Dim lenNum  As Integer 'length of leading integer portion of number

    ' remove whitespace, %, + characters
    strHold = Replace(Replace(Nz(Trim(pVar), ""), "%", ""), "+", "")

    If IsNumeric(strHold) Then 'the entire string is numeric
        lenNum = Len(CStr(Int(strHold)))
        'Fill with leading zeros
        strHold = Switch(lenNum = 3, strHold, lenNum = 2, "0" & strHold, lenNum = 1, "00" & strHold)
    ElseIf IsNumeric(Left(strHold, 2)) Then '
        strHold = "0" & strHold
    ElseIf IsNumeric(Left(strHold, 1)) Then
        strHold = "00" & strHold
    End If 'numeric

    SortablePercent = strHold

End Function

这是使用函数而不是连接查询的慢速查询(27 秒)

SELECT parm_TestConcatReferenceDatasetExposure.fldPurity, 
       SortablePercent([fldPurity]) AS temp2, Count(*) AS RcdCount
FROM parm_TestConcatReferenceDatasetExposure
GROUP BY parm_TestConcatReferenceDatasetExposure.fldPurity, 
         SortablePercent([fldPurity])
ORDER BY SortablePercent([fldPurity]);

这是在一张表上使用函数的快速查询(

SELECT tblTests.fldPurity, 
       SortablePercent([fldPurity]) AS temp2, 
       Count(*) AS RcdCount
FROM tblTests
GROUP BY tblTests.fldPurity, SortablePercent([fldPurity])
ORDER BY SortablePercent([fldPurity]);

这里是没有函数的快速查询,而不是带连接的查询(

SELECT parm_TestConcatReferenceDatasetExposure.fldPurity, 
       Count(*) AS RcdCount
FROM parm_TestConcatReferenceDatasetExposure
GROUP BY parm_TestConcatReferenceDatasetExposure.fldPurity;

这是在一张表上使用 SQL 部分近似函数的快速查询(

SELECT tblTests.fldPurity, 
       IIf(IsNumeric(Replace(Replace(Nz(Trim([fldPurity]),""),"%",""),"+","")),CDbl(Replace(Replace(Trim([fldPurity]),"%",""),"+","")),Trim([fldPurity])) AS tempPurity, 
       Count(*) AS RcdCount
FROM tblTests
GROUP BY tblTests.fldPurity
ORDER BY IIf(IsNumeric(Replace(Replace(Nz(Trim([fldPurity]),""),"%",""),"+","")),CDbl(Replace(Replace(Trim([fldPurity]),"%",""),"+","")),Trim([fldPurity]));

这是带有连接的查询

SELECT q_Test.fldTestsID, q_DatasetTreatment.fldDatasetsID, 
       q_DatasetTreatment.fldExposureEffectsID, q_Test.fldValidated, 
       q_Test.fldPollutantID, q_Test.fldPollutantName, q_Test.fldPollutantCAS, 
       q_Test.fldModeOfActionID, q_Test.fldModeOfAction, q_Test.fldPollutantTypeID, 
       q_Test.fldPollutantType, q_Test.fldSpeciesID, q_Test.fldClass, q_Test.fldGenus,
       q_Test.fldSpecies, q_Test.fldCommonName, q_Test.fldTestTypeID, 
       q_Test.fldTestType, q_Test.fldTechniqueID, q_Test.fldTechnique,
       q_Test.fldConcUnits, q_Test.fldDescription AS fldConcUnitDescription,
       q_Test.fldMRID, q_Test.fldCETISID, q_Test.fldHardness, q_Test.fldSalinity, 
       q_Test.fldpH, q_Test.fldTemperature, q_Test.fldPurity, q_Test.fldDO, 
       q_Test.fldAcute, q_Test.fldUser, q_Test.fldComments, 
       IIf([q_sumTestReference].[fldTestsID] Is Not Null,[ConcatRef],"") AS CombinedRef, 
       q_DatasetTreatment.fldBiolVarNameID, q_DatasetTreatment.fldBiolVarName, 
       q_DatasetTreatment.fldLifeStageID, q_DatasetTreatment.fldLifeStage, 
       q_DatasetTreatment.fldDataTypeID, q_DatasetTreatment.fldDataType, 
       q_DatasetTreatment.fldGenerationID, q_DatasetTreatment.fldGeneration, 
       q_DatasetTreatment.fldEffectTypeID, q_DatasetTreatment.fldEffectType, 
       q_DatasetTreatment.fldDurationDays, q_DatasetTreatment.fldBVUnits, 
       q_DatasetTreatment.fldDescription AS fldBVUnitDescription, 
       q_DatasetTreatment.fldReportedNOEC, q_DatasetTreatment.fldReportedLOEC, 
       q_DatasetTreatment.fldTreatmentNum, q_DatasetTreatment.fldControlTypeID, 
       q_DatasetTreatment.fldControlType, q_DatasetTreatment.fldReplicateNum, 
       q_DatasetTreatment.fldPseudoReplicateNum, q_DatasetTreatment.fldNumberExposed,
       q_DatasetTreatment.fldMeasuredConcentration, 
       q_DatasetTreatment.fldNominalConcentration, q_DatasetTreatment.fldBiolVarValue
FROM q_sumTestReference 
RIGHT JOIN (q_Test 
            LEFT JOIN q_DatasetTreatment 
                   ON q_Test.fldTestsID = q_DatasetTreatment.fldTestsID) 
        ON q_sumTestReference.fldTestsID = q_Test.fldTestsID
ORDER BY q_Test.fldTestsID, q_DatasetTreatment.fldDatasetsID,
         q_DatasetTreatment.fldTreatmentNum, q_DatasetTreatment.fldReplicateNum;

我想使用该函数,因为它更简单、更清晰的代码。有什么建议么?我在this post on SQL Server user-defined functions 中看到它逐行评估。这是否意味着像我的第 4 个示例这样的 SQL 查询中的复杂解析语句是正确的?

【问题讨论】:

如果您有一个包含数字的字符串,请使用Val(your_string) 使其可按数字顺序排序。我不确定您是否需要 UDF,内置的 Val() 函数可以使查询更快。 【参考方案1】:

HansUp 的评论奏效了:

如果您有一个包含数字的字符串,请使用Val(your_string) 使其可按数字顺序排序。我不确定您是否需要 UDF,内置的 Val() 函数可以使查询更快。

这是有效的查询:

SELECT parm_TestConcatReferenceDatasetExposure.fldPurity,
       Val(Replace(Replace(Nz(Trim([fldPurity]),""),"%",""),"+","")) AS temp5, 
       Count(*) AS RcdCount
FROM parm_TestConcatReferenceDatasetExposure
GROUP BY parm_TestConcatReferenceDatasetExposure.fldPurity, 
         Val(Replace(Replace(Nz(Trim([fldPurity]),""),"%",""),"+",""))
ORDER BY Val(Replace(Replace(Nz(Trim([fldPurity]),""),"%",""),"+",""));

【讨论】:

以上是关于Access 2007 用户自定义函数过加入慢的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Access 2007 中使用 Access 2003 mde 并保留我的自定义菜单/工具栏?

R语言用户自定义函数的语法结构编写自定义统计值计算函数(使用ifelse结构计算均值和标准差等)编写自定义日期格式化(format)函数(switch函数使用不同分枝格式化日期数据)应用自定函数

R语言用户自定义函数的语法结构编写自定义统计值计算函数(使用ifelse结构计算均值和标准差等)编写自定义日期格式化(format)函数(switch函数使用不同分枝格式化日期数据)应用自定函数

MS Access 2007 重命名自定义组中的快捷方式以更改对象的名称

如何使用 MS Access 基于变量发送带有附加报告的自定义电子邮件? 2003 年或 2007 年

access 2007如何离开加入列?