Coldfusion cfloop Sql 查询极慢

Coldfusion cfloop Sql queries extremely slow

我有用户参加的测验列表和 table 跟踪他们做对的问题的数量、测验类别、分数和测验的 ID。

在 coldfusion 中,有一个 cfloop 遍历每个测验,并动态计算每个测验的平均分、最高分、最低分并显示出来。这需要永远加载,有什么方法可以优化 cfloop 吗?

原查询是这样的:

SELECT     Quizname,
    NULLIF(QuizId, '') as  QuizId,
    NULLIF(InstructorId, '') as InstructorId,
    NULLIF(Location, '') as Location,
    cast(replace(quiz_user_quiz_percentage,'%','') as decimal(5,2)) as percentage
FROM         QuizResults
where 0=0
    and year(cast(datecompleted as date))>= 2019

然后 Cfloop 通过这个查询来过滤每个 quizname、quizid 并获得平均分、最高分和最低分,如下所示:

<cfloop query="getEachQuiz" >
    <cfquery name="getStats" dbtype="query">
        SELECT 
            count(percentage) as countScore,
            max(percentage) as maxScore,
            min(percentage) as minScore,
            avg(percentage) as avgScore
        FROM data
        where Quizname= <cfqueryparam value="#getEachQuiz.Quizname#" cfsqltype="cf_sql_varchar" >
        and  QuizId= <cfqueryparam value="#getEachQuiz.QuizId#" cfsqltype="cf_sql_varchar" >
        <cfif len(getEachQuiz.InstructorId) gt 0>
            and InstructorId= <cfqueryparam value="#getEachQuiz.InstructorId#" cfsqltype="cf_sql_varchar" >
        </cfif>
        <cfif len(getEachQuiz.Location) gt 0>
            and Location=  <cfqueryparam value="#getEachQuiz.Location#" cfsqltype="cf_sql_varchar" >
        </cfif>
    </cfquery>
    <tr>
        <td>#getEachQuiz.Quizname#</td> 
        <td>#getEachQuiz.QuizId#</td>
        <td>#getStats.countScore#</td>
        <td>#numberformat(getStats.avgScore,'99.99')#%</td>
        <td>#getStats.maxScore#%</td>
        <td>#getStats.minScore#%</td>
    </tr>
</cfloop>

你是运行循环内查询的几个CF查询。你应该可以用一个代替这个。

<cfquery name="getStats" dbtype="query">
select quizname, quizid,instructorId, location
, count(percentage) as countScore
, min(percentage) as minScore
, max(percentage) as maxScore
, avg(percentage) as avgScore
from data
group by quizname, quizid,instructorId, location
</cfquery>

此外,在您的主查询中,替换

where 0=0
    and year(cast(datecompleted as date))>= 2019

where datecompleted >= '2019-01-01'

我的快速代码审查:

  • 检查您的数据库架构并确保您的列数据类型 是他们需要的。
  • 您的列是否允许 NULL?如果他们这样做,而不是插入和空字符串,使用 NULL。那么你不需要NULLIF函数。
  • cast(replace(quiz_user_quiz_percentage,'%','') as decimal(5,2)) as percentage > 看来您的数据库允许您输入“%” 该列中的字符。这是 SQL 中的特殊字符,可以 让你的生活变得困难。如果你有能力把它改成 简单的整数,我会做的。另外,在最后做任何格式化 输出。
  • where 0=0 >> 你不需要这个条件,除非你正在构建某种动态查询,并且你想确保有 WHERE 子句中至少有一项评估。
  • and year(cast(datecompleted as date))>= 2019 >> 如果可以,请避免 WHERE 比较左侧的函数。它会否定 在该列上使用任何索引,并减慢查询速度。
  • 使用Query of Query 建立结果集会占用大量资源。我发现大多数可以在 QoQ 中完成的事情更适合 SQL 本身。

我假设您不知何故在您想要过滤的年份过去了。所以传入“2019”,你可以做类似...

<cfset startDate = createDate(inputYear,1,1)> <!--- 2019-01-01 --->
<cfset endDate = dateAdd("yyyy",1,startDate)> <!--- 2020-01-01 --->

...然后在查询中使用这些变量按日期进行过滤。

注意:为了让您的生活更轻松,我将修复数据库中的 percentage 列以排除“%”字符。请参阅下面我的笔记。

<cfquery name="getEachQuiz" ...>
SELECT qr.QuizName, qr.QuizID, qr.InstructorID, qr.Location
    , count(*) as countScore
    , max(qr.percentage) as maxScore
    , min(qr.percentage) as minScore
    , avg(qr.percentage) as avgScore
FROM QuizResults qr
WHERE qr.datecompleted >= <cfqueryparam value="2019-01-01" cfsqltype="cf_sql_date">
    AND qr.datecompleted < <cfqueryparam value="2020-01-01" cfsqltype="cf_sql_date">
GROUP BY QuizName, QuizID, qr.InstructorID, qr.Location
ORDER BY QuizName, QuizID, qr.InstructorID, qr.Location
</cfquery>

然后就可以直接输出结果了

<cfoutput query="getEachQuiz">
    <tr>
        <td>#Quizname#</td> 
        <td>#QuizId#</td>
        <td>#countScore#</td>
        <td>#numberformat(avgScore,'99.99')#%</td>
        <td>#maxScore#%</td>
        <td>#minScore#%</td>
    </tr>
</cfoutput>

不过,我还建议您为 InstructorIDLocation 添加一列。您正在按查询中的那些进行分组和计数,如果您的结果中未识别出它们,您可能会丢失上下文。

另请注意,如果您在原始查询中将这些值转换为 NULL,您的结果可能不会达到您的预期。您打算如何计算同时在 'TN' 和 '' 中的讲师?

要获得日期过滤的快速替代方法,请使用日历 Table (https://github.com/shawnoden/SQL_Stuff/blob/master/sql_CreateDateDimension.sql)。您需要修改日历 Table 以适合您的数据和需求,但它们是一个很棒的工具。

<cfquery name="getEachQuiz" ...>
SELECT qr.QuizName, qr.QuizID, qr.InstructorID, qr.Location
    , count(*) as countScore
    , max(qr.percentage) as maxScore
    , min(qr.percentage) as minScore
    , avg(qr.percentage) as avgScore
FROM QuizResults qr
INNER JOIN CalendarTable ct ON qr.datecompleted = ct.theDate
    AND ct.theYear = <cfqueryparam value="#inputYear#" cfsqltype="cf_sql_integer">
GROUP BY QuizName, QuizID, qr.InstructorID, qr.Location
ORDER BY QuizName, QuizID, qr.InstructorID, qr.Location
</cfquery>

基于集合的 JOIN 通常比使用日期过滤快很多。日期可能是一个巨大的头痛。

注意:对于一次性的,这可能不是最好的方法。日历 Table 的主要好处是,它使您可以轻松访问可能必须从日期派生的常用值。函数不仅会变慢,而且还会阻止您正确使用 table 的索引。在大多数情况下,我仍然坚信几乎所有数据库都可以从日历中受益 Table.