Od czasu do czasu każdego z nas nachodzi nagła potrzeba wyliczenia skrótu z jakiejś dużej wartości tekstowej. To znaczy, nie dużej, tylko długiej. Ale nachodzi, prawda?
Jeżeli, Czytelniku, nie nachodzi Cię od czasu do czasu nagła potrzeba wyliczenia du... dług.. skrótu z jakiejś długiej wartości tekstowej, ten wpis prawdopodobnie nie jest dla Ciebie - polecam zamiast tego odwiedzić Joe Monstera albo Nowego Pompona.
Tych z Czytelników, którzy od czasu do czasu walczą z przemożoną chęcią haszowania długich stringów, zapraszam do dalszej lektury.
Ogólnie znanym faktem[citation needed] jest, iż próba użycia funkcji HASHBYTES skończy się niepowodzeniem w przypadku tekstów dłuższych niż 8000 znaków. A w przypadku UTF (nvarchar) "tylko" 4000 znaków. Większe stringi nie dadzą się zhaszować, choćby nie wiem co. Wyskakuje błąd w stylu "String or binary would be truncated", czy cóś w ten deseń i człowiek głupieje, zaczyna skrobać się po głowie a w najbardziej ekstremalnych przypadkach nawet szukać rozwiązania w Google.
Naturalnym odruchem każdego logicznie myślącego zjadacza bitów jest w takiej sytuacji podzielenie danych wejściowych na kawałki po 8000 znaków (a raczej bajtów), wyliczenie HASHBYTES z każdego z nich z osobna, a na koniec szybki XOR czy inne cóś, żeby te cząstkowe HASHBYTES scalić do jednej kupy. Prawda?
Niby prawda... Tylko że samodzielne napisanie takiej funkcji wymaga (1) czasu, (2) umiejętności, (3) chęci[citation needed], (4) uprawnień do modyfikowania bazy danych, które nie zawsze są nam dane oraz (5) obarczone jest ryzykiem popełnienia błędu. Aha, i jeszcze (6) wynik będzie różny dla różnych implementacji, więc jeżeli - nie daj Billu - kiedykolwiek spotka się dwóch programistów implementujących hasze na dużych stringach, zamiast pić piwo i rozmawiać o zerach i jedynkach, zaczną się prać po mordach. Czy coś.
Dlatego należy w tym przypadku powstrzymać naturalny odruch i skorzystać z wbudowanej, acz mało znanej funkcji systemowej sys.fn_repl_hash_binary.
Funkcja sys.fn_repl_hash_binary robi dokładnie to, co jest napisane na pudełku: bierze dowolnie wielką porcję danych binarnych i wylicza z nich skrót.
A jakim algorytmem? MD5? SHA? Tego już nie doczytałem bom leniwy, jeżeli jednak któremuś z dogorywających Czytelników udałoby się to ustalić, proszę o komentarz.
No właśnie. Mamy gotowca, który bierze dowolnie wielką porcję danych binarnych i wylicza z nich krótki i zwięzły skrót. W powtarzalny sposób. Wydajnie.
Jeżeli, dajmy na to, mamy w naszej bazie tabelkę, w której przechowujemy pliki (nie same nazwy plików, ale również treść), i chcemy mieć możliwość sprawdzenia, czy któreś z plików aby się nie powtarzają (wg zawartości), zamiast porównywać zawartość każdego pliku z zawartościami wszystkich innych plików (co może być nieco kłopotliwe z obliczeniowego punktu widzenia) po prostu w momencie dodawania pliku do tabeli wyliczamy hasz z jego zawartości, dzięki czemu możemy potem szybciutko sprawdzić, czy takiego pliku aby już nie ma (uwaga na kolizje!).
Wyrażenie, które wyliczy nam hasz, wygląda tak:
CONVERT(VARCHAR(1000), sys.fn_repl_hash_binary(FILE_CONTENT), 2)
O co tu chodzi?
sys.fn_repl_hash_binary(FILE_CONTENT) zwróci binarną wartość skrótu, a konwersja tego na varchar(1000) przerobi ten skrót na postać tekstową (parametr 2 na końcu mówi, że wynik ma być tekstem - jeżeli tego nie zrobimy, dostaniemy w wyniku krzaczki, które od biedy też by mogły być, ale jednak ciąg znaków heksadecymalnych wygląda "porządniej" od ciągu losowych znaków UTF).
Ot i cała tajemnica...
Jeżeli chcesz do komentarza wstawić kod, użyj składni:
[code]
tutaj wstaw swój kod
[/code]
Jeżeli zrobisz literówkę lub zmienisz zdanie, możesz edytować komentarz po jego zatwierdzeniu.