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.
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.)
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.
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
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.
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.
I have a query
Select Column1, column2.... column30
From Table
Where (#var IS NULL OR Col1 = #var)
This can get resolved to a clustered index scan where every row in a table getting checked against this condition #var IS NULL and the index set up on the column Col1 is disregarded. Is there a way to re-write this query to enable the optimizer to use the index on col1? Thank you!
So the current query I have takes long time to run. When i run execution plan, it shows: Table Insert (#tempdata), Cost: 99%. I realized i need to add non clustered index to run the query fast. So I have added this line of code but still there is no difference:
create nonclustered index #temp_index
on [dbo].#tempData ([class_room]) include ([class_name],[class_floor],[class_building])
This is the current query I have:
IF OBJECT_ID('tempdb..#tempdata') IS NOT NULL DROP TABLE #tempdata
SELECT [class_room][class_name],[class_floor],[class_building]
INTO #tempdata
FROM class_info x
create nonclustered index #temp_index
on [dbo].#tempData ([class_room]) include ([class_name],[class_floor],[class_building])
;with cte1 as(
SELECT [class_room][class_name],[class_floor],[class_building]
FROM #tempdata
WHERE class_room <> '')
select * from cte1
This is a bit long for a comment.
Can you explain why you are not just running this code?
SELECT [class_room], [class_name], [class_floor], [class_building]
FROM class_info x
WHERE class_room <> '';
If performance is an issue, I first would recommend getting rid of unnecessary reads and writes -- such as creating a temporary table.
Is there a way to truncate an nvarchar using DATALENGTH? I am trying to create an index on a column, but an index only accepts a maximum of 900 bytes. I have rows that consist of 1000+ bytes. I would like to truncate these rows and only accept the first n characters <= 900 bytes.
Can be this sql useful, Just update the table for that column.
Update Table
Set Column = Left(Ltrim(Column),900)
Create a COMPUTED COLUMN that represents the data you want to index, then create an index on it.
ALTER TABLE MyTable ADD ComputedColumn AS LEFT(LargeNVarcharColumn,900);
CREATE NONCLUSTERED INDEX MyIndex ON MyTable
(
ComputedColumn ASC
);
Reference:
computed_column_definition
Indexes on Computed Columns
Trim the column ,left or right side to 900 characters ,create a index on that column
ALTER TABLE usertable ADD used_column AS LEFT(nvarcharcolumn,900);
create a index on this used columm.it will work
Is there a possibility to create a conditional index in db2?
same as in oracle:
CREATE UNIQUE INDEX my_index ON my_table (
CASE WHEN my_column = 1
THEN indexed_column
ELSE NULL
END);
or mssql:
CREATE UNIQUE INDEX my_index
ON my_table (indexed_column) WHERE my_column = 1
Thanks :)
This looks like a contrived example, but I don't see any benefit of an index containing only the values 1 and NULL.
Expression-based indexes are supported beginning with DB2 LUW 10.5. If you are unable to upgrade, you can simulate the behaviour using a computed column (which is what Oracle does behind the scenes anyway).
The uniqueness is also checked during the execution of the CREATE
INDEX statement. If the table already contains rows with duplicate key
values, the index is not created.