Mengapa variabel tabel memaksa pemindaian indeks sementara tabel temp menggunakan pencarian seek dan bookmark?


18

Saya mencoba memahami mengapa menggunakan variabel tabel mencegah pengoptimal menggunakan pencarian indeks dan kemudian bookmark lookup versus scan indeks.

Mengisi tabel:

CREATE TABLE dbo.Test 
(
    RowKey INT NOT NULL PRIMARY KEY, 
    SecondColumn CHAR(1) NOT NULL DEFAULT 'x',
    ForeignKey INT NOT NULL 
) 

INSERT dbo.Test 
(
    RowKey, 
    ForeignKey
) 
SELECT TOP 1000000 
    ROW_NUMBER() OVER (ORDER BY (SELECT 0)),
    ABS(CHECKSUM(NEWID()) % 10)     
FROM sys.all_objects s1
CROSS JOIN sys.all_objects s2 

CREATE INDEX ix_Test_1 ON dbo.Test (ForeignKey) 

Mengisi variabel tabel dengan catatan tunggal dan mencoba untuk mencari kunci utama dan kolom kedua dengan mencari di kolom kunci asing:

DECLARE @Keys TABLE (RowKey INT NOT NULL) 

INSERT @Keys (RowKey) VALUES (10)

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey

Di bawah ini adalah rencana eksekusi:

masukkan deskripsi gambar di sini

Sekarang kueri yang sama menggunakan tabel temp sebagai gantinya:

CREATE TABLE #Keys (RowKey INT NOT NULL) 

INSERT #Keys (RowKey) VALUES (10) 

SELECT 
    t.RowKey,
    t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    #Keys k
ON
    t.ForeignKey = k.RowKey

Paket permintaan ini menggunakan pencarian pencarian dan penanda:

masukkan deskripsi gambar di sini

Mengapa pengoptimal bersedia melakukan pencarian bookmark dengan tabel temp, tetapi tidak variabel tabel?

Variabel tabel digunakan dalam contoh ini untuk mewakili data yang datang melalui tipe tabel yang ditentukan pengguna dalam prosedur tersimpan.

Saya menyadari pencarian indeks mungkin tidak sesuai jika nilai kunci asing terjadi ratusan ribu kali. Dalam hal ini, pemindaian mungkin akan menjadi pilihan yang lebih baik. Untuk skenario yang saya buat, tidak ada baris dengan nilai 10. Saya masih berpikir perilaku itu menarik dan ingin tahu apakah ada alasan untuk itu.

SQL Fiddle

Menambahkan OPTION (RECOMPILE)tidak mengubah perilaku. UDDT memiliki kunci utama.

@@VERSION adalah SQL Server 2008 R2 (SP2) - 10.50.4042.0 (X64) (Build 7601: Paket Layanan 1) (Hypervisor)

Jawaban:


15

Alasan untuk perilaku ini adalah bahwa SQL Server tidak dapat menentukan berapa banyak baris akan cocok dengan ForeignKey, karena tidak ada indeks dengan RowKey sebagai kolom utama (itu dapat menyimpulkan ini dari statistik pada tabel #temp, tetapi yang tidak ada untuk variabel tabel / UDTT), sehingga membuat perkiraan 100.000 baris, yang lebih baik ditangani dengan pemindaian daripada pencarian + pencarian. Pada saat SQL Server menyadari hanya ada satu baris, sudah terlambat.

Anda mungkin dapat membangun UDTT Anda secara berbeda; di versi SQL Server yang lebih modern Anda bisa membuat indeks sekunder pada variabel tabel, tetapi sintaks ini tidak tersedia di 2008 R2.

BTW Anda bisa mendapatkan perilaku pencarian (setidaknya dalam uji coba terbatas saya) jika Anda mencoba untuk menghindari bitmap / penyelidikan dengan mengisyaratkan loop bersarang bergabung:

DECLARE @Keys TABLE (RowKey INT PRIMARY KEY); -- can't hurt

INSERT @Keys (RowKey) VALUES (10);

SELECT 
     t.RowKey
    ,t.SecondColumn
FROM
    dbo.Test t 
INNER JOIN 
    @Keys k
ON
    t.ForeignKey = k.RowKey
    OPTION (LOOP JOIN);

Saya belajar trik ini dari Paul White beberapa tahun yang lalu. Tentu saja, Anda harus berhati-hati dalam menempatkan segala jenis petunjuk bergabung dalam kode produksi - ini bisa gagal jika orang membuat perubahan pada objek yang mendasarinya dan jenis sambungan khusus tidak lagi mungkin atau tidak lagi paling optimal.

Untuk kueri yang lebih kompleks, dan ketika Anda pindah ke SQL Server 2012 atau lebih tinggi, mungkin saja flag jejak 2453 dapat membantu. Bendera itu tidak membantu dengan penyatuan sederhana ini. Dan penafian yang sama akan berlaku - ini hanya hal alternatif yang biasanya tidak boleh Anda lakukan tanpa satu ton dokumentasi dan prosedur pengujian regresi yang ketat.

Juga, Paket Layanan 1 lama tidak didukung, Anda harus mendapatkan pada Paket Layanan 3 + MS15-058 .


3

Variabel tabel dan tabel temp ditangani secara berbeda dalam beberapa cara. Ada jawaban yang bagus di sini dengan banyak hal spesifik tentang di mana mereka berbeda.

Khususnya dalam kasus Anda, saya akan menebak bahwa fakta bahwa tabel temp dapat memiliki statistik tambahan yang dihasilkan dan rencana paralel sementara variabel tabel memiliki statistik lebih terbatas (tidak ada statistik tingkat kolom) dan tidak ada rencana paralel adalah penyebab Anda.

Anda mungkin lebih baik membuang variabel tabel ke tabel temp selama durasi prosedur yang disimpan.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.