【问题标题】:Classic ASP - When to close recordset经典 ASP - 何时关闭记录集
【发布时间】:2012-07-08 16:21:40
【问题描述】:

我想知道,在我的情况下,以下哪个示例最适合关闭记录集对象?

1)

这会关闭循环内的对象,但在下一次移动时会打开一个新对象。如果有 1000 条记录,这将打开一个对象 1000 次并关闭它 1000 次。这是我通常会做的:

SQL = " ... "
Set rs1 = conn.Execute(SQL)
While NOT rs1.EOF

    SQL = " ... "
    Set rs2 = conn.Execute(SQL)
    If NOT rs2.EOF Then
        Response.Write ( ... )
    End If
    rs2.Close : set rs2 = Nothing

rs1.MoveNext
Wend
rs1.Close : Set rs1 = Nothing

2)

这个例子是我想知道的。将对象闭包 (rs2.close) 保存到循环完成后,会提高还是降低性能?如果有 1000 条记录,这将打开 1000 个对象,但只会关闭一次:

SQL = " ... "
Set rs1 = conn.Execute(SQL)
While NOT rs1.EOF

    SQL = " ... "
    Set rs2 = conn.Execute(SQL)
    If NOT rs2.EOF Then
        Response.Write ( ... )
    End If

rs1.MoveNext
Wend
rs1.Close : Set rs1 = Nothing
rs2.Close : set rs2 = Nothing

我希望我已经对自己进行了充分的解释并且不会太愚蠢。

更新

对于那些认为可以修改我的查询以避免 N+1 问题(第二个查询)的人,这里是:

这是一个在线照片库。我有两张桌子; “照片搜索”和“照片”。第一个,“photoSearch”,只有几列,包含照片的所有可搜索数据,例如“photoID”、“headline”、“caption”、“people”、“dateCaptured”和“keywords”。它在(标题、标题、人物、关键字)上有一个多列全文索引。第二个表“照片”包含所有照片数据;高度、宽度、版权、标题、ID、日期等等。两者都有 500K+ 行,标题和标题字段有时会返回 2000+ 个字符。

这大概是查询现在的样子: (注意事项:我不能将连接与全文搜索一起使用,因此关键字存储在一列中 - 在“非规范化”表中。此外,这种伪代码作为我的应用程序代码在其他地方 - 但它很接近)

SQL = "SELECT photoID FROM photoSearch
WHERE MATCH (headline, caption, people, keywords)
AGAINST ('"&booleanSearchStr&"' IN BOOLEAN MODE)
AND dateCaptured BETWEEN '"&fromDate&"' AND '"&toDate&"' LIMIT 0,50;"
Set rs1 = conn.Execute(SQL)
While NOT rs1.EOF

    SQL = "SELECT photoID, setID, eventID, locationID, headline, caption, instructions, dateCaptured, dateUploaded, status, uploaderID, thumbH, thumbW, previewH, previewW, + more FROM photos LEFT JOIN events AS e USING (eventID) LEFT JOIN location AS l USING (locationID) WHERE photoID = "&rs1.Fields("photoID")&";"
    Set rs2 = conn.Execute(SQL)
    If NOT rs2.EOF Then
        Response.Write ( .. photo data .. )
    End If
    rs2.Close

rs1.MoveNext
Wend
rs1.Close

在测试时,将全文索引放在自己的表“photoSearch”上,而不是大表“photos”上,似乎在一定程度上提高了速度。我没有添加“photoSearch”表,它已经存在 - 这不是我的应用程序。如果我尝试加入两个表以丢失第二个查询,我会一起丢失我的索引,从而导致很长时间 - 所以我不能使用全文连接。这似乎是最快的方法。如果不是因为全文和连接问题,我早就将这两个查询结合起来了。

【问题讨论】:

    标签: object asp-classic recordset


    【解决方案1】:

    事情就是这样。首先,获取您的照片 ID 并让 mysql 认为这是一个仅包含照片 ID 的实际表,然后进行实际语句,不需要任何额外的记录集连接...

    并且不要忘记从头开始执行此操作。这是带有解释的示例代码:

    步骤 1 创建照片 ID 查找表并将其命名:这将是我们的 PhotoId 查找表,因此将其命名为“PhotoIds”

    SELECT photoID FROM photoSearch
    WHERE MATCH (headline, caption, people, keywords)
    AGAINST ('"&booleanSearchStr&"' IN BOOLEAN MODE)
    AND dateCaptured BETWEEN '"&fromDate&"' AND '"&toDate&"' LIMIT 0,50) AS PhotoIds
    

    第 2 步 现在我们有了照片 ID,因此可以从中获取信息。我们将在 WHERE 子句之前插入上面的语句,就像我们对真实表所做的那样。请注意,我们的“假”表必须在括号之间。

    SQL = "SELECT p.photoID, p.setID, p.eventID, p.locationID, p.headline, p.caption, + more FROM
        photos AS p,
        events AS e USING (p.eventID),
        location AS l USING (p.locationID),
        (SELECT photoID FROM photoSearch WHERE MATCH (headline, caption, people, keywords)
            AGAINST ('"&booleanSearchStr&"' IN BOOLEAN MODE) AND dateCaptured BETWEEN
            '"&fromDate&"' AND '"&toDate&"' LIMIT 0,50) AS PhotoIds
        WHERE p.photoID=PhotoIds.photoID;"
    

    注意:我只是在这里编写这些代码,从未测试过。可能有一些拼写错误或smt。如果您遇到问题,请告诉我。

    现在收到您的主要问题

    无需关闭已执行的查询,尤其是在您使用 execute 方法时。 Execute 方法在执行后自行关闭,除非它不返回任何记录集数据(这就是执行命令的目的),例如:“INSERT”、“DELETE”、“UPDATE”。如果您没有打开记录集对象,那么为什么要尝试关闭从未打开过的东西呢?相反,您可以使用 Set Rs=Nothing 取消引用对象并发送到垃圾收集以释放一些系统资源(这与 mysql 本身无关)。如果您使用“SELECT”查询(将返回一些数据的查询),您必须打开一个记录集对象 (ADODB.Recordset),如果您打开它,您需要在它完成工作后立即关闭它。

    最重要的是在每次页面加载后关闭“与mysql服务器的主连接”。所以你可以考虑把你的连接关闭算法(不是记录集close) 到一个包含文件,并将其插入到与数据库建立连接的每一页的末尾。长话短说:如果您使用 Open()

    ,则必须使用 Close()

    【讨论】:

    • 哇!这个新查询很有意义并且有效。我正在 MySQL Workbench 中进行测试,速度非常快。我需要将它添加到我的应用程序中,看看它是否加快了速度。我有一个问题;在这个查询中我应该在哪里放置一个 INNER JOIN?像这个; LEFT JOIN (photoPeople AS pp INNER JOIN as pe ON pp.peopleID = pe.PeopleID) ON p.photoID = pp.photoID?
    • @PaparazzoKid 在使用 MySQL 时,您不必使用 INEER JOIN、LEFT JOIN 等。您可以使用“,”(逗号运算符)连接多个表并伪命名表。即:(“SELECT t1.name, t2.name FROM very_long_table_name_table1 AS t1,very_long_table_name_table2 AS t2 WHERE t1.id=t2.id”)
    • 哦,这把扳手扔了。我一直对 MySQL 使用 LEFT、RIGHT、JOIN 或 INNER JOINS - 我需要阅读相关内容。我使用 LEFT 的原因是我想加入的表并不总是有匹配的记录。你的方法也一样吗?
    • 其实,取消那个,我想我已经解决了。这都是关于“WHERE t1.id = t2.id”的——我想它充当左连接。非常感谢您的帮助,您解决了一些问题。
    • @PaparazzoKid 我使用 LEFT 的原因是我想加入的表并不总是有匹配的记录。 如果是这种情况,你必须使用 LEFT JOIN 是.但也许类似 ("SELECT t1.name, t2.name FROM very_long_table_name_table1 AS t1,very_long_table_name_table2 AS t2 WHERE t1.id=t2.id AND t2.name!=NULL") 也可以代替的左连接。如果您需要有关此主题的帮助,建议您查看 mysql 手册SELECT syntax 参考并查找“逗号运算符”。
    【解决方案2】:

    如果您向我们展示您的 SQL 语句,也许我们可以向您展示如何将它们组合成一条 SQL 语句,这样您只需执行一个循环,否则,像这样的双循环确实会对服务器性能造成影响。但在我学习存储过程和联接之前,我可能会这样做:

     Set Conn = Server.CreateObject("Adodb.Connection")
     Conn.Open "ConnectionString"
    
     Set oRS = Server.CreateObject("Adodb.Recordset")
     oRS.Open "SQL STATEMENT", Conn
    
     Set oRS2 = Server.CreateObject("Adodb.Recordset")
     oRS2.ActiveConnection = Conn
    
     Do Until oRS.EOF
    
        oRS2.Open "SQL STATEMENT"
        If oRS2.EOF Then ...
        oRS2.Close
    
     oRS.Movenext
     Loop
     oRS.Close
     Set oRS = Nothing
     Set oRS2 = Nothing
     Set Conn = Nothing
    

    【讨论】:

    • 同意 100% 可以改进查询的设计以完全避免内部查询。这被称为“选择 N+1 问题”stackoverflow.com/questions/97197/…。如果您专注于该问题而不是优化记录集对象的处理方式,您将获得更好的结果。
    • 我知道,我记得这些天......一旦我开始组合 SQL 查询,服务器的性能显着提高了 100 倍。
    • 使用我的方法实际上得到了更好的结果。尝试和测试。正如您所说,通常我会尝试将两个查询合二为一,但这似乎不适用于我的情况。我的表有 500k 行和很多很多列,还有一些相当大的列(2000 多个字符)。当我的页面只使用一个查询时,它选择了该表中几乎所有的列,然后每页显示 50 条记录。查询花了很长时间!然后我改用上面的方法,只从顶部查询中选择 ID,并从循环内的第二个查询中获取大量数据。
    • @ZeeTee:在我的示例中,我没有双循环。这只是一个循环,但有多个记录集。
    • 我还在使用全文搜索来搜索我的热门查询。这不允许我使用任何连接,因为索引不起作用。
    【解决方案3】:

    我试着把它放在评论中,因为它没有直接回答你原来的问题,但它太长了.. :)

    您可以尝试使用子查询而不是连接,将外部查询嵌套在第二个查询中。 “ ...其中照片ID(从照片搜索中选择照片ID ...)”。不确定它是否会得到更好的结果,但可能值得一试。话虽如此,全文搜索的使用确实改变了查询的优化方式,因此可能需要更多的工作来确定合适的索引是什么(需要)。根据您现有的表现,可能不值得付出努力。

    您确定这个现有的代码/查询是当前的瓶颈吗?有时我们会花时间优化我们认为是瓶颈的东西,而事实并非如此...... :)

    另外一个想法 - 您可能需要考虑一些缓存逻辑来减少您可能进行的冗余查询的数量 - 无论是在页面级别还是在此方法级别。搜索参数可以连接在一起以形成用于将数据存储在某种缓存中的键。当然,您需要处理适当的缓存失效/到期逻辑。我已经看到系统速度提高了 100 倍,并且非常简单的缓存逻辑被添加到这样的瓶颈中。

    【讨论】:

    • 缓存听起来很有趣,可能会进一步研究。我实际上在这个特定页面或查询上没有任何瓶颈。我纯粹想知道在循环中使用时关闭记录集对象的最佳方法是什么。在循环之后或循环中。
    • 好的 - 你确实问过哪种方法是提高性能的最佳方法,所以我认为人们关注代码的性能方面是很自然的,在这种情况下,查询的影响比记录集对象的处理。作为一般原则,如果您没有性能问题,您可能不应该花时间优化它...... :)
    猜你喜欢
    • 2010-12-09
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-01
    • 2010-10-14
    • 2010-12-12
    相关资源
    最近更新 更多