简体   繁体   English

CFML和SQL - 性能提升?

[英]CFML & SQL - Performance Increase?

Brief Overview 简要概述

Okay, so I've written a query that will filter some products, however, I need to try and further tweak this query to allow for even more filters. 好的,所以我写了一个会过滤一些产品的查询,但是,我需要尝试进一步调整这个查询以允许更多的过滤器。 I've kinda done that, however, it takes twice as long, I mean it takes about 5+ seconds for the page to load and be rendered completely, which isn't good enough in my opinion. 有点做了,但是,它需要两倍的时间,我的意思是页面加载和完全渲染大约需要5秒以上,这在我看来还不够好。 I mean it works, but it's just too slow for commercial release. 我的意思是它有效,但它对于商业发布来说太慢了。

I'm assuming it's so much slower with the function I've copied in below but sadly I'm not sure how else I could do this? 我假设它在下面复制的功能要慢得多, 但遗憾的是我不确定我怎么能做到这一点? - I can't think of a way where I could write a query that would essentially eliminate the need to having to use the function I've written. - 我想不出一种方法可以编写一个基本上不需要使用我编写的函数的查询。

I'm guessing , the best way would be to somehow merge the two queries together, plus another one for other filters that don't work for the current one I have in place? 我猜 ,最好的方法是以某种方式将两个查询合并在一起,另外一个用于其他过滤器,这些过滤器不适用于我现有的那个? - I'm not totally sure. - 我不完全确定。

So anyway, here's some code :- 所以无论如何,这里有一些代码 : -


The Initial Query 初始查询

<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>

Just to clarify, this query works just fine, no issues at all. 只是为了澄清,这个查询工作正常,没有任何问题。 The two arrays are just generated prior to this query, the IDarr array is just an array of all ID's that are within a certain category, then the refineArr array is just generated depending on what refinements the user has input. 这两个数组只是在此查询之前生成,IDarr数组只是某个类别中所有 ID的数组,然后根据用户输入的优化生成refineArr数组。 - They're both forced to be the same length so it never tries to get index 'x' for one array when that index doesn't exist. - 它们都被强制为相同的长度,因此当该索引不存在时,它永远不会尝试为一个数组获取索引'x'。

And as you may have guessed, the line 'getallcodes.code', that just gets all codes for all products that should show under a certain cat/sub-cat, etc. 正如你可能已经猜到了,行了“getallcodes.code”,只是得到所有代码应该有一定的猫/子猫下显示所有产品,等等。


Next Part 下一部分

Okay, so with this next part, due to the different kinds of filters, I need to allow for ranges to occur, dates and measurements too. 好的,对于下一部分,由于滤波器种类不同,我需要允许范围发生,日期和测量。 However, ignore the measurements part for now, turns out that the data stored in the database is all screwed up for the measurements. 但是,暂时忽略测量部分,结果是存储在数据库中的数据全部搞砸了,无法进行测量。

I originally tried to create a function, it works, but it's not that fast, and to run call the function, I originally wrote this: 我最初尝试创建一个函数,它工作,但它不是那么快,并且运行调用函数,我最初写这个:

<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>

The Function Itself 功能本身

Just keep in mind, I haven't yet finished writing this function, I know it's still a little bit messy, and as for the arrays, they're honestly just a collection of some variables that are critical for working out additional refinements. 请记住,我还没有写完这个函数,我知道它仍然有点凌乱,至于数组,它们实际上只是一些变量的集合,这些变量对于进行其他改进至关重要。

<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>

Finally 最后

I'd like to apologies for any syntax highlighting issues, as well as the fact that it's a bit messy. 我想为任何语法突出问题道歉,以及它有点混乱的事实。 In addition to the fact that I've got to leave a lot of info out. 除了我必须留下很多信息的事实。 The source code itself looks very different, but that's deliberate, I'm just copying a dummy example here, as it's a part of my responsibility to ensure that I don't expose too much information about the structure of the web page I'm working on, silly I know, but rules are rules. 源代码本身看起来非常不同,但这是故意的,我只是在这里复制一个虚拟示例,因为我的责任是确保我不会暴露太多关于网页结构的信息我是我知道,工作,傻,但规则是规则。

I've also only added functions like 'Int()' or 'ToString()' to ensure that it works as expected, that's it, nothing more, nothing less, it's probably not even needed realistically speaking. 我也只添加了'Int()'或'ToString()'之类的函数来确保它按预期工作,就是这样,仅此而已,它可能甚至不需要现实地说。

This is more of a comment, but it is too long. 这更像是一个评论,但它太长了。

I really don't like the <cfloop> to load a temp table. 我真的不喜欢<cfloop>来加载临时表。 Your SQL Server will not be able to cache the query and it will have to reparse it every time. 您的SQL Server将无法缓存查询,并且每次都必须重新解析它。 I have similar issues with the #valuelist(getallcodes.code)# 我和#valuelist(getallcodes.code)#有类似的问题#valuelist(getallcodes.code)#

I would rather that the multi value parameters have there data pulled via something that never changes. 我宁愿多值参数通过永不改变的东西拉出数据。

<!--- 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>

For an additional example: Select IN on more than 2100 values 有关其他示例: 在超过2100个值上选择IN

Some code review points here (and it's an answer because that formats a bit prettier than a comment): 这里有一些代码评论点(这是一个答案,因为格式比注释更漂亮):

Initial Query 初始查询

1) Move the row variable inside the query and scope it. 1)在查询中移动行变量并对其进行范围化。 <cfset row = 0> before the query is just asking for trouble. <cfset row = 0>在查询之前只是在寻找麻烦。

2) Move your INSERT outside of the loop. 2)将INSERT循环。 SQL 2008 can INSERT multiple values. SQL 2008可以插入多个值。 No need to hit the database multiple times. 无需多次访问数据库。

INSERT INTO @st 
VALUES
<cfloop array="#refineArr#" index="x">
    <cfset row++>
    <cfoutput>('#IDArr[row]#','#x#')</cfoutput>
    <cfif row LT ArrayLen(refineArr)>,</cfif>
</cfloop>

3) I don't know what the rest of the cf page looks like, but my gut feeling is that the cfquery can be refactored to be a single SELECT without the need for the temp @st table variable. 3)我不知道cf页面的其余部分是什么样的,但我的直觉是cfquery可以重构为单个SELECT而不需要temp @st表变量。

Next Part 下一部分

Without seeing more of the code, I'm not sure how this can best be optimized. 没有看到更多的代码,我不确定如何最好地优化它。 Can there be multiple filters at the same time (ie filtering by Months and Periods ? 可以同时有多个过滤器(即Months and Periods过滤?

1) Scoping again. 1)再次确定范围。 There are lots of loose variables here. 这里有很多松散的变量。 Some may not be necessary. 有些可能没有必要。

2) Is the loop necessary here? 2)这里需要循环吗? Again without more code, I don't know what its purpose is. 再没有更多的代码,我不知道它的目的是什么。

3) In your loop, i contains month or i contains period is a lot of extra parsing of the value in i , and it may not always return what's expected. 3)在你的循环中, i contains month or i contains period是对i中的值进行大量额外解析,并且它可能并不总是返回预期的值。 As written, it will return SMonths also. 如上所述,它也将返回SMonths

4) Im assuming tempQ=query is just a copy of the First Part query, correct? 4)我假设tempQ=query只是第一部分查询的副本,对吗? Is this needed? 这需要吗?

The Function Itself 功能本身

1) minArr and maxArr >> Will the arrays be passed into the function? 1) minArrmaxArr >>数组是否会传递给函数? Or will the minf, minh.... variables be passed in? 或者minf, minh....变量会被传入吗? They shouldn't be accessed from outside the function. 不应从函数外部访问它们。 You should be able to run the function completely by itself (and it should probably be its own CFC). 您应该能够完全自己运行该功能(它应该是它自己的CFC)。

2) They are named like they are arrays, but they contain lists. 2)它们被命名为数组,但它们包含列表。 The names should be consistent with their intent. 名称应与其意图一致。

3) You rename a variable to tempq and set it to the query from earlier, then inside the function create another query named tempq . 3)您将变量重命名为tempq并将其设置为之前的查询,然后在函数内部创建另一个名为tempq查询。 This could get confusing as to which tempq you are trying to access if used further down the page. 这可能会造成混淆哪个tempq如果再沿用下来的页面,您试图访问。

4) What is the datatype of Table1.V ? 4) Table1.V的数据类型是Table1.V Can you just use a SQL function to return a number for the date name? 你能用一个SQL函数来返回日期名称的数字吗?

5) In your WHERE clause, you use tbl2.name LIKE.... . 5)在WHERE子句中,使用tbl2.name LIKE.... This should be moved up into the INNER JOIN for Table2 . 这应该移到Table2INNER JOIN中。 `INNER JOIN Table2 tbl2 ON tbl.ID = tbl2.ID AND tbl2.name LIKE () `INNER JOIN Table2 tbl2 ON tbl.ID = tbl2.ID AND tbl2.name LIKE()

6) I don't think you need the CTE in this query. 6)我不认为你在这个查询中需要CTE。 You're building out xdate only to filter the CTE by it. 你只需构建xdate来过滤CTE。 Add the entire block to the WHERE clause. 将整个块添加到WHERE子句中。

Finally 最后

I've spent almost 2 decades working in proprietary or stricter environments, so I totally understand the need to protect the application source. 我花了将近20年的时间在专有或更严格的环境中工作,所以我完全理解保护应用程序源的必要性。 It can be frustrating, but it's a necessary evil sometimes. 它可能令人沮丧,但它有时是一种必要的邪恶。 I got a bit more wordy here than I intended, but I hope it actually helps. 我在这里比我想要的更加罗嗦,但我希望它确实有帮助。 As I said earlier, without knowing a little more about the intent of your application, I can only hope that I've steered you down the right path, and I hope my suggestions have made at least a slight improvement in performance. 正如我之前所说,在不了解应用程序的意图的情况下,我只能希望我已经引导您走上正确的道路,我希望我的建议至少在性能上有所改善。

Good luck. 祝好运。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM