外部結合を含む複数の結合を含むクエリでユーザー定義関数を使用するクエリが遅いのはなぜですか? この関数の理由は、文字列を数値的にソートするように変更することです。文字列を並べ替えるということは、100 < 99
. 関数は を再フォーマットし99 as 099
ます。だから、099 < 100
。他の非数値は変更されません。
問題のクエリは、結合のあるクエリに対して関数を使用しています。100 行を返すのに 27 秒かかります。関数を使用した同じクエリですが、1 つのテーブルに対して 1 秒未満かかります。結合を使用したクエリに対するクエリの SQL 置換は、1 秒未満です。結合のあるクエリに対する関数のないクエリは、1 秒未満です。プライマリ テーブルは、517 行の tblTests です。関数が操作する列は、テキスト列 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]);
これは、1 つのテーブルに対して関数を使用した高速クエリです (<1 秒)。
SELECT tblTests.fldPurity,
SortablePercent([fldPurity]) AS temp2,
Count(*) AS RcdCount
FROM tblTests
GROUP BY tblTests.fldPurity, SortablePercent([fldPurity])
ORDER BY SortablePercent([fldPurity]);
これは、結合を使用したクエリに対する関数を使用しない高速クエリです (<1 秒)
SELECT parm_TestConcatReferenceDatasetExposure.fldPurity,
Count(*) AS RcdCount
FROM parm_TestConcatReferenceDatasetExposure
GROUP BY parm_TestConcatReferenceDatasetExposure.fldPurity;
これは、1 つのテーブルに対する関数の SQL 部分近似を使用した高速クエリです (<1 秒)。
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;
よりシンプルでクリーンなコードであるため、関数を使用したいと思います。助言がありますか?行ごとに評価される SQL Server ユーザー定義関数に関するこの記事を参照してください。これは、私の 4 番目の例のような SQL クエリの複雑な解析ステートメントが正しいということですか?