ColdFusion查询太慢了

时间:2018-02-14 19:36:59

标签: mysql sql coldfusion

我在cfloop中有查询,这使得进程非常慢。有没有办法让这个查询更快?

>>> sysconfig.get_config_var('EXT_SUFFIX')
'.cpython-32mu.so'
>>> sysconfig.get_config_var('SOABI')
'cpython-32mu'

cfoutput就像一个循环。

5 个答案:

答案 0 :(得分:6)

正如其他人所说,你应该摆脱循环并使用连接。查看内部循环,代码检索每个CheckRegisterID的最早和最晚日期。而不是使用LIMIT,使用aggregate functions,如MIN和MAX以及GROUP BY CheckRegisterID。然后将结果包装在derived query中,以便将结果连接回CheckRegister ON id。

原始查询中的某些列没有作用域,所以我做了一些猜测。还有改进的余地,但类似的东西足以让你开始。

-- select only needed columns
SELECT cr.CheckRegisterID, ... other columns
FROM CheckRegister cr 
       INNER JOIN ExpenseType ex ON ex.ExpenseTypeID=cr.ExpenseTypeID 
       INNER JOIN Vendors v ON v.VendorID = cr.VendorID
       LEFT JOIN 
       (
            SELECT CheckRegisterID
              , MIN(TenantTransactionDate) AS MinDate
              , MAX(TenantTransactionDate) AS MaxDate
            FROM  TenantTransactions
            GROUP BY CheckRegisterID
       ) tt ON tt.CheckRegisterID = cr.CheckRegisterID

WHERE cr.PropertyID = 10

我强烈建议您阅读JOIN,因为它们对任何网络应用程序,即IMO都至关重要。

答案 1 :(得分:5)

您应该在一个查询中获取所有数据,然后使用该数据输出您想要的内容。与数据库的多个连接几乎总是比在一次旅行中获取数据并使用它更加耗费资源。要获得结果:

SQL Fiddle

初始架构设置

CREATE TABLE CheckRegister ( checkRegisterID int, PropertyID int, VendorID int, ExpenseTypeID int ) ;
CREATE TABLE ExpenseType ( ExpenseTypeID int ) ;
CREATE TABLE Vendors ( VendorID int ) ;
CREATE TABLE TenantTransactions ( checkRegisterID int, TenantTransactionDate date, note varchar(20) );

INSERT INTO CheckRegister ( checkRegisterID, PropertyID, VendorID, ExpenseTypeID )
VALUES (1,10,1,1),(1,10,1,1),(1,10,2,1),(1,10,1,2),(1,5,1,1),(2,10,1,1),(2,5,1,1) 
;

INSERT INTO ExpenseType ( ExpenseTypeID ) VALUES (1), (2) ;
INSERT INTO Vendors ( VendorID ) VALUES (1), (2) ;

INSERT INTO TenantTransactions ( checkRegisterID, TenantTransactionDate, note )
VALUES 
    (1,'2018-01-01','start')
  , (1,'2018-01-02','another')
  , (1,'2018-01-03','another')
  , (1,'2018-01-04','stop')
  , (2,'2017-01-01','start')
  , (2,'2017-01-02','another')
  , (2,'2017-01-03','another')
  , (2,'2017-01-04','stop')
 ;

主要查询

SELECT cr.*
  , max(tt.TenantTransactionDate) AS startDate
  , min(tt.TenantTransactionDate) AS endDate
FROM CheckRegister cr 
INNER JOIN ExpenseType et ON cr.ExpenseTypeID = et.ExpenseTypeID
INNER JOIN Vendors v ON cr.vendorID = v.VendorID 
LEFT OUTER JOIN TenantTransactions tt ON cr.checkRegisterID = tt.CheckRegisterID
WHERE cr.PropertyID = 10
GROUP BY cr.CheckRegisterID, cr.PropertyID, cr.VendorID, cr.ExpenseTypeID

<强> Results

| checkRegisterID | PropertyID | VendorID | ExpenseTypeID |  startDate |    endDate |
|-----------------|------------|----------|---------------|------------|------------|
|               1 |         10 |        1 |             1 | 2018-01-04 | 2018-01-01 |
|               1 |         10 |        1 |             2 | 2018-01-04 | 2018-01-01 |
|               1 |         10 |        2 |             1 | 2018-01-04 | 2018-01-01 |
|               2 |         10 |        1 |             1 | 2017-01-04 | 2017-01-01 |

我只添加了2个检查寄存器,但CheckRegisterID 1有2个供应商和2个供应商1的费用类型。这看起来像查询中的重复数据。如果您的数据没有以这种方式设置,您将不必在最终查询中担心它。

使用正确的JOIN语法获取所需的相关数据。然后,您可以汇总该数据以获取fromDatetoDate。如果您的数据更复杂,您可能需要查看窗口函数。 https://dev.mysql.com/doc/refman/8.0/en/window-functions.html

我不知道你的最终输出是什么样的,但上面的查询一次性为你提供了所有的查询数据。这些数据的每一行都应该为您提供输出所需的内容,所以现在您只需要一个查询来循环。

答案 2 :(得分:3)

自从我进行任何ColdFusion开发以来已经很长时间了,但一个常见的经验法则是不在循环中调用查询。根据你正在做的事情,循环可以被认为是RBAR(通过痛苦的行排)操作。

您实质上是定义一个查询并循环遍历每条记录。对于每条记录,您正在执行三个额外的查询,即另外三个数据库网络调用每条记录。我看到它的方式,你有几个选择:

  1. 重写您的第一个查询,以便在每个查询中包含所需的数据 记录检查。
  2. 保留您的第一个查询,并创建在用户与记录交互时提供更多信息并以异步方式执行的功能。类似于&#34;显示信用日期&#34;链接出去并按需获取数据。
  3. 将循环中的查询合并为一个查询而不是两个getTenantTransaction...,并查看性能是否有所提高。这会将RBAR数据库调用从三个减少到两个。

答案 3 :(得分:2)

您始终希望避免在循环中进行查询。无论何时查询数据库,都有往返(从服务器到数据库,从数据库返回到服务器),这本质上很慢。

一般方法是通过尽可能少的语句查询所有必需的信息来批量数据。在一个语句中加入所有内容将是理想的,但这显然取决于您的表格方案。如果您无法仅使用SQL解决问题,则可以像这样转换查询:

GetCheckRegister...

(no loop)

<cfquery name="GetVendorName" datasource="rent">
    SELECT * FROM Vendors WHERE VendorID IN (#valueList(GetCheckRegister.VendorID)#)
</cfquery>

<cfquery name="getTenantTransactionDateFrom" dataSource="rent">
    Select TenantTransactionDate as fromDate From TenantTransactions
    Where CheckRegisterID IN (#valueList(GetCheckRegister.CheckRegisterID)#)
</cfquery>

etc.

valueList(query.column)返回指定column值的逗号分隔列表。然后,此列表与MySQL的IN (list)选择器一起使用,以检索属于所有列出值的所有记录。

现在,您只需对循环中的每个语句进行一次查询(总共4次查询,而不是GetCheckRegister中4次记录的数量)。但是所有记录都聚集在一起,所以你需要相应地匹配它们。为此,我们可以使用ColdFusion的Query of Queries (QoQ),它允许您查询已检索的数据。由于检索到的数据在内存中,因此访问它们很快。

GetCheckRegister, GetVendorName, getTenantTransactionDateFrom, getTenantTransactionDateTo etc.

<CFOUTPUT query="GetCheckRegister">

    <!--- query of queries --->
    <cfquery name="GetVendorNameSingle" dbType="query">
        SELECT * FROM [GetVendorName] WHERE VendorID = #GetCheckRegister.VendorID#
    </cfquery>

    etc.

</CFOUTPUT>

您基本上将实际查询移出循环,而是使用QoQ查询循环中真实查询的结果。

无论如何,请通过在MySQL中对其进行分析来确保您的实际查询速度很快。 使用索引!

答案 4 :(得分:0)

如果出现以下情况,使用主查询和循环来处理数据可能会更快:

  • 将SELECT与仅需要的特定字段一起使用,以避免获取这么多列(而不是SELECT *),除非您使用所有字段:

import Tkinter as tk root = tk.Tk() root.attributes('-type', 'dialog') a = tk.Frame(master=root) root.title(__file__) a.mainloop()

  • 在循环中使用较少的子查询,尝试将表连接到主查询。例如,在主查询中使用Vendors表(如果它可以加入此表)

SELECT VendorID, CheckRegisterId, ... FROM CheckRegister, ExpenseType ...

  • 最后,您可以估算流程的时间并检测性能问题:
    • ROWS =主查询中结果的行数
    • TIME_V =使用有效VendorId获取SELECT VendorID, CheckRegisterId, VendorName ... FROM CheckRegister, ExpenseType, Vendors ...结果的时间(毫秒)
    • TIME_TD1 =使用有效CheckRegisterID获取GetVendorName结果的时间(毫秒)
    • TIME_TD2 =使用有效CheckRegisterID获取getTenantTransactionDateFrom结果的时间(毫秒)
  • 然后,您可以使用getTenantTransactionDateTo计算结果时间。
  • 例如,如果ROWS = 10000,TIME_V = 30,TIME_TD1 = 15,TIME_TD2 = 15:TOTAL = ROWS * (TIME_V+ TIME_TD1 + TIME_TD2)

因此,对于10000行,一个毫秒的循环会导致10秒加入到该过程中。

如果主查询有很多结果行,则需要最小化循环中每个元素的查询时间。每个毫秒都会影响循环的性能。因此,您需要确保为循环中的每个查询过滤的每个字段都有正确的索引。