Paket eksekusi menunjukkan operasi CONVERT_IMPLICIT yang mahal. Bisakah saya memperbaikinya dengan pengindeksan atau apakah saya perlu mengubah tabel?

24

Saya memiliki pandangan yang sangat penting, sangat lambat yang mencakup beberapa kondisi yang sangat buruk seperti ini di mana klausa. Saya juga sadar bahwa sambungannya kasar dan lambatvarchar(13) bukan bidang identitas bilangan bulat, tetapi ingin meningkatkan kueri sederhana di bawah ini yang menggunakan tampilan ini:

CREATE VIEW [dbo].[vwReallySlowView]  AS  
AS  
SELECT     
  I.booking_no_v32 AS bkno, 
  I.trans_type_v41 AS trantype, 
  B.Assigned_to_v61 AS Assignbk, 
  B.order_date AS dateo, B.HourBooked AS HBooked,   
  B.MinBooked AS MBooked, B.SecBooked AS SBooked, 
  I.prep_on AS Pon, I.From_locn AS Flocn, 
  I.Trans_to_locn AS TTlocn,   
                      (CASE I.prep_on WHEN 'Y' THEN I.PDate ELSE I.FirstDate END) AS PrDate, I.PTimeH AS PrTimeH, I.PTimeM AS PrTimeM,   
                      (CASE WHEN I.RetnDate < I.FirstDate THEN I.FirstDate ELSE I.RetnDate END) AS RDatev, I.bit_field_v41 AS bitField, I.FirstDate AS FDatev, I.BookDate AS DBooked,   
                      I.TimeBookedH AS TBookH, I.TimeBookedM AS TBookM, I.TimeBookedS AS TBookS, I.del_time_hour AS dth, I.del_time_min AS dtm, I.return_to_locn AS rtlocn,   
                      I.return_time_hour AS rth, I.return_time_min AS rtm, (CASE WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty < I.QtyCheckedOut)   
                      THEN 0 WHEN I.Trans_type_v41 IN (6, 7) AND (I.Trans_qty >= I.QtyCheckedOut) THEN I.Trans_Qty - I.QtyCheckedOut ELSE I.trans_qty END) AS trqty,   
                      (CASE WHEN I.Trans_type_v41 IN (6, 7) THEN 0 ELSE I.QtyCheckedOut END) AS MyQtycheckedout, (CASE WHEN I.Trans_type_v41 IN (6, 7)   
                      THEN 0 ELSE I.QtyReturned END) AS retqty, I.ID, B.BookingProgressStatus AS bkProg, I.product_code_v42, I.return_to_locn, I.AssignTo, I.AssignType,   
                      I.QtyReserved, B.DeprepOn,  
        (CASE  B.DeprepOn       
        WHEN 1 THEN  B.DeprepDateTime     
        ELSE   I.RetnDate  
           END)  AS DeprepDateTime, I.InRack 
FROM         dbo.tblItemtran AS I 

INNER JOIN  -- booking_no = varchar(13)
         dbo.tblbookings AS B ON B.booking_no = I.booking_no_v32  --  string inner-join

INNER JOIN  -- product_code = varchar(13) 
        dbo.tblInvmas AS M ON I.product_code_v42 = M.product_code  --  string inner-join

WHERE     (I.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)) AND (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) OR  
                      (I.trans_type_v41 NOT IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (B.BookingProgressStatus = 1) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut = 0) OR  
                      (I.trans_type_v41 IN (6, 7)) AND (I.bit_field_v41 & 4 = 0) AND (I.QtyCheckedOut > 0) AND (I.trans_qty - (I.QtyCheckedOut - I.QtyReturned) > 0)  

Tampilan ini biasanya digunakan seperti ini:

select * from vwReallySlowView
where product_code_v42  = 'LIGHTBULB100W'  -- find "100 watt lightbulb" rows

Ketika saya menjalankannya saya mendapatkan item rencana eksekusi ini biaya 20 hingga 80% dari total biaya batch, dengan predikat CONVERT_IMPLICIT( .... &(4)) menunjukkan bahwa tampaknya sangat lambat dalam melakukan inibitwise boolean tests seperti(I.ibitfield & 4 = 0) .

Saya bukan ahli dalam MS SQL atau pada tipe DBA bekerja secara umum karena saya sebagian besar pengembang perangkat lunak non-SQL. Tetapi saya menduga bahwa kombinasi bitwise seperti itu adalah ide yang buruk, dan akan lebih baik jika memiliki bidang boolean yang terpisah.

Bisakah saya entah bagaimana meningkatkan indeks yang saya miliki ini, untuk menangani pandangan ini dengan lebih baik tanpa mengubah skema (yang sudah diproduksi di ribuan lokasi) atau haruskah saya mengubah tabel dasar yang memiliki beberapa nilai boolean yang dikemas menjadi bilangan bulat bit_field_v41, untuk memperbaiki masalah ini ?

Inilah Indeks Clustered saya tblItemtranyang sedang dipindai dalam rencana eksekusi ini:

-- goal:  speed up  select * from vwReallySlowView where productcode  = 'X'
CREATE CLUSTERED INDEX [idxtblItemTranProductCodeAndTransType] ON [dbo].[tblItemtran] 
(
    [product_code_v42] ASC,  -- varchar(13)
    [trans_type_v41] ASC     -- int
)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]

Berikut adalah rencana eksekusi, untuk salah satu produk lain yang menghasilkan biaya 27% pada CONVERT_IMPLICITpredikat ini . pembaruan Perhatikan bahwa dalam kasus ini, simpul terburuk saya sekarang adalah "hash match" pada inner join, yang biayanya 34% Saya percaya ini adalah biaya yang tidak dapat saya hindari kecuali saya bisa menghindari melakukan penggabungan pada string yang saat ini saya tidak bisa hindari. Singkirkan itu. KeduanyaINNER JOIN operasi dalam tampilan di atas aktifvarchar(13) bidang.

Memperbesar di sudut kanan bawah:

masukkan deskripsi gambar di sini

Seluruh paket eksekusi sebagai .sqlplan tersedia di skydrive. Gambar ini hanyalah gambaran visual. Klik di sini untuk melihat gambar dengan sendirinya.

masukkan deskripsi gambar di sini

Perbarui diposting seluruh rencana eksekusi. Saya sepertinya tidak dapat menemukan product_codenilai yang buruk secara patologis, tetapi salah satu cara untuk melakukannya adalah dengan select count(*) from viewtidak melakukan satu produk. Tetapi produk yang hanya digunakan dalam 5% dari catatan dalam tabel yang mendasari atau kurang tampaknya menunjukkan biaya yang jauh lebih rendah dalam CONVERT_IMPLICIT operasi. Jika saya akan memperbaiki SQL di sini, saya pikir saya akan mengambil WHEREklausa kotor dalam tampilan, dan menghitung dan menyimpan hasil raksasa itu di mana klausa-kondisi sebagai bidang bit "IncludeMeInTheView", di tabel yang mendasarinya . Presto, masalah terpecahkan, bukan?

Warren P
sumber
.SQLPLAN yang saya posting jauh lebih sedikit patologis daripada kasus 432k / 17k pada gambar asli. Saya minta maaf saya tidak bisa lagi menemukan product_codenilai yang saya gunakan untuk menghasilkan 87%kasus patologis dengan data saya. Gambar sekarang ditampilkan 27%. Sekali lagi, permintaan maaf untuk kebingungan karena suntingan saya.
Warren P

Jawaban:

49

Anda seharusnya tidak terlalu mengandalkan persentase biaya dalam rencana pelaksanaan. Ini selalu merupakan perkiraan biaya , bahkan dalam rencana pasca-eksekusi dengan angka 'aktual' untuk hal-hal seperti jumlah baris. Perkiraan biaya didasarkan pada model yang bekerja dengan sangat baik untuk tujuan yang dimaksudkan: memungkinkan pengoptimal untuk memilih antara berbagai rencana pelaksanaan kandidat untuk permintaan yang sama. Informasi biaya menarik, dan faktor yang perlu dipertimbangkan, tetapi seharusnya jarang menjadi metrik utama untuk penyetelan kueri. Menafsirkan informasi rencana eksekusi memerlukan pandangan yang lebih luas dari data yang disajikan.

Operator Pencarian Indeks ItemTran Clustered

Indeks ItemTran Clustered Seek

Operator ini benar-benar dua operasi dalam satu. Pertama operasi pencarian indeks menemukan semua baris yang cocok dengan predikat product_code_v42 = 'M10BOLT', maka setiap baris memiliki predikat residual yang bit_field_v41 & 4 = 0diterapkan. Ada konversi implisit dari bit_field_v41tipe dasar ( tinyintatau smallint) keinteger .

Konversi terjadi karena operator bitwise-AND (&) mengharuskan kedua operan memiliki tipe yang sama. Tipe implisit dari nilai konstan '4' adalah bilangan bulat dan aturan prioritas tipe data berarti bit_field_v41nilai bidang prioritas rendah dikonversi.

Masalahnya (seperti itu) mudah diperbaiki dengan menulis predikat sebagai bit_field_v41 & CONVERT(tinyint, 4) = 0- yang berarti nilai konstan memiliki prioritas lebih rendah dan dikonversi (selama pelipatan konstan) daripada nilai kolom. Jika bit_field_v41ini tinyinttidak ada konversi terjadi sama sekali. Demikian juga, CONVERT(smallint, 4)bisa digunakan kalau bit_field_v41ada smallint. Yang mengatakan, konversi bukan masalah kinerja dalam kasus ini, tetapi masih merupakan praktik yang baik untuk mencocokkan jenis dan menghindari konversi tersirat jika memungkinkan.

Bagian utama dari perkiraan biaya pencarian ini adalah ke ukuran tabel dasar. Meskipun kunci indeks yang dikelompokkan itu sendiri cukup sempit, ukuran setiap baris besar. Definisi untuk tabel tidak diberikan, tetapi hanya kolom yang digunakan dalam tampilan menambahkan hingga lebar baris yang signifikan. Karena indeks berkerumun mencakup semua kolom, jarak antara kunci indeks berkerumun adalah lebar baris , bukan lebar kunci indeks . Penggunaan sufiks versi pada beberapa kolom menunjukkan bahwa tabel sebenarnya memiliki lebih banyak kolom untuk versi sebelumnya.

Melihat kolom seek, predicate residual, dan output, kinerja operator ini dapat diperiksa secara terpisah dengan membangun kueri yang setara ( 1 <> 2ini adalah trik untuk mencegah parameterisasi otomatis, kontradiksi dihapus oleh pengoptimal dan tidak muncul dalam rencana permintaan):

SELECT
    it.booking_no_v32,
    it.QtyCheckedOut,
    it.QtyReturned,
    it.Trans_qty,
    it.trans_type_v41
FROM dbo.tblItemTran AS it
WHERE
    1 <> 2
    AND it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0;

Kinerja kueri ini dengan cache data dingin sangat menarik, karena read-ahead akan dipengaruhi oleh fragmentasi tabel (indeks berkerumun). Kunci pengelompokan untuk tabel ini mengundang fragmentasi, sehingga penting untuk memelihara (mengatur ulang atau membangun kembali) indeks ini secara teratur, dan menggunakan yang sesuai FILLFACTORuntuk memberikan ruang bagi baris baru di antara jendela pemeliharaan indeks.

Saya melakukan tes efek fragmentasi pada baca-depan menggunakan data sampel yang dihasilkan menggunakan SQL Data Generator . Menggunakan jumlah baris tabel yang sama seperti yang ditunjukkan dalam rencana kueri pertanyaan, indeks cluster yang sangat terfragmentasi menghasilkan SELECT * FROM viewwaktu 15 detik setelahDBCC DROPCLEANBUFFERS . Tes yang sama dalam kondisi yang sama dengan indeks berkerumun yang baru dibangun kembali pada tabel ItemTrans diselesaikan dalam 3 detik.

Jika data tabel biasanya seluruhnya dalam cache, masalah fragmentasi sangat kurang penting. Tetapi, bahkan dengan fragmentasi rendah, baris tabel lebar mungkin berarti jumlah bacaan logis dan fisik jauh lebih tinggi dari yang mungkin diharapkan. Anda juga dapat bereksperimen dengan menambahkan dan menghapus eksplisit CONVERTuntuk memvalidasi harapan saya bahwa masalah konversi implisit tidak penting di sini, kecuali sebagai pelanggaran praktik terbaik.

Lebih penting lagi adalah perkiraan jumlah baris yang meninggalkan operator pencarian. Perkiraan waktu optimasi adalah 165 baris, tetapi 4.226 diproduksi pada waktu eksekusi. Saya akan kembali ke titik ini nanti, tetapi alasan utama untuk perbedaan ini adalah bahwa selektivitas predikat residual (yang melibatkan bitwise-AND) sangat sulit untuk diprediksi oleh pengoptimal - pada kenyataannya ia perlu menebak.

Operator Penyaring

Operator Penyaring

Saya menunjukkan predikat filter di sini sebagian besar untuk menggambarkan bagaimana kedua NOT INdaftar digabungkan, disederhanakan dan kemudian diperluas, dan juga untuk memberikan referensi untuk diskusi pertandingan hash berikut. Permintaan tes dari pencarian dapat diperluas untuk memasukkan efeknya dan menentukan efek dari operator Filter pada kinerja:

SELECT
    it.booking_no_v32,
    it.trans_type_v41,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut
FROM dbo.tblItemTran AS it
WHERE
    it.product_code_v42 = 'M10BOLT'
    AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    AND
    (
        (
            it.trans_type_v41 NOT IN (2, 3, 6, 7, 18, 19, 20, 21, 12, 13, 22)
            AND it.trans_type_v41 NOT IN (6, 7)
        )
        OR
        (
            it.trans_type_v41 NOT IN (6, 7)
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut = 0
        )
        OR 
        (
            it.trans_type_v41 IN (6, 7)
            AND it.QtyCheckedOut > 0
            AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
        )
    );

Operator Hitung Skalar dalam paket mendefinisikan ekspresi berikut (perhitungan itu sendiri ditangguhkan hingga hasilnya diperlukan oleh operator yang lebih baru):

[Expr1016] = (trans_qty - (QtyCheckedOut - QtyReturned))

Operator Pencocokan Hash

Melakukan penggabungan pada tipe data karakter bukan alasan tingginya estimasi biaya operator ini. Tip alat SSMS hanya menampilkan entri Hash Keys Probe, tetapi detail penting ada di jendela Properti SSMS.

Operator Pencocokan Hash membangun tabel hash menggunakan nilai-nilai booking_no_v32kolom (Hash Keys Build) dari tabel ItemTran, dan kemudian mencari kecocokan menggunakan booking_nokolom (Hash Keys Probe) dari tabel Pemesanan. Tooltip SSMS juga biasanya menampilkan Probe Residual, tetapi teksnya terlalu panjang untuk tooltip dan dihilangkan begitu saja.

Sisa Probe mirip dengan Sisa yang terlihat setelah indeks mencari sebelumnya; predikat residual dievaluasi pada semua baris yang cocok hash untuk menentukan apakah baris harus diteruskan ke operator induk. Menemukan kecocokan hash di tabel hash yang seimbang sangat cepat, tetapi menerapkan predikat residu yang kompleks untuk setiap baris yang cocok cukup lambat jika dibandingkan. Tooltip Pencocokan Hash di Plan Explorer menunjukkan detail, termasuk ekspresi Probe Residual:

Operator Pencocokan Hash

Predikat residual rumit dan termasuk status kemajuan pemesanan sekarang kolom tersedia dari tabel pemesanan. Tip alat juga menunjukkan perbedaan yang sama antara taksiran dan jumlah baris aktual yang terlihat sebelumnya dalam pencarian indeks. Mungkin aneh bahwa sebagian besar penyaringan dilakukan dua kali, tetapi ini hanya optimis yang optimis. Itu tidak mengharapkan bagian-bagian dari filter yang dapat menekan rencana dari residu probe untuk menghilangkan baris (perkiraan jumlah baris adalah sama sebelum dan setelah filter) tetapi pengoptimal tahu itu mungkin salah tentang hal itu. Peluang untuk memfilter baris lebih awal (mengurangi biaya hash join) sepadan dengan biaya yang kecil dari filter tambahan. Keseluruhan filter tidak dapat ditekan karena mencakup tes pada kolom dari tabel pemesanan, tetapi sebagian besar bisa.

Penghitungan baris meremehkan adalah masalah bagi operator Pencocokan Hash karena jumlah memori yang dicadangkan untuk tabel hash didasarkan pada perkiraan jumlah baris. Di mana memori terlalu kecil untuk ukuran tabel hash yang diperlukan pada waktu berjalan (karena jumlah baris yang lebih besar), tabel hash secara rekursi tumpah ke penyimpanan tempdb fisik , seringkali mengakibatkan kinerja yang sangat buruk. Dalam kasus terburuk, mesin eksekusi berhenti menumpahkan ember dan resor hash secara rekursif ke a sangat lambatalgoritma bailout. Tumpahan hash (rekursif atau bailout) adalah penyebab paling mungkin dari masalah kinerja yang diuraikan dalam pertanyaan (bukan kolom tipe karakter bergabung atau konversi tersirat). Penyebab utama adalah server menyimpan terlalu sedikit memori untuk kueri berdasarkan estimasi jumlah baris (kardinalitas) yang salah.

Sayangnya, sebelum SQL Server 2012, tidak ada indikasi dalam rencana eksekusi bahwa operasi hashing melebihi alokasi memorinya (yang tidak dapat tumbuh secara dinamis setelah dipesan sebelum eksekusi dimulai, bahkan jika server memiliki banyak memori bebas) dan harus tumpah ke tempdb. Dimungkinkan untuk memantau Kelas Acara Peringatan Hash menggunakan Profiler, tetapi mungkin sulit untuk menghubungkan peringatan dengan permintaan tertentu.

Memperbaiki masalah

Ketiga masalah tersebut adalah fragmentasi, residu probe kompleks pada operator hash match, dan estimasi kardinalitas yang salah dihasilkan dari tebakan pada pencarian indeks.

Solusi yang disarankan

Periksa fragmentasi dan perbaiki jika perlu, penjadwalan pemeliharaan untuk memastikan indeks tetap terorganisir. Cara biasa untuk mengoreksi perkiraan kardinalitas adalah dengan memberikan statistik. Dalam hal ini, pengoptimal membutuhkan statistik untuk kombinasi ( product_code_v42, bitfield_v41 & 4 = 0). Kami tidak dapat membuat statistik pada ekspresi secara langsung, jadi pertama-tama kami harus membuat kolom yang dikomputasi untuk ekspresi bidang bit, dan kemudian membuat statistik multi-kolom manual:

ALTER TABLE dbo.tblItemTran
ADD Bit3 AS bit_field_v41 & CONVERT(tinyint, 4);

CREATE STATISTICS [stats dbo.ItemTran (product_code_v42, Bit3)]
ON dbo.tblItemTran (product_code_v42, Bit3);

Definisi teks kolom yang dihitung harus sesuai dengan teks dalam definisi tampilan cukup tepat untuk statistik yang akan digunakan, jadi mengoreksi tampilan untuk menghilangkan konversi implisit harus dilakukan pada saat yang sama, dan hati-hati untuk memastikan kecocokan tekstual.

Statistik multi-kolom seharusnya menghasilkan perkiraan yang jauh lebih baik, sangat mengurangi kemungkinan bahwa operator hash match akan menggunakan penumpahan rekursif atau algoritma bailout. Menambahkan kolom yang dikomputasi (yang merupakan operasi metadata saja, dan tidak memakan ruang dalam tabel karena tidak ditandai PERSISTED) dan statistik multi-kolom adalah tebakan terbaik saya pada solusi pertama.

Saat memecahkan masalah kinerja permintaan, penting untuk mengukur hal-hal seperti waktu yang berlalu, penggunaan CPU, pembacaan logis, pembacaan fisik, jenis dan durasi tunggu ... dan seterusnya. Ini juga dapat berguna untuk menjalankan bagian dari kueri secara terpisah untuk memvalidasi dugaan penyebab seperti yang ditunjukkan di atas.

Di beberapa lingkungan, di mana tampilan data yang up-to-the-second tidak penting, dapat berguna untuk menjalankan proses latar belakang yang mematerialisasikan seluruh tampilan ke dalam tabel snapshot sesering mungkin. Tabel ini hanya tabel dasar normal dan dapat diindeks untuk permintaan baca tanpa khawatir tentang dampak kinerja pembaruan.

Lihat pengindeksan

Jangan tergoda untuk mengindeks tampilan asli secara langsung. Kinerja baca akan luar biasa cepat (pencarian tunggal pada indeks tampilan) tetapi (dalam hal ini) semua masalah kinerja dalam rencana kueri yang ada akan ditransfer ke kueri yang mengubah salah satu kolom tabel yang direferensikan dalam tampilan. Pertanyaan yang mengubah baris tabel dasar akan berdampak sangat buruk.

Solusi canggih dengan tampilan sebagian terindeks

Ada solusi tampilan indeks parsial untuk kueri khusus ini yang mengoreksi perkiraan kardinalitas dan menghilangkan sisa filter dan probe, tetapi didasarkan pada beberapa asumsi tentang data (kebanyakan dugaan saya di skema) dan memerlukan implementasi pakar, khususnya yang sesuai indeks untuk mendukung rencana pemeliharaan tampilan terindeks. Saya membagikan kode di bawah ini untuk minat, saya tidak menyarankan Anda menerapkannya tanpa analisis dan pengujian yang sangat hati-hati .

-- Indexed view to optimize the main view
CREATE VIEW dbo.V1
WITH SCHEMABINDING
AS
SELECT
    it.ID,
    it.product_code_v42,
    it.trans_type_v41,
    it.booking_no_v32,
    it.Trans_qty,
    it.QtyReturned,
    it.QtyCheckedOut,
    it.QtyReserved,
    it.bit_field_v41,
    it.prep_on,
    it.From_locn,
    it.Trans_to_locn,
    it.PDate,
    it.FirstDate,
    it.PTimeH,
    it.PTimeM,
    it.RetnDate,
    it.BookDate,
    it.TimeBookedH,
    it.TimeBookedM,
    it.TimeBookedS,
    it.del_time_hour,
    it.del_time_min,
    it.return_to_locn,
    it.return_time_hour,
    it.return_time_min,
    it.AssignTo,
    it.AssignType,
    it.InRack
FROM dbo.tblItemTran AS it
JOIN dbo.tblBookings AS tb ON
    tb.booking_no = it.booking_no_v32
WHERE
    (
        it.trans_type_v41 NOT IN (2, 3, 7, 18, 19, 20, 21, 12, 13, 22)
        AND it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
    )
    OR
    (
        it.trans_type_v41 NOT IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND tb.BookingProgressStatus = 1
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut = 0
    )
    OR 
    (
        it.trans_type_v41 IN (6, 7)
        AND it.bit_field_v41 & CONVERT(tinyint, 4) = 0
        AND it.QtyCheckedOut > 0
        AND it.trans_qty - (it.QtyCheckedOut - it.QtyReturned) > 0
    );
GO
CREATE UNIQUE CLUSTERED INDEX cuq ON dbo.V1 (product_code_v42, ID);
GO

Tampilan yang ada diubah untuk menggunakan tampilan yang diindeks di atas:

CREATE VIEW [dbo].[vwReallySlowView2]
AS
SELECT
    I.booking_no_v32 AS bkno,
    I.trans_type_v41 AS trantype,
    B.Assigned_to_v61 AS Assignbk,
    B.order_date AS dateo,
    B.HourBooked AS HBooked,
    B.MinBooked AS MBooked,
    B.SecBooked AS SBooked,
    I.prep_on AS Pon,
    I.From_locn AS Flocn,
    I.Trans_to_locn AS TTlocn,
    CASE I.prep_on 
        WHEN 'Y' THEN I.PDate
        ELSE I.FirstDate
    END AS PrDate,
    I.PTimeH AS PrTimeH,
    I.PTimeM AS PrTimeM,
    CASE
        WHEN I.RetnDate < I.FirstDate 
        THEN I.FirstDate 
        ELSE I.RetnDate
    END AS RDatev,
    I.bit_field_v41 AS bitField,
    I.FirstDate AS FDatev,
    I.BookDate AS DBooked,
    I.TimeBookedH AS TBookH,
    I.TimeBookedM AS TBookM,
    I.TimeBookedS AS TBookS,
    I.del_time_hour AS dth,
    I.del_time_min AS dtm,
    I.return_to_locn AS rtlocn,
    I.return_time_hour AS rth,
    I.return_time_min AS rtm,
    CASE
        WHEN
            I.Trans_type_v41 IN (6, 7) 
            AND I.Trans_qty < I.QtyCheckedOut
            THEN 0 
        WHEN
            I.Trans_type_v41 IN (6, 7)
            AND I.Trans_qty >= I.QtyCheckedOut
            THEN I.Trans_Qty - I.QtyCheckedOut
        ELSE
            I.trans_qty
    END AS trqty,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyCheckedOut
    END AS MyQtycheckedout,
    CASE
        WHEN I.Trans_type_v41 IN (6, 7)
        THEN 0
        ELSE I.QtyReturned
    END AS retqty,
    I.ID,
    B.BookingProgressStatus AS bkProg,
    I.product_code_v42,
    I.return_to_locn,
    I.AssignTo,
    I.AssignType,
    I.QtyReserved,
    B.DeprepOn,
    CASE B.DeprepOn
        WHEN 1 THEN B.DeprepDateTime
        ELSE I.RetnDate
    END AS DeprepDateTime,
    I.InRack
FROM dbo.V1 AS I WITH (NOEXPAND)
JOIN dbo.tblbookings AS B ON
    B.booking_no = I.booking_no_v32
JOIN dbo.tblInvmas AS M ON
    I.product_code_v42 = M.product_code;

Contoh kueri dan rencana eksekusi:

SELECT
    vrsv.*
FROM dbo.vwReallySlowView2 AS vrsv
WHERE vrsv.product_code_v42 = 'M10BOLT';

Rencana eksekusi baru

Dalam rencana baru, pencocokan hash tidak memiliki predikat residual , tidak ada filter kompleks , tidak ada predikat residual pada pencarian tampilan yang diindeks, dan perkiraan kardinalitas tepat.

Sebagai contoh bagaimana pengaruh sisipkan / perbarui / hapus rencana, ini adalah rencana penyisipan ke tabel ItemTrans:

Masukkan rencana

Bagian yang disorot adalah baru dan diperlukan untuk pemeliharaan tampilan yang diindeks. Kumparan meja memutar ulang baris tabel dasar yang dimasukkan untuk pemeliharaan tampilan terindeks. Setiap baris digabungkan ke tabel pemesanan menggunakan pencarian indeks berkerumun, kemudian filter menerapkan kompleksWHERE predikat klausa untuk melihat apakah baris perlu ditambahkan ke tampilan. Jika demikian, penyisipan dilakukan ke indeks pengelompokan tampilan.

Sama SELECT * FROM viewTes yang dilakukan sebelumnya diselesaikan dalam 150 ms dengan tampilan indeks di tempat.

Hal terakhir: Saya perhatikan server 2008 R2 Anda masih di RTM. Itu tidak akan memperbaiki masalah kinerja Anda, tetapi Paket Layanan 2 untuk 2008 R2 telah tersedia sejak Juli 2012, dan ada banyak alasan bagus untuk tetap serahasia mungkin dengan paket layanan.

Paul White mengatakan GoFundMonica
sumber