Saya memiliki tampilan besar yang saya gunakan dari dalam suatu aplikasi. Saya pikir saya sudah mempersempit masalah kinerja saya, tetapi saya tidak yakin bagaimana cara memperbaikinya. Versi tampilan yang disederhanakan terlihat seperti ini:
SELECT ISNULL(SEId + '-' + PEId, '0-0') AS Id,
*,
DATEADD(minute, Duration, EventTime) AS EventEndTime
FROM (
SELECT se.SEId, pe.PEId,
COALESCE(pe.StaffName, se.StaffName) AS StaffName, -- << Problem!
COALESCE(pe.EventTime, se.EventTime) AS EventTime,
COALESCE(pe.EventType, se.EventType) AS EventType,
COALESCE(pe.Duration, se.Duration) AS Duration,
COALESCE(pe.Data, se.Data) AS Data,
COALESCE(pe.Field, se.Field) AS Field,
pe.ThisThing, se.OtherThing
FROM PE pe FULL OUTER JOIN SE se
ON pe.StaffName = se.StaffName
AND pe.Duration = se.Duration
AND pe.EventTime = se.EventTime
WHERE NOT(pe.ThisThing = 1 AND se.OtherThing = 0)
) Z
Itu mungkin tidak membenarkan seluruh alasan untuk struktur kueri, tetapi mungkin memberi Anda ide - pandangan ini bergabung dengan dua tabel yang dirancang sangat buruk yang saya tidak punya kontrol atas dan mencoba untuk mensintesiskan beberapa informasi dari itu.
Jadi, karena ini adalah tampilan yang digunakan dari aplikasi, ketika mencoba mengoptimalkan saya membungkusnya dalam SELECT lain, seperti ini:
SELECT * FROM (
-- … above code …
) Q
WHERE StaffName = 'SMITH, JOHN Q'
karena aplikasi sedang mencari anggota staf tertentu dalam hasilnya.
Masalahnya tampaknya adalah COALESCE(pe.StaffName, se.StaffName) AS StaffName
bagian, dan saya memilih dari tampilan StaffName
. Jika saya mengubahnya ke pe.StaffName AS StaffName
atau se.StaffName AS StaffName
, masalah kinerja hilang (tetapi lihat pembaruan 2 di bawah) . Tetapi itu tidak akan berhasil karena satu sisi atau sisi yang lain FULL OUTER JOIN
bisa hilang, sehingga satu atau bidang lainnya mungkin NULL.
Bisakah saya refactor ini mengganti COALESCE(…)
dengan sesuatu yang lain, yang akan ditulis ulang ke dalam subquery?
Catatan lain:
- Saya sudah menambahkan beberapa indeks untuk memperbaiki masalah kinerja dengan sisa kueri - tanpa
COALESCE
itu sangat cepat. - Agak mengherankan saya, melihat rencana eksekusi tidak menaikkan bendera, bahkan ketika subquery dan
WHERE
pernyataan pembungkus disertakan. Total biaya subkueri dalam penganalisa adalah0.0065736
. Hmph. Diperlukan empat detik untuk mengeksekusi. - Mengubah aplikasi ke permintaan secara berbeda
(mis. Kembalimungkin berhasil, tetapi sebagai upaya terakhir - saya benar-benar berharap saya dapat mengoptimalkan tampilan tanpa harus resor untuk menyentuh aplikasi.pe.StaffName AS PEStaffName, se.StaffName AS SEStaffName
dan melakukanWHERE PEStaffName = 'X' OR SEStaffName = 'X'
) - Prosedur tersimpan mungkin akan lebih masuk akal untuk ini, tetapi aplikasi ini dibuat dengan Entity Framework, dan saya tidak tahu bagaimana membuatnya bagus dengan SP yang mengembalikan tipe tabel (topik lain seluruhnya).
Indeks
Indeks yang saya tambahkan sejauh ini terlihat seperti ini:
CREATE NONCLUSTERED INDEX [IX_PE_EventTime]
ON [dbo].[PE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[ThisThing])
CREATE NONCLUSTERED INDEX [IX_SE_EventTime]
ON [dbo].[SE] ([EventTime])
INCLUDE ([StaffName],[Duration],[EventType],[Data],[Field],[OtherThing])
Memperbarui
Hmm ... Saya mencoba mensimulasikan perubahan yang terjadi di atas, dan itu tidak membantu. Yaitu, sebelum di ) Z
atas, saya menambahkan AND (pe.StaffName = 'SMITH, JOHN Q' OR se.StaffName = 'SMITH, JOHN Q')
, tetapi kinerjanya sama. Sekarang saya benar-benar tidak tahu harus mulai dari mana.
Perbarui 2
Komentar @ypercube tentang perlunya bergabung penuh membuat saya menyadari bahwa permintaan saya yang disintesis meninggalkan komponen yang mungkin penting. Sementara, ya, saya perlu bergabung penuh, tes yang saya lakukan di atas dengan menjatuhkan COALESCE
dan menguji hanya satu sisi bergabung untuk nilai non-nol akan membuat sisi lain dari bergabung penuh tidak relevan , dan pengoptimal mungkin menggunakan ini fakta untuk mempercepat permintaan. Juga, saya telah memperbarui contoh untuk menunjukkan bahwa StaffName
sebenarnya adalah salah satu kunci bergabung - yang mungkin memiliki pengaruh signifikan pada pertanyaan. Saya juga sekarang condong ke sarannya bahwa memecah ini menjadi serikat tiga arah, bukan bergabung penuh mungkin jawabannya, dan akan menyederhanakan banyak hal yang COALESCE
saya lakukan. Cobalah sekarang.
KeyField
, kedua indeks INCLUDE
tersebut StaffName
lapangan dan beberapa bidang lainnya. Saya dapat memposting definisi indeks dalam pertanyaan. Saya sedang mengerjakan ini pada server uji sehingga saya dapat menambahkan indeks yang menurut Anda mungkin berguna untuk dicoba!
WHERE pe.ThisThing = 1 AND se.OtherThing = 0
kondisi yang membatalkan FULL OUTER
gabung dan membuat kueri setara dengan gabung dalam. Apakah Anda yakin Anda perlu bergabung LENGKAP?
INNER JOIN
, LEFT JOIN
dengan WHERE IS NULL
cek, KANAN BERGABUNG dengan IS NULL) dan kemudian UNION ALL
tiga bagian. Dengan cara ini tidak perlu menggunakan COALESCE()
dan mungkin (mungkin saja) membantu pengoptimal untuk mengetahui penulisan ulang.