【问题标题】:Does Adding Indexes speed up String Wildcard % searches?添加索引是否会加快字符串通配符百分比搜索?
【发布时间】:2021-12-27 00:33:18
【问题描述】:

我们正在使用列字符串对数据库表进行通配符搜索。在列上创建非聚集索引是否有助于通配符搜索?这会提高性能吗?

CREATE TABLE [dbo].[Product](
    [ProductId] [int] NOT NULL,
    [ProductName] [varchar](250) NOT NULL,
    [ModifiedDate] [datetime] NOT NULL, 
    ...
    CONSTRAINT [PK_ProductId] PRIMARY KEY CLUSTERED 
    (
       [ProductId] ASC
    )
 )

建议索引:

CREATE NONCLUSTERED INDEX [IX_Product_ProductName] ON [dbo].[Product] [ProductName])

对于这个查询

select * from dbo.Product where ProductName like '%furniture%'

当前使用 Microsoft SQL Server 2019。

【问题讨论】:

  • 没有。如果您提供第一个字符,则(通常)将使用索引,例如like 'X%furniture%'
  • 试试 charindex 与通配符,看看你是否能发现任何性能差异
  • 投反对票并不代表您个人。但是编写高效的 SQL 和设计高效的数据库是一个很大的话题,不太适合 SO 这样的简单技术问答网站。

标签: sql sql-server tsql indexing sql-server-2019


【解决方案1】:

创建普通索引无济于事 (*),但 full-text index 将有帮助,但您必须将查询更改为以下内容:

select * from dbo.Product where ProductName CONTAINS 'furniture'

(* -- 嗯,它可能有点有帮助,因为它可以减少对表中每一行和每一列的扫描,而仅仅行和仅相关列。但是,它不会实现我们通常期望的将扫描转换为单次搜索的索引的数量级性能提升。)

【讨论】:

  • 您的回答未按要求回答问题。并且不完全正确,索引可以提供帮助,只是不能过滤行。
  • @DaleK 这问题的答案,并且很容易成为解决所描述问题的最低成本/影响的方法。尽管从技术上讲,创建一个窄索引可以稍微有所帮助,因为较小的叶子扫描速度稍微快,但这通常不被认为是对索引的良好使用,它很难将其视为 OPs 问题的专家之外的任何东西。这当然没有帮助。
  • FullTextIndexes 可以帮助处理此类查询,但它们很难理解和实现。如果您这样做,请进行研究并进行一些认真的测试!
  • @PhilipKelley 他们曾经是一个真正的设置和管理熊,但我发现自 SQL Server 2012 以来,他们变得更容易使用和管理。不过仍然比常规索引要多得多,所以“研究和测试”是个好建议。
【解决方案2】:

首先,您可以使用 FTS 将单词搜索成句子,甚至部分(开始)。

对于那些以你结尾或包含你的人可以使用旋转索引技术:

CREATE TABLE T_WRD
(WRD_ID                BIGINT IDENTITY PRIMARY KEY,
 WRD_WORD              VARCHAR(64) COLLATE Latin1_General_100_BIN NOT NULL UNIQUE,
 WRD_DROW              AS REVERSE(WRD_WORD) PERSISTED NOT NULL UNIQUE,
 WRD_WORD2             VARCHAR(64) COLLATE Latin1_General_100_CI_AI NOT NULL) ;
GO
    
CREATE TABLE T_WORD_ROTATE_STRING_WRS
(WRD_ID                BIGINT NOT NULL REFERENCES T_WRD (WRD_ID),
 WRS_ROTATE            SMALLINT NOT NULL,
 WRD_ID_PART           BIGINT NOT NULL REFERENCES T_WRD (WRD_ID),
 PRIMARY KEY (WRD_ID,  WRS_ROTATE));
GO
    
CREATE OR ALTER TRIGGER E_I_WRD
ON T_WRD
FOR INSERT
AS
    
SET NOCOUNT ON;
    
-- splitting words
WITH R AS
(
SELECT WRD_ID, TRIM(WRD_WORD) AS WRD_WORD, 0 AS ROTATE
FROM   INSERTED
UNION ALL
SELECT WRD_ID, RIGHT(WRD_WORD, LEN(WRD_WORD) -1), ROTATE + 1
FROM   R
WHERE  LEN(WRD_WORD) > 1
)
SELECT *
INTO #WRD
FROM   R;
    
-- inserting missing words
INSERT INTO T_WRD (WRD_WORD, WRD_WORD2)
SELECT WRD_WORD, LOWER(WRD_WORD) COLLATE SQL_Latin1_General_CP1251_CI_AS
FROM   #WRD
WHERE  WRD_WORD NOT IN (SELECT WRD_WORD 
                        FROM T_WRD);
    
-- inserting cross reference words
INSERT INTO T_WORD_ROTATE_STRING_WRS
SELECT M.WRD_ID, ROTATE, D.WRD_ID
FROM   #WRD AS M
      JOIN T_WRD AS D
         ON M.WRD_WORD = D.WRD_WORD
WHERE  NOT EXISTS(SELECT 1/0
                  FROM   T_WORD_ROTATE_STRING_WRS AS S
                  WHERE  S.WRD_ID = M.WRD_ID
                  AND  S.WRS_ROTATE = ROTATE);
GO

那么现在你可以在第一个表中插入你想要的句子中的所有单词,并通过查询这两个表的部分结尾来找到它...

例如,单词:

WITH 
T AS (SELECT 'électricité' AS W)
INSERT INTO T_WRD 
SELECT W, LOWER(CAST(W AS VARCHAR(64)) COLLATE SQL_Latin1_General_CP1251_CI_AS) AS W2
FROM  T;

您现在可以使用:

SELECT * FROM T_WRD;
SELECT * FROM T_WORD_ROTATE_STRING_WRS;

找到那些部分单词

【讨论】:

    【解决方案3】:

    这取决于优化器。就像通常需要全表扫描一样。如果优化器可以扫描索引以查找匹配项,那么它将执行比全表扫描更快的索引扫描。

    如果优化器没有选择索引扫描,您可以强制它使用索引。您必须测量性能时间以确定使用索引扫描是否会减少搜索时间

    使用with (index(index_name)) 强制进行索引扫描,例如

    select * from t1 with (index(t1i1)) where v1 like '456%'
    

    SQL Server Index - Any improvement for LIKE queries?

    如果您使用%search% 模式,优化器将始终执行全表扫描。

    另一种加快搜索速度的技术是使用子字符串和完全匹配搜索。

    【讨论】:

      【解决方案4】:

      对于如图所示的双端通配符搜索,索引无法通过限制 SQL Server 必须查看的行来帮助您 - 将执行全表扫描。但它可以帮助处理必须从磁盘检索的数据量。

      因为在ProductName like '%furniture%' 中,ProductName 可以以任何字符串开头或结尾,因此没有索引可以减少必须检查的行数。

      但是,如果您的 Product 表中的一行有 1,000 个字符并且您有 10,000 行,则您必须加载那么多数据。但是如果你在ProductName上有一个索引,而ProductName只有50个字符,那么你只需要加载10,000 * 50而不是10,000 * 1000。

      注意:如果查询是单端通配符搜索,在'furniture%' 末尾带有 %,那么建议的索引肯定会有所帮助。

      【讨论】:

      • 好的,我明白了,它是一个双面通配符,我相信如果在 'furniture%' 的末尾用 % 单结尾,会有帮助吗?
      • @mattsmith5 正确。
      【解决方案5】:

      是的,第一个% 之前的部分与索引匹配。当然,如果您的模式以 % 开头,则将改为执行完整扫描。

      【讨论】:

      • 但问题显示搜索词以%开头?
      • 不,这个问题的字面意思是Does Adding Indexes speed up String Wildcard % searches?,答案是肯定的,它加快了关于第一个非通配符部分的查询。我添加了评论的第二部分以涵盖 OP 的其余部分。戴尔,还有什么我可以帮你的吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      • 1970-01-01
      • 1970-01-01
      • 2023-01-05
      • 2011-01-19
      相关资源
      最近更新 更多