Понякога виждам как хора се опитват да "оптимизират" своите изявления за актуализиране, за да избегнат записването на същата стойност в определена колона. Моето разбиране винаги е било, че ако ще актуализирате ред, като приемем, че всички стойности са в ред, разходите за заключване на реда са много по-високи от допълнителните разходи за актуализиране на една, две или всички колони в този ред .
И така, създадох проста таблица, за да тествам това:
СЪЗДАВАНЕ НА ТАБЛИЦА dbo.whatever( ID INT IDENTITY(1,1) ПЪРВИЧЕН КЛЮЧ, v1 NVARCHAR(50) НЕ NULL, v2 NVARCHAR(50) НЕ NULL, v3 NVARCHAR(50) НЕ NULL, v4 NVARCHAR(50) НЕ NULL, v5 NVARCHAR(50) НЕ NULL, v6 NVARCHAR(50) NOT NULL);
След това създадох съхранена процедура за попълване на таблицата с 50 000 реда с различни малки низове:
СЪЗДАВАНЕ НА ПРОЦЕДУРА dbo.cleanASBEGIN SET NOCOUNT ON; ТАБЛИЦА ОТСЪЖАНЕ dbo.whatever;;С x(d) AS ( ИЗБЕРЕТЕ d ОТ ( СТОЙНОСТИ (N'abc'),(N'def'),(N'ghi'), (N'jkl'),(N'mno'),(N 'pqr') ) КАТО y(d) ) ВМЕСЕТЕ dbo.whatever(v1, v2, v3, v4, v5, v6) ИЗБЕРЕТЕ ГОРЕ (50000) x1.d, x2.d, x3.d, x4.d, x5 .d, x6.d ОТ x AS x1, x AS x2, x AS x3, x AS x4, x AS x5, x AS x6, x AS x7;ENDGO
След това написах изявления за актуализиране, формулирани по два начина, които бихте могли да „избегнете“ записването в конкретна колона, като се има предвид това присвояване на променлива:
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';
Първо, като използвате CASE израз, за да проверите дали стойността в колоната е същата като стойността в променливата:
UPDATE 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 ИНАЧЕ v3 КРАЙ, v4 =СЛУЧАЙ, КОГАТО v4 <> @v4 THEN @v4 ELSE v4 КРАЙ, v5 =СЛУЧАЙ, КОГАТО v5 <> @v5 THEN @v5 ИНАЧЕ v5 КРАЙ, v6 =СЛУЧАЙ, КОГАТО v6 <> @v6 THEN @v6 ELSE v6 ENDWHERE( v1 <> @v1 OR v2 <> @v2 OR v3 <> @v3 OR v4 <> @v4 OR v5 <> @v5 OR v6 <> @v6);
И второ чрез издаване на независима АКТУАЛИЗАЦИЯ за всяка колона (всяка е насочена само към редовете, където тази стойност всъщност се е променила):
UPDATE 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;UPDATE 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;предварително>Тогава бих сравнявал това с начина, по който повечето от нас биха направили това днес:просто АКТУАЛИЗИРАТЕ всички колони, без да се интересувате дали това е вече съществуващата стойност за тази конкретна колона:
АКТУАЛИЗИРАНЕ dbo.whatever SET v1 =@v1, v2 =@v2, v3 =@v3, v4 =@v4, v5 =@v5, v6 =@v6WHERE( v1 <> @v1 ИЛИ v2 <> @v2 ИЛИ v3 <> @v3 ИЛИ v4 <> @v4 ИЛИ v5 <> @v5 ИЛИ v6 <> @v6);(Всички те предполагат, че колоните и параметрите/променливите не са с NULL – те ще трябва да използват COALESCE, за да отчетат сравняването на NULL от двете страни, ако това е така. Те също така предполагат, че ще имате допълнителна клауза WHERE за целеви конкретни редове – в този пример бихте могли да изпълните първата и третата заявка без всеобхватната клауза WHERE и да видите почти идентични резултати. Запазих това просто за краткост.)
След това исках да видя какво се случва в тези три случая, когато някоя стойност може да бъде променена, когато определени стойности могат да бъдат променени, когато няма да се променят никакви стойности и когато всички стойности ще бъдат променени. Мога да повлияя на това, като променя съхранената процедура за вмъкване на константи в определени колони или като променя начина на присвояване на променливите.
-- за да покаже кога някоя стойност може да се промени в един ред, процедурата използва пълно кръстосано присъединяване:SELECT TOP (50000) x1.d, x2.d, x3.d, x4.d, x5.d, x6 .d -- за да покажем кога определени стойности ще се променят на много редове, можем да кодираме константи:-- две стойности са освободени:SELECT TOP (50000) N'abc', N'def', x3.d, x4.d , x5.d, x6.d -- четири стойности са освободени:ИЗБЕРЕТЕ TOP (50000) N'abc', N'def', N'ghi', N'jkl', x5.d, x6.d -- за показване когато никакви стойности няма да се променят, кодираме всички шест стойности твърдо:SELECT TOP (50000) N'abc', N'def', N'ghi', N'jkl', N'mno', N'pqr' -- и за да се покаже кога всички стойности ще се променят, ще се извърши различно присвояване на променлива: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';Резултати
След провеждане на тези тестове, "сляпа актуализация" печели във всеки един сценарий. Сега си мислите какво са няколкостотин милисекунди? Екстраполирайте. Ако извършвате много актуализации в системата си, това наистина може да започне да се отразява негативно.
Подробни резултати в Plan Explorer:Всяка промяна | 2 стойности освободени | 4 стойности освободени | Всички стойности са освободени | Всички промени
Въз основа на обратна връзка от Roji реших да тествам и това с няколко индекса:
СЪЗДАВАЙТЕ ИНДЕКС x1 НА dbo.whatever(v1);СЪЗДАЙТЕ ИНДЕКС x2 НА dbo.whatever(v2);СЪЗДАЙТЕ ИНДЕКС x3 НА dbo.whatever(v3) INCLUDE(v4,v5,v6);Продължителностите бяха значително увеличени с тези индекси:
Подробни резултати в Plan Explorer:Всяка промяна | 2 стойности освободени | 4 стойности освободени | Всички стойности са освободени | Всички промени
Заключение
От този тест ми се струва, че обикновено не си струва да проверявате дали дадена стойност трябва да се актуализира. Ако вашият оператор UPDATE засяга множество колони, почти винаги е по-евтино за вас да сканирате всички колони, където всяка стойност може да се е променила, вместо да проверявате всяка колона поотделно. В следваща публикация ще проуча дали този сценарий е паралелен за LOB колони.