Pchełki SQL: duże hasze

https://xpil.eu/6ZD

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

https://xpil.eu/6ZD

Leave a Comment

Komentarze mile widziane.

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.