Pada server dengan 32GB kami menjalankan SQL Server 2014 SP2 dengan memori maksimal 25GB kami memiliki dua tabel, di sini Anda menemukan struktur yang disederhanakan dari kedua tabel:
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
dengan indeks non-cluster berikut:
CREATE NONCLUSTERED INDEX [IX_UID] ON [dbo].[Resources]
(
[resourceUID] ASC
)
CREATE NONCLUSTERED INDEX [IX_Test] ON [dbo].[Settings]
(
[resourceId] ASC,
[typeID] ASC
)
Basis data dikonfigurasikan dengan compatibility level
120.
Ketika saya menjalankan kueri ini ada tumpahan ke tempdb
. Inilah cara saya menjalankan kueri:
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
Jika tidak memilih [remark]
bidang, tidak akan terjadi tumpahan. Reaksi pertama saya adalah bahwa tumpahan terjadi karena jumlah baris perkiraan yang rendah pada operator loop bersarang.
Jadi saya menambahkan 5 datetime dan 5 kolom integer ke tabel pengaturan dan menambahkannya ke pernyataan pilih saya. Saat saya mengeksekusi kueri, tidak ada tumpahan yang terjadi.
Mengapa tumpahan hanya terjadi saat [remark]
dipilih? Mungkin ada hubungannya dengan fakta bahwa ini adalah varchar(max)
. Apa yang bisa saya lakukan untuk menghindari tumpah tempdb
?
Menambahkan OPTION (RECOMPILE)
ke kueri tidak membuat perbedaan.
sumber
select r.id, LEFT(remark, 512)
(atau apa pun panjang substring yang masuk akal).Jawaban:
Akan ada beberapa kemungkinan penyelesaian di sini.
Anda dapat secara manual menyesuaikan hibah memori, meskipun saya mungkin tidak akan memilih rute itu.
Anda juga dapat menggunakan CTE dan TOP untuk mendorong sortir lebih rendah, sebelum meraih kolom panjang maks. Akan terlihat seperti di bawah ini.
Bukti konsep dbfiddle di sini . Data sampel masih akan dihargai!
Jika Anda ingin membaca analisis yang sangat baik oleh Paul White, baca di sini.
sumber
Tumpahan terjadi ketika Anda memasukkan kolom itu karena Anda tidak mendapatkan hibah memori yang cukup besar untuk data string besar yang diurutkan.
Anda tidak mendapatkan hibah memori yang cukup besar karena jumlah baris aktual adalah 10x lebih banyak dari perkiraan jumlah baris (1.302 aktual vs 126 diperkirakan).
Mengapa taksirannya mati? Mengapa SQL Server berpikir hanya ada satu baris dalam dbo.Settings dengan
resourceid
38?Ini bisa menjadi masalah statistik, yang dapat Anda periksa dengan menjalankan
DBCC SHOW_STATISTICS('dbo.Settings', 'IX_Test')
dan melihat jumlah untuk langkah histogram itu. Tetapi rencana eksekusi tampaknya menunjukkan bahwa statistiknya selengkap dan setanggal mungkin.Karena statistik tidak membantu, taruhan terbaik Anda mungkin adalah penulisan ulang kueri - yang Forrest telah sampaikan dalam jawabannya.
sumber
Bagi saya tampaknya
where
klausa dalam kueri memberikan masalah, dan merupakan penyebab dari estimasi rendah, bahkan jikaOPTION(RECOMPILE)
digunakan.Saya membuat beberapa data uji, dan pada akhirnya muncul dengan dua solusi, menyimpan
ID
bidang dariresources
salah satu variabel (jika selalu unik) atau tabel temp, jika kita dapat memiliki lebih dari satuID
.Catatan uji dasar
Masukkan nilai 'Seek', untuk mendapatkan perkiraan hasil yang sama seperti OP (1300 catatan)
Ubah statistik & Perbarui statistik untuk mencocokkan OP
Permintaan asli
Perkiraan saya bahkan lebih buruk , dengan satu baris perkiraan, sedangkan 1300 dikembalikan. Dan seperti kata OP, tidak masalah jika saya tambahkan
OPTION(RECOMPILE)
Satu hal penting yang perlu diperhatikan, adalah bahwa ketika kita menyingkirkan klausa di mana estimasi adalah 100% benar, yang diharapkan karena kita menggunakan semua data di kedua tabel.
Saya memaksa indeks hanya untuk memastikan kami menggunakan yang sama seperti pada permintaan sebelumnya, untuk membuktikan maksudnya
Seperti yang diharapkan, perkiraan bagus .
Jadi, apa yang bisa kita ubah untuk mendapatkan estimasi yang lebih baik tetapi masih mencari nilai-nilai kita?
JIKA @UID unik, seperti dalam contoh OP memberi, kita bisa menempatkan tunggal
id
yang dikembalikan dariresources
dalam variabel, kemudian mencari variabel itu dengan OPTION (RECOMPILE)Yang memberikan perkiraan akurat 100%
Tetapi bagaimana jika ada banyak sumber daya di dalam sumber daya?
tambahkan beberapa data uji
Ini bisa diselesaikan dengan tabel temp
Lagi dengan perkiraan yang akurat .
Ini dilakukan dengan dataset saya sendiri, YMMV.
Ditulis dengan sp_executesql
Dengan variabel
Dengan meja temp
Masih 100% perkiraan yang benar pada pengujian saya
sumber