使用附加条件的全文搜索搜索 1300 万条记录
Searching 13 million records using full text search with additional conditions
执行 SQL 带有附加条件的服务器全文搜索时出现性能问题。 (SQL 服务器 2012)
我正在尝试根据搜索过滤器列表(table 值参数)过滤数据,这将 return 匹配过滤器的所有记录和过滤器的单个记录没有来自 tables.
的任何记录
SNAME
.
列的 table Names
上已经有全文搜索索引
在存储过程中,table类型参数SearchFilter
用于传递名称和地址信息列表。
两个 table 都有超过 1400 万条记录,当我们在过滤器列表中传递 1000 条唯一记录执行该过程时,return 结果(1400 条记录)花费了大约 7 分钟。
过滤条件是:包含(姓名)和街道地址、城市、州、邮政编码完全匹配。
是否有任何替代方法可以避免 while 循环,因为 SQL 服务器 CONTAINS
函数需要字符串值或变量?
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
目前无法在CONTAINS or CONTAINSTABLE中使用列名作为搜索条件。因此,您不能在应用 FTS 谓词的情况下在数据 table 和 SearchFilter
table 之间执行直接 JOIN
。
在其他 questions/forums 中找到的当前解决方案是循环遍历过滤器列表并在变量中提供 CONTAINS
搜索条件,就像您所做的那样。所以,你不会摆脱这个循环。
但是,在查看您的查询时,我发现了一些可能影响性能的其他问题:
DISTINCT
INSERT INTO @result ... SELECT DISTINCT ...
中的子句。它处于 JOIN
到 table 数百万条记录的级别。虽然我知道最终结果可能只包含几千行,但最好将 DISTINCT
移动到这一行:
SELECT DISTINCT
UIN, NAME, STREETADDRESS, CITY, STATE, ZIPCODE, PHONE
FROM
@result
这个条件 AND (ISNULL(ea.STREET1, '') + ' ' + ISNULL(ea.STREET2, '')) = Filter_CTE.StreetAddress
当然不是 SARGable。您使用串联和函数 (ISNULL()
) 阻止 SQL 服务器使用 dbo.ADDRESSES ea
table 上的现有索引。检查这个问题:What makes a SQL statement sargable? 以了解如何以允许使用索引的方式构造 JOIN
/ WHERE
条件。
在这种特殊情况下,最好将计算列添加到 dbo.Addresses table,然后在其上构建索引(或将其添加到现有索引):
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
执行 SQL 带有附加条件的服务器全文搜索时出现性能问题。 (SQL 服务器 2012)
我正在尝试根据搜索过滤器列表(table 值参数)过滤数据,这将 return 匹配过滤器的所有记录和过滤器的单个记录没有来自 tables.
的任何记录SNAME
.
Names
上已经有全文搜索索引
在存储过程中,table类型参数SearchFilter
用于传递名称和地址信息列表。
两个 table 都有超过 1400 万条记录,当我们在过滤器列表中传递 1000 条唯一记录执行该过程时,return 结果(1400 条记录)花费了大约 7 分钟。
过滤条件是:包含(姓名)和街道地址、城市、州、邮政编码完全匹配。
是否有任何替代方法可以避免 while 循环,因为 SQL 服务器 CONTAINS
函数需要字符串值或变量?
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
目前无法在CONTAINS or CONTAINSTABLE中使用列名作为搜索条件。因此,您不能在应用 FTS 谓词的情况下在数据 table 和 SearchFilter
table 之间执行直接 JOIN
。
在其他 questions/forums 中找到的当前解决方案是循环遍历过滤器列表并在变量中提供 CONTAINS
搜索条件,就像您所做的那样。所以,你不会摆脱这个循环。
但是,在查看您的查询时,我发现了一些可能影响性能的其他问题:
DISTINCT
INSERT INTO @result ... SELECT DISTINCT ...
中的子句。它处于JOIN
到 table 数百万条记录的级别。虽然我知道最终结果可能只包含几千行,但最好将DISTINCT
移动到这一行:SELECT DISTINCT UIN, NAME, STREETADDRESS, CITY, STATE, ZIPCODE, PHONE FROM @result
这个条件
AND (ISNULL(ea.STREET1, '') + ' ' + ISNULL(ea.STREET2, '')) = Filter_CTE.StreetAddress
当然不是 SARGable。您使用串联和函数 (ISNULL()
) 阻止 SQL 服务器使用dbo.ADDRESSES ea
table 上的现有索引。检查这个问题:What makes a SQL statement sargable? 以了解如何以允许使用索引的方式构造JOIN
/WHERE
条件。 在这种特殊情况下,最好将计算列添加到 dbo.Addresses table,然后在其上构建索引(或将其添加到现有索引):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