CFML & SQL - 性能提升?
CFML & SQL - Performance Increase?
简要概述
好的,我已经编写了一个查询来过滤一些产品,但是,我需要尝试进一步调整这个查询以允许更多的过滤器。我已经 kinda 做到了,但是,它需要两倍的时间,我的意思是页面加载和完全呈现需要大约 5 秒以上的时间,这在我的看法。我的意思是它可以工作,但对于商业发布来说太慢了。
我假设我在下面复制的函数要慢得多但遗憾的是我不确定如何否则我能做到吗? - 我想不出一种可以编写查询的方法,该查询基本上可以消除必须使用我编写的函数的需要。
我猜,最好的方法是以某种方式将这两个查询合并在一起,再加上一个用于其他过滤器的查询,这些过滤器不适用于我当前拥有的过滤器到位? - 我不完全确定。
所以无论如何,这是一些代码:-
初始查询
<cfset row = 0>
...
<cfquery name="query" datasource="ds">
DECLARE @st TABLE (ID int, z varchar(50))
DECLARE @tc int
<cfloop array="#refineArr#" index="x">
<cfset row ++>
<cfoutput>
INSERT INTO @st VALUES ('#IDArr[row]#', '#x#')
</cfoutput>
</cfloop>
SELECT @tc = COUNT(DISTINCT ID)
FROM @st
SELECT tbl.code
FROM Table1 tbl
INNER JOIN @st T
ON T.ID = tbl.ID
AND tbl.V = T.z
INNER JOIN Table2 tbl2
ON tbl.ID = tbl2.ID
WHERE tbl.code IN (<cfqueryparam list="yes" value="#valuelist(getallcodes.code)#">)
GROUP BY tbl.code
HAVING COUNT(tbl.ID) = @tc
</cfquery>
澄清一下,这个查询工作正常,没有任何问题。这两个数组是在此查询之前生成的,IDarr 数组只是一个包含特定类别内的 all ID 的数组,然后 refineArr 数组是根据优化的内容生成的用户有输入。 - 它们都被强制为相同的长度,因此当索引不存在时,它永远不会尝试为一个数组获取索引 'x' 。
And 正如您可能已经猜到的那样,行 'getallcodes.code' 只获取 应该 的所有产品的所有代码在某个 cat/sub-cat 下显示,等等
下一部分
好的,在下一部分中,由于 种类 过滤器的不同,我需要允许出现范围、日期和测量值。然而,暂时忽略测量部分,事实证明存储在数据库中的数据都是为了测量而搞砸的。
我最初尝试创建一个函数,它可以工作,但速度不是那么快,要 运行 调用该函数,我最初是这样写的:
<cfset ranges = ['Months','Period','SMonths']>
<cfset tc = 0>
<cfset tempQ = query>
...
<cfloop array="#ranges#" index="i">
<cfset tc ++>
<cfif i contains 'month' or i contains 'period'>
<cfset tempQ = rangesFnc(tempQ, Int(tc), ToString(i))>
</cfif>
</cfloop>
函数本身
记住,我还没有写完这个函数,我知道它仍然有点乱,至于数组,老实说,它们只是一些对工作至关重要的变量的集合出额外的改进。
<cffunction name="rangesFnc">
<cfargument name="q" type="query" required="true">
<cfargument name="i" type="numeric" required="true">
<cfargument name="s" type="string" required="true">
<cfset minArr = '#minf#,
#minh#,
#minh2#,
#minm#,
#mins#'>
<cfset maxArr = '#maxf#,
#maxh#,
#maxh2#,
#maxm#,
#maxs#'>
<cfset min = listGetAt(minArr, i)>
<cfset max = listGetAt(maxArr, i)>
<cfquery name="tempq" datasource="ds">
WITH q AS (
SELECT DISTINCT tbl.code,
CASE
WHEN tbl.V = 'January' THEN 1
WHEN tbl.V = 'February' THEN 2
WHEN tbl.V = 'March' THEN 3
WHEN tbl.V = 'April' THEN 4
WHEN tbl.V = 'May' THEN 5
WHEN tbl.V = 'June' THEN 6
WHEN tbl.V = 'July' THEN 7
WHEN tbl.V = 'August' THEN 8
WHEN tbl.V = 'September' THEN 9
WHEN tbl.V = 'October' THEN 10
WHEN tbl.V = 'November' THEN 11
WHEN tbl.V = 'December' THEN 12
ELSE 0
END AS xdate
FROM Table1 tbl
INNER JOIN Table2 tbl2
ON tbl.ID = tbl2.ID
WHERE tbl2.name LIKE <cfqueryparam value="%#s#%">
AND tbl.code IN (<cfqueryparam value="#valueList(q.code)#" list="yes">)
)
SELECT code
FROM q
WHERE xdate <= <cfqueryparam value="#Int(max)#">
AND xdate >= <cfqueryparam value="#Int(min)#">
</cfquery>
<cfreturn tempq>
</cffunction>
终于
对于任何突出显示语法的问题以及它有点混乱这一事实,我深表歉意。除了我必须遗漏很多信息这一事实之外。源代码本身看起来 非常 不同,但这是故意的,我只是在这里复制一个 dummy 示例,因为这是我负责的一部分确保我不会暴露太多关于我正在处理的网页结构的信息,我知道这很愚蠢,但规则就是规则。
我也只添加了像'Int()'或'ToString()'这样的函数来确保它按预期工作,仅此而已,仅此而已,实际上什至可能不需要。
这更像是评论,但太长了。
我真的不喜欢 <cfloop>
加载临时 table。您的 SQL 服务器将无法缓存查询,并且每次都必须重新解析它。 #valuelist(getallcodes.code)#
我也有类似的问题
我宁愿多值参数通过永不改变的方式提取数据。
<!--- Generate XML of data --->
<cfquery>
DECLARE @st TABLE (ID int, z varchar(50))
DECLARE @tc int
DECLARE @xmlIDArr xml = <cfqueryparam value="#xmlIDarr#">
INSERT INTO @st
SELECT tbl.Col.value('id', 'int'), tbl.Col.value('z', 'varchar(50)')
FROM @xmlIDArr.nodes('/data') tbl(Col)S
SELECT @tc = COUNT(DISTINCT ID)
FROM @st
SELECT tbl.code
FROM Table1 tbl
INNER JOIN @st T
ON T.ID = tbl.ID
AND tbl.V = T.z
</cfquery>
再举一个例子:Select IN on more than 2100 values
这里有一些代码审查要点(这是一个答案,因为它的格式比注释更漂亮):
初始查询
1) 将行变量移动到查询中并确定其范围。 <cfset row = 0>
之前的查询都是自找麻烦。
2) 将 INSERT
移出循环。 SQL 2008 可以插入多个值。无需多次访问数据库。
INSERT INTO @st
VALUES
<cfloop array="#refineArr#" index="x">
<cfset row++>
<cfoutput>('#IDArr[row]#','#x#')</cfoutput>
<cfif row LT ArrayLen(refineArr)>,</cfif>
</cfloop>
3) 我不知道 cf 页面的其余部分是什么样子,但我的直觉是 cfquery 可以重构为单个 SELECT 而无需临时 @st table 变量。
下一篇
在没有看到更多代码的情况下,我不确定如何对其进行最佳优化。是否可以同时有多个过滤器(即按Months and Periods
过滤?
1) 再次确定范围。这里有很多松散的变量。有些可能没有必要。
2)这里需要循环吗?再次没有更多代码,我不知道它的目的是什么。
3) 在您的循环中,i contains month or i contains period
是对 i
中值的大量额外解析,它可能并不总是 return 所期望的。正如所写,它也会 return SMonths
。
4) 我假设 tempQ=query
只是第一部分查询的副本,对吗?有这个必要吗?
函数本身
1) minArr
和 maxArr
>> 数组会被传递到函数中吗?还是会传入 minf, minh....
个变量?不应从函数外部访问它们。您应该能够 运行 完全独立地使用该函数(并且它可能应该是它自己的 CFC)。
2) 它们被命名为数组,但它们包含列表。名称应符合其意图。
3) 将变量重命名为 tempq
并将其设置为之前的查询,然后在函数内创建另一个名为 tempq
的查询。如果在页面下方使用,这可能会混淆您正在尝试访问的 tempq
。
4) Table1.V
的数据类型是什么?您可以只使用 SQL 函数来 return 日期名称的数字吗?
5) 在您的 WHERE
子句中,您使用 tbl2.name LIKE....
。对于 Table2
,这应该向上移动到 INNER JOIN
中。 `INNER JOIN Table2 tbl2 ON tbl.ID = tbl2.ID AND tbl2.name LIKE ()
6) 我认为您在此查询中不需要 CTE。您构建 xdate
只是为了通过它过滤 CTE。将整个块添加到 WHERE
子句。
终于
我在专有或更严格的环境中工作了近 2 年,所以我完全理解保护应用程序源的必要性。这可能令人沮丧,但有时这是必要的邪恶。我在这里说得比我预想的要多一些,但我希望它真的能有所帮助。正如我之前所说,在对您的应用程序的意图了解不多的情况下,我只能希望我已经引导您走上了正确的道路,并且我希望我的建议至少对性能有轻微的改进。
祝你好运。
简要概述
好的,我已经编写了一个查询来过滤一些产品,但是,我需要尝试进一步调整这个查询以允许更多的过滤器。我已经 kinda 做到了,但是,它需要两倍的时间,我的意思是页面加载和完全呈现需要大约 5 秒以上的时间,这在我的看法。我的意思是它可以工作,但对于商业发布来说太慢了。
我假设我在下面复制的函数要慢得多但遗憾的是我不确定如何否则我能做到吗? - 我想不出一种可以编写查询的方法,该查询基本上可以消除必须使用我编写的函数的需要。
我猜,最好的方法是以某种方式将这两个查询合并在一起,再加上一个用于其他过滤器的查询,这些过滤器不适用于我当前拥有的过滤器到位? - 我不完全确定。
所以无论如何,这是一些代码:-
初始查询
<cfset row = 0>
...
<cfquery name="query" datasource="ds">
DECLARE @st TABLE (ID int, z varchar(50))
DECLARE @tc int
<cfloop array="#refineArr#" index="x">
<cfset row ++>
<cfoutput>
INSERT INTO @st VALUES ('#IDArr[row]#', '#x#')
</cfoutput>
</cfloop>
SELECT @tc = COUNT(DISTINCT ID)
FROM @st
SELECT tbl.code
FROM Table1 tbl
INNER JOIN @st T
ON T.ID = tbl.ID
AND tbl.V = T.z
INNER JOIN Table2 tbl2
ON tbl.ID = tbl2.ID
WHERE tbl.code IN (<cfqueryparam list="yes" value="#valuelist(getallcodes.code)#">)
GROUP BY tbl.code
HAVING COUNT(tbl.ID) = @tc
</cfquery>
澄清一下,这个查询工作正常,没有任何问题。这两个数组是在此查询之前生成的,IDarr 数组只是一个包含特定类别内的 all ID 的数组,然后 refineArr 数组是根据优化的内容生成的用户有输入。 - 它们都被强制为相同的长度,因此当索引不存在时,它永远不会尝试为一个数组获取索引 'x' 。
And 正如您可能已经猜到的那样,行 'getallcodes.code' 只获取 应该 的所有产品的所有代码在某个 cat/sub-cat 下显示,等等
下一部分
好的,在下一部分中,由于 种类 过滤器的不同,我需要允许出现范围、日期和测量值。然而,暂时忽略测量部分,事实证明存储在数据库中的数据都是为了测量而搞砸的。
我最初尝试创建一个函数,它可以工作,但速度不是那么快,要 运行 调用该函数,我最初是这样写的:
<cfset ranges = ['Months','Period','SMonths']>
<cfset tc = 0>
<cfset tempQ = query>
...
<cfloop array="#ranges#" index="i">
<cfset tc ++>
<cfif i contains 'month' or i contains 'period'>
<cfset tempQ = rangesFnc(tempQ, Int(tc), ToString(i))>
</cfif>
</cfloop>
函数本身
记住,我还没有写完这个函数,我知道它仍然有点乱,至于数组,老实说,它们只是一些对工作至关重要的变量的集合出额外的改进。
<cffunction name="rangesFnc">
<cfargument name="q" type="query" required="true">
<cfargument name="i" type="numeric" required="true">
<cfargument name="s" type="string" required="true">
<cfset minArr = '#minf#,
#minh#,
#minh2#,
#minm#,
#mins#'>
<cfset maxArr = '#maxf#,
#maxh#,
#maxh2#,
#maxm#,
#maxs#'>
<cfset min = listGetAt(minArr, i)>
<cfset max = listGetAt(maxArr, i)>
<cfquery name="tempq" datasource="ds">
WITH q AS (
SELECT DISTINCT tbl.code,
CASE
WHEN tbl.V = 'January' THEN 1
WHEN tbl.V = 'February' THEN 2
WHEN tbl.V = 'March' THEN 3
WHEN tbl.V = 'April' THEN 4
WHEN tbl.V = 'May' THEN 5
WHEN tbl.V = 'June' THEN 6
WHEN tbl.V = 'July' THEN 7
WHEN tbl.V = 'August' THEN 8
WHEN tbl.V = 'September' THEN 9
WHEN tbl.V = 'October' THEN 10
WHEN tbl.V = 'November' THEN 11
WHEN tbl.V = 'December' THEN 12
ELSE 0
END AS xdate
FROM Table1 tbl
INNER JOIN Table2 tbl2
ON tbl.ID = tbl2.ID
WHERE tbl2.name LIKE <cfqueryparam value="%#s#%">
AND tbl.code IN (<cfqueryparam value="#valueList(q.code)#" list="yes">)
)
SELECT code
FROM q
WHERE xdate <= <cfqueryparam value="#Int(max)#">
AND xdate >= <cfqueryparam value="#Int(min)#">
</cfquery>
<cfreturn tempq>
</cffunction>
终于
对于任何突出显示语法的问题以及它有点混乱这一事实,我深表歉意。除了我必须遗漏很多信息这一事实之外。源代码本身看起来 非常 不同,但这是故意的,我只是在这里复制一个 dummy 示例,因为这是我负责的一部分确保我不会暴露太多关于我正在处理的网页结构的信息,我知道这很愚蠢,但规则就是规则。
我也只添加了像'Int()'或'ToString()'这样的函数来确保它按预期工作,仅此而已,仅此而已,实际上什至可能不需要。
这更像是评论,但太长了。
我真的不喜欢 <cfloop>
加载临时 table。您的 SQL 服务器将无法缓存查询,并且每次都必须重新解析它。 #valuelist(getallcodes.code)#
我宁愿多值参数通过永不改变的方式提取数据。
<!--- Generate XML of data --->
<cfquery>
DECLARE @st TABLE (ID int, z varchar(50))
DECLARE @tc int
DECLARE @xmlIDArr xml = <cfqueryparam value="#xmlIDarr#">
INSERT INTO @st
SELECT tbl.Col.value('id', 'int'), tbl.Col.value('z', 'varchar(50)')
FROM @xmlIDArr.nodes('/data') tbl(Col)S
SELECT @tc = COUNT(DISTINCT ID)
FROM @st
SELECT tbl.code
FROM Table1 tbl
INNER JOIN @st T
ON T.ID = tbl.ID
AND tbl.V = T.z
</cfquery>
再举一个例子:Select IN on more than 2100 values
这里有一些代码审查要点(这是一个答案,因为它的格式比注释更漂亮):
初始查询
1) 将行变量移动到查询中并确定其范围。 <cfset row = 0>
之前的查询都是自找麻烦。
2) 将 INSERT
移出循环。 SQL 2008 可以插入多个值。无需多次访问数据库。
INSERT INTO @st
VALUES
<cfloop array="#refineArr#" index="x">
<cfset row++>
<cfoutput>('#IDArr[row]#','#x#')</cfoutput>
<cfif row LT ArrayLen(refineArr)>,</cfif>
</cfloop>
3) 我不知道 cf 页面的其余部分是什么样子,但我的直觉是 cfquery 可以重构为单个 SELECT 而无需临时 @st table 变量。
下一篇
在没有看到更多代码的情况下,我不确定如何对其进行最佳优化。是否可以同时有多个过滤器(即按Months and Periods
过滤?
1) 再次确定范围。这里有很多松散的变量。有些可能没有必要。
2)这里需要循环吗?再次没有更多代码,我不知道它的目的是什么。
3) 在您的循环中,i contains month or i contains period
是对 i
中值的大量额外解析,它可能并不总是 return 所期望的。正如所写,它也会 return SMonths
。
4) 我假设 tempQ=query
只是第一部分查询的副本,对吗?有这个必要吗?
函数本身
1) minArr
和 maxArr
>> 数组会被传递到函数中吗?还是会传入 minf, minh....
个变量?不应从函数外部访问它们。您应该能够 运行 完全独立地使用该函数(并且它可能应该是它自己的 CFC)。
2) 它们被命名为数组,但它们包含列表。名称应符合其意图。
3) 将变量重命名为 tempq
并将其设置为之前的查询,然后在函数内创建另一个名为 tempq
的查询。如果在页面下方使用,这可能会混淆您正在尝试访问的 tempq
。
4) Table1.V
的数据类型是什么?您可以只使用 SQL 函数来 return 日期名称的数字吗?
5) 在您的 WHERE
子句中,您使用 tbl2.name LIKE....
。对于 Table2
,这应该向上移动到 INNER JOIN
中。 `INNER JOIN Table2 tbl2 ON tbl.ID = tbl2.ID AND tbl2.name LIKE ()
6) 我认为您在此查询中不需要 CTE。您构建 xdate
只是为了通过它过滤 CTE。将整个块添加到 WHERE
子句。
终于
我在专有或更严格的环境中工作了近 2 年,所以我完全理解保护应用程序源的必要性。这可能令人沮丧,但有时这是必要的邪恶。我在这里说得比我预想的要多一些,但我希望它真的能有所帮助。正如我之前所说,在对您的应用程序的意图了解不多的情况下,我只能希望我已经引导您走上了正确的道路,并且我希望我的建议至少对性能有轻微的改进。
祝你好运。