【问题标题】:Caching result of SELECT statement for reuse in multiple queries缓存 SELECT 语句的结果,以便在多个查询中重用
【发布时间】:2012-02-13 20:41:30
【问题描述】:

我有一个相当复杂的查询来根据用户输入的参数提取我感兴趣的结果的Id 字段。

提取相关 ID 后,我在单独的查询中多次使用生成的 ID 集,以提取我想要的实际输出记录集(通过加入其他表、使用聚合函数等)。

我想避免为我想要返回的每组结果单独运行初始查询。我想我的情况是一种常见的模式,所以我对最好的方法很感兴趣。

数据库在 MS SQL Server 中,我使用的是 .NET 3.5。

【问题讨论】:

  • 到目前为止我所了解的是,我假设您有一个存储的过程,您提取一些 ID 并将其发送回前端,并且基于这些 ID,您必须获取一些其他信息,您是每次都重复第一步,并且您想重复使用第一步。对吗?
  • 我没有存储过程。我正在与前端分开运行一系列查询,每个查询都使用一个复杂的子查询,这对于所有查询都是相同的。
  • 所有这些系列的查询是否都在从前端到数据库再到前端的单个请求中执行?
  • @Penkaj 不,单独的请求。

标签: asp.net sql-server performance


【解决方案1】:

如果问题包含对未优化解决方案的一些测量(数据大小、时间),那肯定会有所帮助。这里可以考虑多种技术,其中一些在其他答案中列出。我假设您不想重复运行相同查询的原因是性能。

如果缓存 ID 集的所有使用都包含整个集合与其他表的连接,则解决方案绝对不应涉及在数据库之外缓存 ID 集。如果可以避免的话,数据不应该往返传输。

在某些情况下(当不涉及游标或极其复杂的 SQL 时)最好(即使违反直觉)不执行缓存并将重复的 SQL 简单地连接到所有所需的查询。毕竟,每个查询都需要基于其中一个连接的表进行遍历,然后性能在很大程度上取决于快速连接和评估所有剩余信息所需的索引的可用性。

在数据库中“缓存”一组 ID 最直观的方法是临时表(如果命名为 #something,它是连接专用的,因此可供并行独立客户端使用;或者它可以命名为 @987654322 @ 并且是全球性的)。如果表将有许多记录,则索引是必要的。为获得最佳性能,索引应该是聚集索引(每个表只允许一个),或者仅在构建该集合后创建,其中索引创建速度稍快。

索引视图显然比临时表更可取,除非在整个过程中仅读取基础数据,或者您可以并且希望忽略此类更新以保持整个报告集的一致性。然而,索引视图始终准确地投影基础数据的能力是以减慢这些更新为代价的。

这个问题的另一个答案提到了存储过程。这主要是一种组织代码的方式。但是,如果采用这种方式,最好避免使用临时表,因为对临时表的这种引用会阻止预编译存储过程;如果可以,请查看视图或索引视图。

无论您选择哪种方法,都不要猜测性能特征和查询优化器的行为。学习显示查询执行计划(在 SQL Server Management Studio 中)并确保您看到索引访问,而不是结合多个大型数据集的嵌套循环;只添加可以明显且彻底改变查询性能的索引。选择得当的索引通常可以将查询的性能提高 1000 倍,因此学习起来有些复杂,但对于成功至关重要。

最后但并非最不重要的一点是,确保在重新填充数据库时使用UPDATE STATISTICS(并且在生产中每晚使用),否则您的查询优化器将无法将您创建的索引用于最佳用途。

【讨论】:

  • 感谢您的详尽回答。目前,我已按照您的建议在每个查询中使用 JOIN 重复子查询。这样做可能比我认为的其他任何事情都更容易和更快。感觉就是违反了 DRY,浪费了处理。就像你说的那样,每个查询都可以通过这种方式得到更好的优化——而且似乎 DBMS 以提供意想不到的性能特征而臭名昭著。
  • @Andrew - 如果 DRY 是目标,那么公共部分可以封装到普通的 VIEW 中。这对性能没有(直接)影响,尽管过度使用视图,不相关的表确实倾向于潜入查询,使查询优化器的工作更加困难。所以我只建议对大的、复杂的、经常修改的或期望从越来越多的地方调用的 JOIN 进行这种封装。
【解决方案2】:

如果您打算在应用程序代码中缓存结果集,那么 ASP.NET 具有缓存,您的 Winform 将拥有保存数据的对象,您可以使用它来重用数据。

如果计划在 SQL Server 中做同样的事情,您可以考虑使用索引视图来找出 Id。该视图将被具体化,因此您可以更快地获得结果。您甚至可以考虑使用临时表来临时保存 id。

【讨论】:

    【解决方案3】:

    使用 SQL Server 2008,您可以将表变量作为参数传递给 SQL。只需缓存 ID,然后将它们作为表变量传递给获取数据的查询。这种方法唯一需要注意的是,您必须将表类型预定义为 UDT。

    http://msdn.microsoft.com/en-us/library/bb510489.aspx

    【讨论】:

      【解决方案4】:

      对于 SQL Server,Microsoft 通常建议在可行的情况下使用存储过程。

      以下是一些优点:

      http://blog.sqlauthority.com/2007/04/13/sql-server-stored-procedures-advantages-and-best-advantage/

      * Execution plan retention and reuse
      * Query auto-parameterization
      * Encapsulation of business rules and policies
      * Application modularization
      * Sharing of application logic between applications
      * Access to database objects that is both secure and uniform
      * Consistent, safe data modification
      * Network bandwidth conservation
      * Support for automatic execution at system start-up
      * Enhanced hardware and software capabilities
      * Improved security
      * Reduced development cost and increased reliability
      * Centralized security, administration, and maintenance for common routines
      

      还值得注意的是,与其他 RDBMS 供应商(例如 Oracle)不同,MSSQL 会自动缓存所有执行计划:

      http://msdn.microsoft.com/en-us/library/ms973918.aspx

      但是,对于 SQL Server 的最后几个版本,执行 为所有 T-SQL 批处理缓存计划,无论是否 它们在存储过程中

      【讨论】:

      • 使用存储过程如何帮助缓存子查询的结果?
      • 我猜这个答案是对问题标题的回应,它说“缓存初始 SELECT 语句以供重用”,而不是缓存该查询的 results
      【解决方案5】:

      最佳方法取决于 ID 更改的频率,或者您希望多久重新查找一次。

      一种技术是使用Cache 对象(也可以从HttpRuntime.Cache 访问)将结果简单地存储在ASP.NET 对象缓存中。例如(来自页面):

      this.Cache["key"] = "value";
      

      这个主题有很多可能的变化。

      【讨论】:

      • 我可以将缓存的记录集重新用作表以供将来查询吗?
      • 如果您使用分离的数据存储,例如 DataTable,您可以将其重新用作未来查询的信息源,只要您只读取它而不写入它。
      • 你会说这比在odetocode.com/code/365.aspx 中使用表变量更好吗?
      • 我取决于你想要做什么。如果您需要将一个查询的多行结果作为存储过程的一部分存储到另一个查询,表变量是一个不错的选择。如果要将结果从一个页面请求传递到另一个页面请求,那么 ASP.NET 对象缓存是一个不错的选择。您可以单独使用 DataTable 在 Web 服务器端的同一页面请求中将结果从一个查询传送到另一个查询。
      【解决方案6】:

      您可以使用Memcached 将值缓存在内存中。 正如我看到的there are 一些.net 端口。

      【讨论】:

      • Memcached 或 Velocity 在多服务器、负载平衡的环境中可能很有用。但是,对于典型的单服务器应用程序,与进程内解决方案相比,它们过于繁琐且速度较慢。
      【解决方案7】:

      您要查询的数据多久更改一次?对我来说,这听起来像是数据仓库的完美场景,您可以将数据扁平化以便更快地检索数据,并完全按照您的“DTO”想要查看数据的方式创建表。此方法与索引视图不同,因为它只是一个具有快速查找操作的表,如果您在计划查询的列上正确设置索引,则可以特别改进

      【讨论】:

        【解决方案8】:

        您可以创建Global temporary Table。即时创建表。现在根据您的要求插入记录。在您的连接中的下一个请求中访问此表...以实现可重用性

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-12-12
          • 1970-01-01
          • 1970-01-01
          • 2016-01-04
          • 1970-01-01
          • 1970-01-01
          • 2011-06-26
          • 2022-11-21
          相关资源
          最近更新 更多