binary型のカラムへのデータ保存について

binary型のメリットとカラムへデータを保存する方法についてのメモです。

なぜbinary型を使うのか

ストレージの効率化とパフォーマンスの向上、そしてデータ整合性を保つメリットがあります。

パフォーマンスの向上

  • ストレージの効率化: 例えばUUIDを文字列形式で格納する場合に比べて、バイナリ形式で格納するとデータサイズが半分になります(文字列が32byteなのに対して、binary(16)だと16byte)。これにより、ディスク容量の節約ができます。
  • インデックスの効率化: バイナリデータは、文字列よりも比較演算が速く、インデックスの作成や検索時のパフォーマンスが向上することがあります。特に、UUIDなどの固定長のデータに対して、文字列の比較よりもバイナリ比較の方が効率的です。

データの一貫性

BINARY型は固定長のデータを保持するため、格納するデータの長さが常に一定であることが保証されます。これにより、データが予期しない形で変更されることを防ぐことができます(例:文字列型でのトリムやパディングの問題)。

binary型のデータを保存する方法

UUIDなどの16進数で表現された値。これをbinary(size)のカラムにを保存するケースを考えてみます。

a34e5f1d-60f7-4230-9b74-7885e4558c76 ならsize=16。UUIDですね。

101001-27482797-3e59-7c18-a761-7b54a3c4c9ee UUIDとは異なる16進数で表現された値。size=19に対応します。

SQLの場合

16進数の文字列をUNHEX関数やX'...'構文でバイトに変換してINSERTします。ハイフンなどを含んでいる場合は除外しておきます(SQLならREPLACE関数などで除外できます)。

1INSERT INTO sample_table(column_size16, column_size19)
2VALUES(X'a34e5f1d60f742309b747885e4558c76', X'101001274827973e597c18a7617b54a3c4c9ee';

↓取得する際はHEX関数で16進数に戻します。

1SELECT HEX(column_size16) AS original_size16, HEX(column_size19) AS oritinal_size19 FROM sample_table;
2+----------------------------------+----------------------------------------+
3| original_size16                  | oritinal_size19                        |
4+----------------------------------+----------------------------------------+
5| A34E5F1D60F742309B747885E4558C76 | 101001274827973E597C18A7617B54A3C4C9EE |
6+----------------------------------+----------------------------------------+

UUIDに特化した関数も用意されていますが、それらの関数はbinary(16)にしか使えないため注意。

Golangの場合

プログラミング言語でDBに値を保存する場合もSQLの場合と同様に、16進数からバイトに変換してSQLを実行してあげればOKです。

Goには encoding/hexという16進数を扱うパッケージがあるのでそれを使います。

 1// GORMの場合
 2
 3type sampleTable struct {
 4	Column16 []byte `gorm:"primaryKey;column:column16;size:16"`
 5	Column19 []byte `gorm:"column:column19;size:19"`
 6}
 7
 8func Create(tx *gorm.DB) error {
 9	size16 := "a34e5f1d-60f7-4230-9b74-7885e4558c76"
10	size16Bytes, size16HexDecodeErr := hex.DecodeString(strings.ReplaceAll(size16, "-", ""))
11	if size16HexDecodeErr != nil {
12		return size16HexDecodeErr
13	}
14
15	size19 := "101000-f64d0ffb-5c7b-1a65-aef6-b4aee892ef56"
16	size19Bytes, size19HexDecodeErr := hex.DecodeString(strings.ReplaceAll(size19, "-", ""))
17	if size19HexDecodeErr != nil {
18		return size19HexDecodeErr
19	}
20
21	result := tx.Create(&sampleTable{Column16: size16Bytes, Column19: size19Bytes})
22	if result.Error != nil {
23		return result.Error
24	}
25
26	return nil
27}