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>
不过,我还建议您为 InstructorID
和 Location
添加一列。您正在按查询中的那些进行分组和计数,如果您的结果中未识别出它们,您可能会丢失上下文。
另请注意,如果您在原始查询中将这些值转换为 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.
我有用户参加的测验列表和 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>
不过,我还建议您为 InstructorID
和 Location
添加一列。您正在按查询中的那些进行分组和计数,如果您的结果中未识别出它们,您可能会丢失上下文。
另请注意,如果您在原始查询中将这些值转换为 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.