SQL Server nvarchar (max) ve nvarchar (n) performansı etkiler


16

Bu, SQL Server 2008 R2 SP2'dir. 2 masam var. İlk tablonun bir VALUE sütunu nvarchar(max)ve ikincisinin de aynı sütunu olması dışında her ikisi de aynıdır (veri ve dizin oluşturma) nvarchar(800). Bu sütun kümelenmemiş bir dizine eklenir. Ayrıca her iki tabloda da kümelenmiş bir dizin oluşturdum. Ayrıca dizinleri yeniden inşa ettim. Bu sütundaki maksimum dize uzunluğu 650'dir.

Ben her iki nvarchar(800)tabloya karşı aynı sorguyu çalıştırırsanız sürekli olarak daha hızlı, iki kat daha hızlı. Tabii "varchar" amacını yeniyor gibi görünüyor. Tablo 800.000+ satır içerir. Sorgu yaklaşık 110.000 satıra (planın tahmin ettiği şey) bakmalıdır.

İo istatistiklerine göre lob okuma yok, bu yüzden her şey arka arkaya görünüyor. İki tablo arasında maliyet yüzdesinde küçük bir fark olması ve tahmini satır boyutunun nvarchar(max)(91 bayt - 63 bayt) daha büyük olması dışında, yürütme planları aynıdır . Okuma sayısı da hemen hemen aynı.

Neden fark var?

===== Şema ======

 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]

===== Tablo1 sorgusu ======

    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)

===== Table2 sorgusu ======

    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)

4
Her bir sorgu için sorgular, tablo şeması, örnek veya gösterge verileri ve yürütme planları lütfen. "Sanmıyorum ...", "Kesinlikle yok ..." ile aynı değildir.
Mark Storey-Smith

Hangi SQL Server sürümüne sahipsiniz?
Max Vernon

Nvarchar (max) alanları için sıralı depolama hakkında ayrıntılar için technet.microsoft.com/en-us/library/ms189087(v=SQL.105).aspx adresine bakın . Bu alanlardaki gerçek veriler ne kadar büyük?
Max Vernon

Gönderiyi yukarıdaki geri bildirime hitap edecek şekilde güncelledim.
Brian Bohl

Yanıtlar:


14

Kullanım MAXtürlerinin maliyet yükünü görüyorsunuz .

Da NVARCHAR(MAX)aynıdır NVARCHAR(n)TSQL ve in satır saklanabilir kapalı satır itilebilir, bunun nedeni depolama sistemi tarafından ayrı olarak ele alınır. Sıra dışı olduğunda, LOB_DATAtahsis birimi yerine bir ROW_OVERFLOW_DATAtahsis birimidir ve gözlemlerinizden bunun bir yük taşıdığını varsayabiliriz.

İki türün küçük bir DBCC PAGE spelunking ile dahili olarak farklı şekilde depolandığını görebilirsiniz . Mark Rasmussen farklılıkları göstermek örnek sayfa dökümlerini yayınlanmıştır (MAX) Türleri gibi varchar, Varbinary Vb için LOB Pointer Boyutu nedir?

Muhtemelen durumunuzdaki performans farkına neden olan sütunun GROUP BYüzerinde olduğunu varsayabiliriz MAX. Bir MAXtürdeki diğer işlemleri test etmedim, ancak bunu yapmak ilginç olabilir ve benzer sonuçların görülüp görülmediğini görmek.


Yani düz bir 'ol varchar'a karşı [BLOB Satır İçi Verileri] okumak için ekstra işlem olduğunu mu söylüyorsunuz? Satır gitti eğer önemli yükü bekliyordum, ama tüm bu veriler inline (kullanılan dbcc ind). Ve neden grubun bunu ortaya çıkardığını düşünüyorsun?
Brian Bohl

Okumak için biraz yük, üzerinde herhangi bir hesaplama için çok örn GROUP BY. @ RemusRusanu muhtemelen bir fikir verebilir (umarım ping'i görür).
Mark Storey-Smith

Eşit ve benzeri durumlarda bile aynı davranışı belgeleyen başka bir makale buldum . Nvarchar (max) 'ın daha az verimli bir algoritma kullanıp kullanmadığını merak ediyorum.
Brian Bohl
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.