Ciekawe zachowanie się funkcji CHECKSUM pokazał mi wczoraj jeden ze znajomych. Otóż CHECKSUM(10.00) = CHECKSUM(100.00). Zaobserwował On taki zachowanie gdy sprawdzał jakie rekordy zmieniły się w tabelce, a zmiana polegała na zwiększeniu ceny 10-krotnie. Korzystając z funkcji CHECKSUM okazało się, że nic się nie zmieniło. Przykład poniżej :
DECLARE @T1 TABLE ( ID INT, NAZWA VARCHAR(50), CENA DECIMAL(10,2) ); DECLARE @T2 TABLE ( ID INT, NAZWA VARCHAR(50), CENA DECIMAL(10,2) ); INSERT INTO @T1 (ID, NAZWA, CENA) VALUES (1, 'TEST1', 125.23) , (2, 'TEST2', 10.00) , (3, 'TEST3', 23.23); INSERT INTO @T2 (ID, NAZWA, CENA) VALUES (1, 'TEST1', 125.23) , (2, 'TEST2', 10.00) , (3, 'TEST3', 23.23); -- DANE W OBU TABELKACH SĄ IDENTYCZNE SELECT * FROM @T1 SELECT * FROM @T2 -- SPRAWDZENIE CZY SĄ JAKIEŚ REKORDY, KTÓRE SIĘ RÓŻNIĄ SELECT * FROM @T1 AS T1 INNER JOIN @T2 AS T2 ON T1.ID = T2.ID WHERE CHECKSUM(T1.NAZWA, T1.CENA) <> CHECKSUM(T2.NAZWA, T2.CENA) -- ZWIĘKSZENIE CENY W TABELI @T1 UPDATE @T1 SET CENA = CENA * 10 -- DANE W TABELACH RÓŻNIĄ SIĘ SELECT * FROM @T1 SELECT * FROM @T2 -- SPRAWDZENIE CZY REKORDY RÓŻNIĄ SIĘ ZA POMOCĄ CHECKSUM DAJE WYNIK NEGATYWNY -- WEDŁUG CHECKSUM NIC SIĘ NIE ZMIENIŁO SELECT * FROM @T1 AS T1 INNER JOIN @T2 AS T2 ON T1.ID = T2.ID WHERE CHECKSUM(T1.NAZWA, T1.CENA) <> CHECKSUM(T2.NAZWA, T2.CENA) -- OBIE SUMY KONTROLNE SĄ TAKIE SAME SELECT CHECKSUM(T1.NAZWA, T1.CENA) AS CHECKSUM_T1 , CHECKSUM(T2.NAZWA, T2.CENA) AS CHECKSUM_T2 FROM @T1 AS T1 INNER JOIN @T2 AS T2 ON T1.ID = T2.ID
W helpie do funkcji CHECKSUM jest fajne stwierdzenie, które może tłumaczyć takie zachowanie :
However, there is a small chance that the checksum will not change. For this reason, we do not recommend using CHECKSUM to detect whether values have changed, unless your application can tolerate occasionally missing a change.Jak poczytałem na necie to problem jest znany. Jedni piszą swoje funkcje sprawdzające sumę kontrolną np. w C# i podłączają je jako CLR. Inni korzystają z funkcji HashBytes, ale tu elementem wejściowym musi być tekst.
Do powyższej sytuacji rozwiązaniem wydaje się "skastowanie" wartości numerycznej na VARCHARa. Wówczas osiągamy to o co nam chodziło.
A może jest inny sposób ...?
A czy to nie jest problem wynikajacy z tego, że do checksumy liczby rzeczywistej jest brany tylko wykładnik, a w tych liczbach różni sie tylko mantysa?
OdpowiedzUsuńDomyślam się do czego dążysz (http://www.zgapa.pl/zgapedia/Mantysa.html), ale szczerze przyznam, że nie wiem. Trzeba by się dobrać do algorytmu funkcji CHECKSUM.
OdpowiedzUsuńW necie znalazłem jeszcze takie przykłady
select checksum(10, 10), checksum(11, 26) -- Both 170
select checksum ('A','A'), checksum('B', 'Q') -- Both 2158
Co do drugiego przykładu to się zgodzić nie mogę bo u mnie daje to inne wyniki, ale może zależy to też od Collation?
http://www.sqlmonster.com/Uwe/Forum.aspx/sql-server-programming/65567/CHECKSUM-problem
28 year-old Speech Pathologist Stacee McCrisken, hailing from Windsor enjoys watching movies like Tattooed Life (Irezumi ichidai) and Cosplaying. Took a trip to Yin Xu and drives a Mercedes-Benz 680S Torpedo Roadster. kliknij tutaj, aby dowiedziec sie wiecej
OdpowiedzUsuń