To jest SQL Server 2008 R2 SP2. Mam 2 stoły. Oba są identyczne (dane i indeksowanie), z tą różnicą, że pierwsza tabela ma kolumnę WARTOŚĆ, nvarchar(max)
a druga ma taką samą kolumnę jak nvarchar(800)
. Ta kolumna jest uwzględniona w indeksie nieklastrowanym. Utworzyłem również indeks klastrowy na obu tabelach. Odbudowałem również indeksy. Maksymalna długość ciągu w tej kolumnie wynosi 650.
Jeśli uruchomię to samo zapytanie dla obu nvarchar(800)
tabel, jest to konsekwentnie szybsze, wiele razy dwa razy szybsze. Pewnie wydaje się, że pokonuje cel „varchara”. Tabela zawiera ponad 800 000 wierszy. Kwerenda powinna obejmować około 110 000 wierszy (co szacuje plan).
Według statystyk io nie ma odczytów lob, więc wszystko wydaje się być w rzędzie. Plany wykonania są takie same, z tą różnicą, że istnieje niewielka różnica w odsetku kosztów między dwiema tabelami, a szacowany rozmiar wiersza jest większy w przypadku nvarchar(max)
(91 bajtów w porównaniu z 63 bajtami). Liczba odczytów jest prawie taka sama.
Skąd ta różnica?
===== Schemat ======
CREATE TABLE [dbo].[table1](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ProductID] [bigint] NOT NULL,
[ProductSkeletonID] [bigint] NOT NULL,
[Value] [nvarchar](max) NOT NULL,
[IsKeywordSearchable] [bit] NULL,
[ValueInteger] [bigint] NULL,
[ValueDecimal] [decimal](18, 2) NULL,
[ValueDate] [datetime] NULL,
[TypeOfData] [nvarchar](20) NOT NULL,
CONSTRAINT [PK_table1] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_table1_productskeletonid] ON [dbo].[table1]
(
[ProductSkeletonID] ASC
)
INCLUDE ( [ProductID],
[Value]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE TABLE [dbo].[table2](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ProductID] [bigint] NOT NULL,
[ProductSkeletonID] [bigint] NOT NULL,
[Value] [nvarchar](800) NOT NULL,
[IsKeywordSearchable] [bit] NULL,
[ValueInteger] [bigint] NULL,
[ValueDecimal] [decimal](18, 2) NULL,
[ValueDate] [datetime] NULL,
[TypeOfData] [nvarchar](20) NOT NULL,
CONSTRAINT [PK_table2] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_table2_productskeletonid] ON [dbo].[table2]
(
[ProductSkeletonID] ASC
)
INCLUDE ( [ProductID],
[Value]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
CREATE TABLE [dbo].[table_results](
[SearchID] [bigint] NOT NULL,
[RowNbr] [int] NOT NULL,
[ProductID] [bigint] NOT NULL,
[PermissionList] [varchar](250) NULL,
[SearchWeight] [int] NULL,
CONSTRAINT [PK_table_results] PRIMARY KEY NONCLUSTERED
(
[SearchID] ASC,
[RowNbr] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE NONCLUSTERED INDEX [IX_table_results_SearchID] ON [dbo].[cart_product_searches_results]
(
[SearchID] ASC
)
INCLUDE ( [ProductID]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
===== Tabela 1 kwerenda ======
SELECT cppev.ProductSkeletonID, cppev.Value, COUNT(*) AS Value FROM table1 cppev
JOIN search_results cpsr ON cppev.ProductID = cpsr.ProductID AND cpsr.SearchID = 227568
WHERE cppev.ProductSkeletonID in (3191, 3160, 3158, 3201)
GROUP BY cppev.ProductSkeletonID, cppev.Value
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table1'. Scan count 4, logical reads 582, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table_results'. Scan count 1, logical reads 82, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 1373 ms, elapsed time = 1576 ms.
|--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(int,[Expr1008],0)))
|--Stream Aggregate(GROUP BY:([cppev].[Value], [cppev].[ProductSkeletonID]) DEFINE:([Expr1008]=Count(*)))
|--Sort(ORDER BY:([cppev].[Value] ASC, [cppev].[ProductSkeletonID] ASC))
|--Hash Match(Inner Join, HASH:([cpsr].[ProductID])=([cppev].[ProductID]), RESIDUAL:([dbo].[table1].[ProductID] as [cppev].[ProductID]=[dbo].[table_results].[ProductID] as [cpsr].[ProductID]))
|--Index Seek(OBJECT:([dbo].[table_results].[IX_table_results_SearchID] AS [cpsr]), SEEK:([cpsr].[SearchID]=(227568)) ORDERED FORWARD)
|--Index Seek(OBJECT:([dbo].[table1].[IX_table1_productskeletonid] AS [cppev]), SEEK:([cppev].[ProductSkeletonID]=(3158) OR [cppev].[ProductSkeletonID]=(3160) OR [cppev].[ProductSkeletonID]=(3191) OR [cppev].[ProductSkeletonID]=(3201)) ORDERED FORWARD)
===== Tabela 2 ======
SELECT cppev.ProductSkeletonID, cppev.Value, COUNT(*) AS Value FROM table2 cppev
JOIN table_results cpsr ON cppev.ProductID = cpsr.ProductID AND cpsr.SearchID = 227568
WHERE cppev.ProductSkeletonID in (3191, 3160, 3158, 3201)
GROUP BY cppev.ProductSkeletonID, cppev.Value
Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table2'. Scan count 4, logical reads 584, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
Table 'table_results'. Scan count 1, logical reads 82, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:
CPU time = 484 ms, elapsed time = 796 ms.
|--Compute Scalar(DEFINE:([Expr1005]=CONVERT_IMPLICIT(int,[Expr1008],0)))
|--Stream Aggregate(GROUP BY:([cppev].[Value], [cppev].[ProductSkeletonID]) DEFINE:([Expr1008]=Count(*)))
|--Sort(ORDER BY:([cppev].[Value] ASC, [cppev].[ProductSkeletonID] ASC))
|--Hash Match(Inner Join, HASH:([cpsr].[ProductID])=([cppev].[ProductID]), RESIDUAL:([auctori_core_v40_D].[dbo].[table2].[ProductID] as [cppev].[ProductID]= [dbo].[table2].[ProductID] as [cpsr].[ProductID]))
|--Index Seek(OBJECT:([dbo].[table_results].[IX_table_results_SearchID] AS [cpsr]), SEEK:([cpsr].[SearchID]=(227568)) ORDERED FORWARD)
|--Index Seek(OBJECT:([dbo].[table2].[IX_table2_productskeletonid] AS [cppev]), SEEK:([cppev].[ProductSkeletonID]=(3158) OR [cppev].[ProductSkeletonID]=(3160) OR [cppev].[ProductSkeletonID]=(3191) OR [cppev].[ProductSkeletonID]=(3201)) ORDERED FORWARD)