こんにちは、サイオステクノロジーの佐藤 陽です。
今回はSQLで文字列をHash化するときに気を付けるポイントについてご紹介します。
基本的な事ですが、意外と気づかずハマってしまう人も多いかと思うので最後までご覧ください!
はじめに
今回はSQLServer(T-SQL)を題材にお話しします。
Databaseにデータを保存するとき、素のままではなくHASH化してから保存、というのはよくあるかと思います。
SQLServerには文字列をハッシュ化するHASHBYTESという関数が用意されています。
非常に便利な関数なんですが、これを使う際、一度ドはまりして解決に数日費やしてしまったことがあるので
同じような犠牲者を出さないよう、ここに書き留めたいと思います。
結論
早速ですが、結論です。
Hash化する元の文字列が同じ文字であっても、データ型が違う場合は異なるHash値が生成される場合があります!
検証
前提
SQLServerで文字列を扱う際、以下4つのデータ型が用意されています。
- char:固定長, 全角2byte,半角1byte
- nchar:固定長, 全角/半角ともに2byte
- varchar:可変長, 全角2byte,半角1byte
- nvarhcar:可変長, 全角/半角ともに2byte
試しに、同じ文字を4つの型に代入し、中身とbyte数を取得しました。
文字の中身は同じで、byte数が異なっていることが分かります。
これは半角文字のbyte数が異なっていることと、固定長or可変長によるためです。
Hash化
ではこの4つの文字列をHash化してみます。
今回はMD5のアルゴリズムで行いますが、他のアルゴリズムでも動作は同様です。
文字列としては同じですが、Hash化するとすべて異なる値となります。
考察は後にして、少し違うパターンのデータも入れてみます。
今回は空文字をHash化してみました。
空文字であったとしても、Hash値が異なってきていますね。
ただ、今回はvarcharとnvarharのHash値は同じとなりました。
最後に、文字数をデータ型のn値と同じにした場合で試してみます。(今回であれば10文字)
今回はvarcharとcharのHash値が等しく、nvarcharとncharのHash値が等しくなっていることが分かります。
以上の3パターンにおいて、Hash値とbyte数の結果から以下のことが分かります。
- 同じ文字で、同じbyte数の場合は同じHash値が生成される
- 同じ文字でも、byte数が異なっている場合は異なるHash値が生成される
まとめ
Hash化する際には、文字とbyte数が同じである必要があることが分かりました。
よくよく考えれば、Hash化は同じ文字ではなく同じデータの場合に、同じHash値が生成されるため、当たり前の挙動ですね。
ただ、当時はあまりデータ型を意識せず実装してしまっており、
「同じ文字なのにHash値が違う?!」という経験をしてしまいました。
文字を表すならなんでもいいや!ではなく
しっかり4つのデータ型を区別し、正しく把握したうえで実装する必要がありますね。
おまけ
先頭に紹介したMicrosoftのHASHBYTESのドキュメントを読むと
@input
ハッシュされるデータを含む変数を指定します。 @input
は、varchar、nvarchar、または varbinary です。
という記載があります。(2022/11現在)
そもそもchar,ncharがinputとして与えられることは想定されていないかもしれません。
今回はそれっぽく動作しましたが、注意して利用してください。