środa, 16 lutego 2011

CHECKSUM - ciekawostka

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 ...?

3 komentarze:

  1. 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ń
  2. 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.

    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

    OdpowiedzUsuń
  3. 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ń