3

We are conducting a wildcard search on a database table with column string. Does creating a non-clustered index on columns help with wildcard searches? Will this improve performance?

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
    )
 )

Proposed Index:

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

for this query

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

Currently using Microsoft SQL Server 2019.

mattsmith5
  • 540
  • 4
  • 29
  • 67
  • No. Although an index will (generally) be used if you provide the first character, eg `like 'X%furniture%'` – Bohemian Nov 15 '21 at 20:38
  • try `charindex` vs wild card as see if you can spot any performance difference – RoMEoMusTDiE Nov 15 '21 at 20:48
  • 3
    Downvotes are not a reflection on you personally. But writing efficient SQL and designing efficient databases is a very large topic that is not well-suited to a simple technical Q&A site like SO. – SMor Nov 15 '21 at 20:49

5 Answers5

3

For a double ended wildcard search as shown, an index cannot help you by restricting the rows SQL Server has to look at - a full table scan will be carried out. But it can help with the amount of data that has to be retrieved from disk.

Because in ProductName like '%furniture%', ProductName could start or end with any string, so no index can reduce the rows that have to be inspected.

However if a row in your Product table is 1,000 characters and you have 10,000 rows, you have to load that much data. But if you have an index on ProductName, and ProductName is only 50 characters, then you only have to load 10,000 * 50 rather than 10,000 * 1000.

Note: If the query was a single ended wildcard search with % at end of 'furniture%', then the proposed index would certainly help.

Dale K
  • 25,246
  • 15
  • 42
  • 71
3

Creating a normal index will not help(*), but a full-text index will, though you would have to change your query to something like this:

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

(* -- well, it can be slightly helpful, in that it can reduce a scan over every row and column in your table into a scan over merely every row and only the relevant columns. However, it will not achieve the orders of magnitude performance boost that we normally expect from indexes that turn scans into single seeks.)

RBarryYoung
  • 55,398
  • 14
  • 96
  • 137
  • 1
    Your answer doesn't answer the question as asked. And is not entirely correct, the index can help, just not in the filtering of rows. – Dale K Nov 15 '21 at 20:49
  • 1
    @DaleK This *is* an answer to the question and easily the lowest cost/impact way to address the problem described. And although it is technically true that creating a narrow index can help *slightly* because scans are *slightly* faster with the smaller leaves, this is generally not considered a good use of indexes and it is hard to see this as anything other than punditry wrt to the OPs problem. It certainly is not helpful. – RBarryYoung Nov 15 '21 at 20:54
  • 1
    FullTextIndexes can help with such queries, but they are complex to understand and implement. If you go this way, do your research and some serious testing! – Philip Kelley Dec 01 '21 at 17:32
  • @PhilipKelley They used to be a real bear to setup and manage, but I found that since SQL Server 2012 they've gotten a lot easier to use and administer. Still a lot more than regular indexes though, so "research and testing" is good advice. – RBarryYoung Dec 01 '21 at 17:47
1

First you can use FTS to search words into sentences even partially (beginning by).

For those ending by or for those containing you can use a rotative indexing technic:

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

Then now you can insert into the first table all the words you want from your sentences and finding it by ending of partially in querying those two tables...

As an example, word:

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;

You can now use :

SELECT * FROM T_WRD;
SELECT * FROM T_WORD_ROTATE_STRING_WRS;

To find those partial words

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
SQLpro
  • 3,994
  • 1
  • 6
  • 14
-2

It depends on the optimizer. Like usually requires a full table scan. if the optimizer can scan an index for matches than it will do an index scan which is faster than a full table scan.

if the optimizer does not select an index scan you can force it to use an index. You must measure performance times to determine if using an index scan decreases search time

Use with (index(index_name)) to force an index scan e.g.

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

SQL Server Index - Any improvement for LIKE queries?

If you use %search% pattern, the optimizer will always perform a full table scan.

Another technique for speeding up searches is to use substrings and exact match searches.

Golden Lion
  • 3,840
  • 2
  • 26
  • 35
-3

Yes, the part before the first % is matched against the index. Of course however, if your pattern starts with %, then a full scan will be performed instead.

Blindy
  • 65,249
  • 10
  • 91
  • 131
  • 4
    But question shows search term starting with `%`? – Dale K Nov 15 '21 at 20:39
  • No, the question is quite literally `Does Adding Indexes speed up String Wildcard % searches?`, and the answer is yes, it speeds up the query in regards to the first non-wildcard part. I added the second part of my comment to cover the rest of OP. Is there anything else I can help you with, Dale? – Blindy Nov 15 '21 at 20:57