【问题标题】:Searching 13 million records using full text search with additional conditions使用带附加条件的全文搜索搜索 1300 万条记录
【发布时间】:2017-12-27 11:44:05
【问题描述】:

使用附加条件执行 SQL Server 全文搜索时出现性能问题。 (SQL Server 2012)

我正在尝试根据搜索过滤器列表(表值参数)过​​滤数据,这将返回匹配过滤器的所有记录,而过滤器的单个记录没有来自表的任何记录。

全文搜索索引已在表 Names 上,用于列 SNAME

在存储过程中,表类型参数SearchFilter用于传递名称和地址信息列表。

两个表都有超过 1400 万条记录,当我们使用过滤器列表中传递的 1000 条唯一记录执行该过程时,大约需要 7 分钟才能返回结果(1400 条记录)。

过滤条件是:包含(姓名)和街道地址、城市、州、邮编完全匹配。

是否有任何替代方法可以避免 SQL Server CONTAINS 函数需要字符串值或变量时的 while 循环?

CREATE TABLE [dbo].[Names]
(
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [UIN] [varchar](9) NULL,
    [SNAME] [varchar](500) NULL,
    CONSTRAINT [PK_Names] 
        PRIMARY KEY CLUSTERED ([ID] ASC)
)

CREATE TABLE [dbo].[ADDRESSES]
(
    [UIN] [varchar](9) NULL,
    [STREET1] [varchar](100) NULL,
    [STREET2] [varchar](50) NULL,
    [CITY] [varchar](30) NULL,
    [STATE] [varchar](2) NULL,
    [ZIP] [varchar](10) NULL    
) ON [PRIMARY]

CREATE TYPE [dbo].[SearchFilter] AS TABLE
(
    [UIN] [varchar](40) NULL,
    [SNAME] [varchar](max) NULL,
    [StreetAddress] [varchar](max) NULL,
    [City] [varchar](max) NULL,
    [State] [varchar](50) NULL,
    [Zip] [varchar](20) NULL
)

-- Stored procedure logic
DECLARE @filterList AS [dbo].[SearchFilter]

DECLARE @NoOfRows INT, @counter INT = 0

SET @NoOfRows = (SELECT COUNT(1) FROM @filterList)

DECLARE @result TABLE (UIN varchar(40), 
                       NAME varchar(500), 
                       StreetAddress varchar(1000), 
                       Zipcode varchar(20),
                       State varchar(20),
                       City varchar(1000),
                       IsRecordFound varchar(50)
                      );

WHILE (@NoOfRows > @counter)
BEGIN
    DECLARE @SearchName VARCHAR(4000)

    SET @SearchName = (SELECT '"'+SNAME+'"' FROM @filterList ORDER BY SNAME OFFSET @counter ROWS FETCH NEXT 1 ROWS ONLY)  

    --Start: Process to Select Records
    ;WITH Filter_CTE AS
    (
        SELECT 
            SNAME, StreetAddress, City, State, ZipCode 
        FROM
            @filterList 
        ORDER BY 
            SNAME 
            OFFSET @counter ROWS FETCH NEXT 1 ROWS ONLY 
    )
    INSERT INTO @result (UIN, NAME, STREETADDRESS, CITY, STATE, ZIPCODE, PHONE, IsRecordFound)
        SELECT DISTINCT 
            en.UIN, ISNULL(en.SNAME, Filter_CTE.SNAME),
            Filter_CTE.StreetAddress, Filter_CTE.ZipCode,
            Filter_CTE.state, Filter_CTE.City,
            IIF(en.UIN IS NULL, 'Not Found', 'Found') AS IsRecordFound 
        FROM 
            dbo.Names en 
        INNER JOIN 
            dbo.ADDRESSES ea ON en.UIN = ea.UIN
        RIGHT JOIN 
            Filter_CTE ON ea.ZIP = Filter_CTE.Zip 
                       AND ea.STATE = Filter_CTE.State 
                       AND ea.CITY = Filter_CTE.City 
                       AND (ISNULL(ea.STREET1, '') + ' ' + ISNULL(ea.STREET2, '')) = Filter_CTE.StreetAddress
                       AND CONTAINS(en.SNAME,@SearchName)
            --END

    SET @counter += 1
END 

SELECT 
    UIN, NAME, STREETADDRESS, CITY, STATE, ZIPCODE, PHONE 
FROM 
    @result 

【问题讨论】:

  • 您可以尝试使用like '%<yourname>%' 匹配而不是contains() - 它可能会更快地依赖于sqlServer 所做的优化。查询优化很大程度上取决于您使用的数据和计划/索引 - 如果您真的必须搜索那么多数据,则需要一些时间。
  • 您确定表NamesAddresses 具有覆盖您RIGHT JOIN Filter_CTE 子句中使用的所有列的索引吗?
  • 是的,名称和地址表涵盖了 RIGHT JOIN Filter_CTE 子句中使用的所有列(地址中的 STREET1 和 STREET2 列除外)的索引
  • 如果从RIGHT JOIN 中删除AND CONTAINS(en.SNAME,@SearchName) 条件会怎样?你有没有尝试过?会不会更快?

标签: sql-server-2012 full-text-search query-performance table-valued-parameters query-tuning


【解决方案1】:

目前无法在CONTAINSCONTAINSTABLE 中使用列名作为搜索条件。因此,您不能在数据表和应用了 FTS 谓词的 SearchFilter 表之间直接使用 JOIN

在其他问题/论坛中找到的当前解决方案是循环遍历过滤器列表并在变量中提供CONTAINS 的搜索条件,就像您一样。所以,你不会摆脱这个循环。

但是,查看您的查询,我发现了许多其他可能影响性能的问题:

  1. INSERT INTO @result ... SELECT DISTINCT ... 中的DISTINCT 子句。它位于您 JOIN 拥有数百万条记录的表的级别。虽然我知道最终结果可能只包含几千行,但最好将 DISTINCT 移动到这一行:

    SELECT DISTINCT
        UIN, NAME, STREETADDRESS, CITY, STATE, ZIPCODE, PHONE 
    FROM 
        @result 
    
  2. 这个条件AND (ISNULL(ea.STREET1, '') + ' ' + ISNULL(ea.STREET2, '')) = Filter_CTE.StreetAddress 肯定不是 SARGable。您使用连接和函数 (ISNULL()) 来阻止 SQL Server 在dbo.ADDRESSES ea 表上使用现有索引。检查这个问题:What makes a SQL statement sargable? 了解如何以允许使用索引的方式构造 JOIN / WHERE 条件。 在这种特殊情况下,最好将计算列添加到 dbo.Addresses 表,然后在其上构建索引(或将其添加到现有索引):

    CREATE TABLE [dbo].[ADDRESSES]
    (
        ...
        STREET as (ISNULL(ea.STREET1, '') + ' ' + ISNULL(ea.STREET2, '')),
        ...
    )
    

因此修复上述 1. 和 2. 然后在 RIGHT JOIN 中注释 AND CONTAINS(en.SNAME,@SearchName) 条件并注意执行时间。之后,取消注释 CONTAINS 条件并查看添加了多少延迟。这样您就可以确定是 FTS 引擎造成了延迟,还是您的主查询本身需要改进。

为了能够提供更多建议,我们需要查看您的程序的执行计划。您可以使用此页面分享您的查询执行计划:https://www.brentozar.com/pastetheplan/

HTH

【讨论】:

  • 感谢您在我的问题上花费的宝贵时间和精力。我已经更新了 while 循环中的逻辑,还实现了您的建议之一,即使用索引计算列,它提高了存储过程的整体性能。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 2011-11-13
  • 1970-01-01
  • 1970-01-01
  • 2018-07-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多