Kadang-kadang saya melihat orang mencoba "mengoptimalkan" pernyataan pembaruan mereka untuk menghindari penulisan nilai yang sama ke kolom tertentu. Pemahaman saya selalu bahwa jika Anda akan memperbarui baris, dengan asumsi semua nilai berada di baris, biaya penguncian baris jauh lebih tinggi daripada biaya tambahan untuk memperbarui satu, dua atau semua kolom di baris itu .
Jadi, saya membuat tabel sederhana untuk menguji ini:
BUAT TABEL dbo.whatever( ID INT IDENTITY(1,1) PRIMARY KEY, v1 NVARCHAR(50) NOT NULL, v2 NVARCHAR(50) NOT NULL, v3 NVARCHAR(50) NOT NULL, v4 NVARCHAR(50) NOT NULL, v5 NVARCHAR(50) NOT NULL, v6 NVARCHAR(50) NOT NULL);
Kemudian saya membuat prosedur tersimpan untuk mengisi tabel dengan 50.000 baris dengan berbagai string kecil:
BUAT PROSEDUR dbo.cleanASBEGIN AKTIFKAN NOCOUNT; TRUNCATE TABLE dbo.whatever;;DENGAN x(d) AS ( PILIH d DARI ( NILAI (N'abc'),(N'def'),(N'ghi'), (N'jkl'),(N'mno'),(N 'pqr') ) AS y(d) ) INSERT dbo.whatever(v1, v2, v3, v4, v5, v6) PILIH TOP (50000) x1.d, x2.d, x3.d, x4.d, x5 .d, x6.d DARI x AS x1, x AS x2, x AS x3, x AS x4, x AS x5, x AS x6, x AS x7;ENDGO
Kemudian saya menulis pernyataan pembaruan yang dirumuskan dalam dua cara sehingga Anda dapat "menghindari" menulis ke kolom tertentu, mengingat penugasan variabel ini:
DECLARE @v1 NVARCHAR(50) =N'abc', @v2 NVARCHAR(50) =N'def', @v3 NVARCHAR(50) =N'ghi', @v4 NVARCHAR(50) =N'jkl ', @v5 NVARCHAR(50) =N'mno', @v6 NVARCHAR(50) =N'pqr';
Pertama dengan menggunakan ekspresi CASE untuk memeriksa apakah nilai di kolom sama dengan nilai di variabel:
PERBARUI dbo.whatever SET v1 =CASE WHEN v1 <> @v1 THEN @v1 ELSE v1 END, v2 =CASE WHEN v2 <> @v2 THEN @v2 ELSE v2 END, v3 =CASE WHEN v3 <> @v3 THEN @v3 LAIN v3 SELESAI, v4 =KASUS KETIKA v4 <> @v4 MAKA @v4 LAIN v4 SELESAI, v5 =KASUS KETIKA v5 <> @v5 LALU @v5 LAIN v5 AKHIR, v6 =KASUS KETIKA v6 <> @v6 MAKA @v6 LAINNYA v6 AKHIR( v1 <> @v1 ATAU v2 <> @v2 ATAU v3 <> @v3 ATAU v4 <> @v4 ATAU v5 <> @v5 ATAU v6 <> @v6);
Dan kedua dengan mengeluarkan UPDATE independen untuk setiap kolom (masing-masing hanya menargetkan baris yang sebenarnya telah diubah nilainya):
PERBARUI dbo.whatever SET v1 =@v1 WHERE v1 <> @v1;UPDATE dbo.whatever SET v2 =@v2 WHERE v2 <> @v2;UPDATE dbo.whatever SET v3 =@v3 WHERE v3 <> @v3;PERBARUI dbo.whatever SET v4 =@v4 WHERE v4 <> @v4;UPDATE dbo.whatever SET v5 =@v5 WHERE v5 <> @v5;UPDATE dbo.whatever SET v6 =@v6 WHERE v6 <> @v6;Kemudian saya akan membandingkan ini dengan cara kebanyakan dari kita akan melakukannya hari ini:cukup PERBARUI semua kolom tanpa peduli apakah itu adalah nilai yang sudah ada sebelumnya untuk kolom tertentu:
PERBARUI dbo.setelan apa pun v1 =@v1, v2 =@v2, v3 =@v3, v4 =@v4, v5 =@v5, v6 =@v6WHERE( v1 <> @v1 ATAU v2 <> @v2 ATAU v3 <> @v3 ATAU v4 <> @v4 ATAU v5 <> @v5 ATAU v6 <> @v6);(Semua ini mengasumsikan bahwa kolom dan parameter/variabel tidak NULLable – mereka perlu menggunakan COALESCE untuk memperhitungkan perbandingan NULL di kedua sisi jika demikian. Mereka juga menganggap Anda akan memiliki klausa WHERE tambahan untuk menargetkan baris tertentu – dalam contoh ini Anda dapat menjalankan kueri pertama dan ketiga tanpa klausa WHERE yang mencakup semua dan melihat hasil yang hampir sama. Saya membuat ini sederhana untuk singkatnya.)
Kemudian saya ingin melihat apa yang terjadi dalam tiga kasus ini ketika nilai apa pun dapat diubah, ketika nilai tertentu mungkin diubah, ketika tidak ada nilai yang akan diubah, dan kapan semua nilai akan diubah. Saya dapat memengaruhi ini dengan mengubah prosedur tersimpan untuk memasukkan konstanta ke dalam kolom tertentu, atau dengan mengubah cara variabel ditetapkan.
-- untuk menunjukkan kapan nilai apa pun mungkin berubah dalam satu baris, prosedur menggunakan gabungan silang penuh:SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6 .d -- untuk menunjukkan kapan nilai tertentu akan berubah pada banyak baris, kita dapat membuat hard-code konstanta:-- dua nilai dikecualikan:SELECT TOP (50000) N'abc', N'def', x3.d, x4.d , x5.d, x6.d -- empat nilai dikecualikan:SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d -- untuk ditampilkan ketika tidak ada nilai yang berubah, kami melakukan hard-code keenam nilai:SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr' -- dan untuk menunjukkan kapan semua nilai akan berubah, penetapan variabel yang berbeda akan dilakukan:DECLARE @v1 NVARCHAR(50) =N'zzz', @v2 NVARCHAR(50) =N'zzz', @v3 NVARCHAR(50) =N 'zzz', @v4 NVARCHAR(50) =N'zzz', @v5 NVARCHAR(50) =N'zzz', @v6 NVARCHAR(50) =N'zzz';Hasil
Setelah menjalankan tes ini, "pembaruan buta" menang di setiap skenario. Sekarang, Anda berpikir, apa yang dimaksud dengan beberapa ratus milidetik? Meramalkan kemungkinan. Jika Anda melakukan banyak pembaruan di sistem Anda, ini dapat benar-benar mulai memakan korban.
Hasil mendetail di Plan Explorer:Perubahan apa pun | 2 nilai dikecualikan | 4 nilai dikecualikan | Semua nilai dikecualikan | Semua berubah
Berdasarkan umpan balik dari Roji, saya memutuskan untuk menguji ini dengan beberapa indeks juga:
BUAT INDEX x1 PADA dbo.whatever(v1);BUAT INDEX x2 PADA dbo.whatever(v2);BUAT INDEX x3 PADA dbo.whatever(v3) TERMASUK(v4,v5,v6);Durasi meningkat secara substansial dengan indeks ini:
Hasil mendetail di Plan Explorer:Perubahan apa pun | 2 nilai dikecualikan | 4 nilai dikecualikan | Semua nilai dikecualikan | Semua berubah
Kesimpulan
Dari tes ini, menurut saya biasanya tidak ada gunanya memeriksa apakah suatu nilai harus diperbarui. Jika pernyataan UPDATE Anda memengaruhi beberapa kolom, hampir selalu lebih murah bagi Anda untuk memindai semua kolom di mana nilai apa pun mungkin telah berubah daripada memeriksa setiap kolom satu per satu. Dalam posting mendatang, saya akan menyelidiki apakah skenario ini paralel untuk kolom LOB.