Döküntüleri varchar nedeniyle tempdb'ye göre sıralayın (maks.)


10

32 GB'lık bir sunucuda maksimum 25 GB belleğe sahip SQL Server 2014 SP2 çalıştırıyoruz, iki tablonuz var, burada her iki tablonun basitleştirilmiş bir yapısını bulacaksınız:

CREATE TABLE [dbo].[Settings](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceId] [int] NULL,
    [typeID] [int] NULL,
    [remark] [varchar](max) NULL,
    CONSTRAINT [PK_Settings] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO

CREATE TABLE [dbo].[Resources](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [resourceUID] [int] NULL,
 CONSTRAINT [PK_Resources] PRIMARY KEY CLUSTERED ([id] ASC)
) ON [PRIMARY]
GO

aşağıdaki kümelenmemiş dizinlerle:

CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
    [resourceUID] ASC
)

CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
    [resourceId] ASC,
    [typeID] ASC
)

Veritabanı compatibility level120 ile yapılandırılmıştır .

Bu sorguyu çalıştırdığımda dökülmeler var tempdb. Sorguyu şu şekilde yürütüyorum:

exec sp_executesql N'
select r.id,remark
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38

[remark]Alanı seçmezseniz dökülme olmaz. İlk tepkim, dökülmelerin, iç içe döngü işlecindeki tahmini tahmini satır sayısının düşük olması nedeniyle meydana gelmesiydi.

Bu yüzden ayarlar tablosuna 5 datetime ve 5 tamsayı sütunu ekleyip bunları select deyimime ekliyorum. Sorguyu yürüttüğümde hiçbir dökülme olmuyor.

Döküntüler neden sadece [remark]seçildiğinde oluyor ? Muhtemelen bunun bir olduğu gerçeği ile ilgilidir varchar(max). Dökülmekten kaçınmak için ne yapabilirim tempdb?

OPTION (RECOMPILE)Sorgu eklemek fark etmez.


Deneyebilirsiniz select r.id, LEFT(remark, 512)(veya mantıklı alt dize uzunluğu ne olursa olsun).
1919'da mustaccio

@Forrest: Sorunu simüle etmek için gerekli verileri yeniden oluşturmaya çalışıyorum. İlk bakışta, iç içe döngünün düşük tahmini ile ilgilidir. Kukla verilerimde, tahmini satır sayısı çok daha fazla ve dökülme
yaşanmıyor

Yanıtlar:


10

Burada birkaç olası çözüm bulunacaktır.

Şunları yapabilirsiniz elle ayarlamak Herhalde o yoldan gitmez, buna rağmen bellek verme.

Maksimum uzunluk sütununu yakalamadan önce sıralamayı daha aşağıya itmek için bir CTE ve TOP kullanabilirsiniz. Aşağıdaki gibi görünecektir.

WITH CTE AS (
SELECT TOP 1000000000 r.ID, s.ID AS ID2, s.typeID
FROM Resources r
inner join Settings s on resourceid=r.id
where resourceUID=@UID
ORDER BY s.typeID
)
SELECT c.ID, ca.remark
FROM CTE c
CROSS APPLY (SELECT remark FROM dbo.Settings s WHERE s.id = c.ID2) ca(remark)
ORDER BY c.typeID

Kavram kanıtı dbfiddle burada . Örnek veriler yine de takdir edilecektir!

Paul White'ın mükemmel bir analizini okumak istiyorsanız, burada okuyun.


7

Döküntüler neden sadece [açıklama] seçildiğinde oluyor?

Dökülme, bu sütunu eklediğinizde gerçekleşiyor çünkü sıralanan büyük dize verileri için yeterince büyük bir bellek yardımı alamıyorsunuz.

Yeterince büyük bir bellek yardımı alamazsınız çünkü gerçek satır sayısı tahmini satır sayısından 10 kat daha fazladır (1.302 gerçek vs 126 tahmini).

Tahmin neden kapalı? Neden SQL Server tek satır bir ile dbo.Settings orada sanıyor resourceid38?

Çalıştırarak kontrol edebileceğiniz DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')ve bu histogram adımı için sayıları görebileceğiniz bir istatistik sorunu olabilir . Ancak uygulama planı, istatistiklerin olabildiğince eksiksiz ve güncel olduğunu gösteriyor.

İstatistikler yardımcı olmadığından, en iyi bahsiniz muhtemelen Forrest'in cevabında yer aldığı bir sorgu yeniden yazma işlemidir .


3

Bana göre where, sorgudaki tümcenin sorunu oluşturduğu ve OPTION(RECOMPILE)kullanılsa bile düşük tahminlerin nedeni olduğu anlaşılıyor .

Bazı test verileri oluşturdum ve sonunda IDalanı resourcesbir değişken (her zaman benzersizse) veya bir geçici tablodan, birden fazla var ise depolayabilen iki çözüm ortaya çıktı ID.

Temel test kayıtları

SET NOCOUNT ON
DECLARE @i int= 1;
WHILE @i <= 10000
BEGIN
INSERT INTO [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(@i,@i,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here'); -- 23254 character length on each value
INSERT INTO  [dbo].[Resources](resourceUID)
VALUES(@i);
SET @i += 1;
END

OP ile aynı yaklaşık sonuç kümesine ulaşmak için 'Seek' değerlerini girin (1300 kayıt)

INSERT INTO  [dbo].[Settings]([resourceId],[typeID],remark)
VALUES(38,38,'KEPT THESE VALUES OUT BECAUSE IT WOULD CLUTTER THE EXAMPLES, VALUES OVER 8000 Chars entered here')
GO 1300

Uyumluluk ve istatistikleri istatistikleri OP ile eşleşecek şekilde değiştirin

ALTER DATABASE StackOverflow SET COMPATIBILITY_LEVEL = 120;
UPDATE STATISTICS settings WITH FULLSCAN;
UPDATE STATISTICS resources WITH FULLSCAN;

Orijinal sorgu

exec sp_executesql N'
select r.id
FROM Resources r
inner join Settings on resourceid=r.id
where resourceUID=@UID
ORDER BY typeID',
N'@UID int',
@UID=38

Tahminlerim daha da kötüleşiyor , tahmin edilen bir satırla 1300 döndürülür. Ve OP'nin belirttiği gibi, ekleyip eklemediğim önemli değilOPTION(RECOMPILE)

Dikkat edilmesi gereken önemli bir nokta, nerede cümlesinden kurtulduğumuzda, tahminlerin% 100 doğru olduğu, bu da her iki tabloda da tüm verileri kullandığımızdan beklenen.

Dizinleri sadece önceki sorgudakiyle aynı kullandığımızdan emin olmak için zorladım, noktayı kanıtlamak için

exec sp_executesql N'
select r.id,remark
FROM Resources r with(index([IX_UID]))
inner join Settings WITH(INDEX([IX_Test])) 
on resourceid=r.id
ORDER BY typeID',
N'@UID int',
@UID=38

Beklendiği gibi, iyi tahminler.

Öyleyse, daha iyi tahminler almak için yine de değerlerimizi araştırmak için neyi değiştirebiliriz?

@UID benzersizse, OP örneğinde idolduğu gibi, döndürülen tek resourcesdeğişkeni bir değişkene koyabilir ve ardından bir SEÇENEK (RECOMPILE) ile bu değişkeni arayabiliriz

DECLARE @UID int =38 , @RID int;
SELECT @RID=r.id from 
Resources r where resourceUID = @UID;

SELECT @uid, remark 
from Settings 
where resourceId = @uid 
Order by typeID
OPTION(RECOMPILE);

Bu da% 100 doğru tahminler verir

Ancak, kaynaklarda birden çok resourceUID varsa ne olur?

biraz test verisi ekle

INSERT INTO Resources(ResourceUID)
VALUES (38);
go 50

Bu geçici bir tablo ile çözülebilir

CREATE TABLE #RID (id int)
DECLARE @UID int =38 
INSERT INTO #RID
SELECT r.id 
from 
Resources r where resourceUID = @UID

SELECT @uid, remark 
from Settings  s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)

DROP TABLE #RID

Yine doğru tahminlerle .

Bu kendi veri setim YMMV ile yapıldı.


Sp_executesql ile yazıldı

Değişken ile

exec sp_executesql N'
DECLARE  @RID int;
    SELECT @RID=r.id from 
    Resources r where resourceUID = @UID;

    SELECT @uid, remark 
    from Settings 
    where resourceId = @uid 
    Order by typeID
    OPTION(RECOMPILE);',
N'@UID int',
@UID=38

Geçici masa ile

exec sp_executesql N'

CREATE TABLE #RID (id int)

INSERT INTO #RID
SELECT r.id 
from 
Resources r where resourceUID = @UID

SELECT @uid, remark 
from Settings  s
INNER JOIN #RID r
ON r.id =s.resourceId
Order by typeID
OPTION(RECOMPILE)

DROP TABLE #RID',
N'@UID int',
@UID=38

Testimde hala% 100 doğru tahminler

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.