动态查询的索引解决方案

Mur*_*ilo 0 index sql-server sql-server-2014

我有一个贷记/借记表(5 百万条记录)。应用程序必须提供一个 UI 视图来搜索提供 N 个条件的数据:

  • 日期类型(必填)例如。创建日期或截止日期
  • 日期范围(必填)
  • 状态(付费、未付费或两者兼有)
  • 文件号码
  • 顾客ID
  • 付款方式(信用卡、现金等)

用户必须提供前两个标准,但可以提供或不提供另一个。所以我有很多组合。

我想我将不得不创建许多索引,例如:

  • ix_search_customerID_dueDate (customerID, DueDate)
  • ix_search_customerID_creationDate (customerID, creationDate)

有无数组合。我使用 C# 和实体框架根据提供的值生成查询,这很容易做到,但我不知道如何创建索引来涵盖所有可能性。是否有可能或我应该更改 UI 逻辑?

我阅读了这个答案,因此基于此我相信如果我创建一个索引(creationDate、状态、文档、customerID、typeOfPayment)并且用户只提供例如 Customer ID 索引将不起作用。

Aar*_*and 7

您无法创建支持所有这些可选参数的单个索引。您可以制作多个索引,尽最大努力支持最常见的参数组合,但要支持所有可能的组合,开销可能不值得(并且您必须衡量对工作负载写入部分的影响 -索引不是免费的)。

我对“客户可以以任何他们喜欢的方式搜索”问题的解决方案是创建厨房水槽过程,它使用动态 SQL 根据提供的参数构造 where 子句。这样做的目的是让 SQL Server 为每个参数组合编译一个单独的计划,这样您就不会拥有一个试图针对所有场景进行优化但实际上只能针对极少数场景进行优化的海量查询。基本方法是:

CREATE PROCEDURE dbo.KitchenSink
  @param1 datatype = NULL,
  @param2 datatype = NULL
AS
BEGIN
  SET NOCOUNT ON;

  DECLARE @sql nvarchar(max) = N'SELECT ... FROM dbo.table 
    WHERE 1 = 1'

    + CASE WHEN @param1 IS NULL THEN N'' ELSE
        N' AND column1 = @param1' END

    + CASE WHEN @param2 IS NULL THEN N'' ELSE
        N' AND column2 = @param2' END

    ...
  ;

  EXEC sys.sp_executesql @sql, N'@param1 datatype, @param2 datatype, ...',
         @param1, @param2, ...;
END
GO
Run Code Online (Sandbox Code Playgroud)

这将使您有机会充分利用具有与提供的参数组合对齐的键列的任何索引。您将不得不承认某些查询的性能不佳,但这应该有助于最大限度地减少这些情况。

额外的警告:

  • 您可以OPTION (RECOMPILE)在动态 SQL 内部使用来阻止参数嗅探(例如,即使仅提供姓氏,单独的动态 SQL 也无法帮助您每次生成最佳计划,因为搜索LIKE '%x%'需要与 不同的计划LIKE '[a-m]%')。
  • 您应该使用优化临时工作负载设置(请参阅此处此处),以便 SQL Server 不会在仅使用一次的潜在复杂和大型计划上浪费宝贵的计划缓存空间。