SQLアンチパターン　メタデータトリブル

# メタデータトリブル

どんなに優れたデータベースでもデータが増えることでパフォーマンスは低下していく。
インデックスを使用することで状況は改善するが、それでもデータの増加によってパフォーマンスが低下していく。

メタデータトリブルとはデータ増加につれてパフォーマンスが低下することに対応するための構造を指す。
具体的には以下のようなパターンが当てはまる。

* 行数の多いテーブルを複数のテーブルに分割する
* 列を複数列に分割する

表で表すと以下の通り。

***book***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|
|2|2020-01-03|エンジニアになろう２巻|
|3|2020-04-03|エンジニアになろう３巻|
|4|2020-12-31|エンジニアになろう４巻|
|5|2021-03-02|エンジニアになろう５巻|
|6|2021-06-02|エンジニアになろう６巻|
|7|2021-09-02|エンジニアになろう７巻|
|8|2021-12-02|エンジニアになろう８巻|

上記の表は、本のタイトルと発売日を格納したシンプルなテーブル。
現在は「エンジニアになろう」シリーズの書籍が格納されている。
１タイトルのみ格納されていて巻数もまだ８巻しかない。

しかし今後このサービスで数万タイトル扱うこととなりテーブルを年ごとに分割し格納する構造に変更した。
以下の構成が今回の***メタデータトリブル***パターンに該当する

***2019_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|

***2020_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2020-01-03|エンジニアになろう２巻|
|2|2020-04-03|エンジニアになろう３巻|
|3|2020-12-31|エンジニアになろう４巻|

***2021_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2021-03-02|エンジニアになろう５巻|
|2|2021-06-02|エンジニアになろう６巻|
|3|2021-09-02|エンジニアになろう７巻|
|8|2021-12-02|エンジニアになろう８巻|

上記のパターンは発売した年でテーブルを分割し１テーブルでデータを持つよりレコード数が減りパフォーマンスの低下を軽減ですることを目的とした構成である。

# デメリット

* テーブルの増殖
* データの整合性管理
* データの同期
* 一意性の保証
* テーブルをまたいだクエリ実行
* メタデータの同期
* 参照整合性の管理

## テーブルの増殖

データを複数のテーブルに分割する際、どのレコードがどのテーブルに属しているか定義するポリシーが必要となる。

***2019_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|

***2020_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2020-01-03|エンジニアになろう２巻|
|2|2020-04-03|エンジニアになろう３巻|
|3|2020-12-31|エンジニアになろう４巻|

***2021_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2021-03-02|エンジニアになろう５巻|
|2|2021-06-02|エンジニアになろう６巻|
|3|2021-09-02|エンジニアになろう７巻|
|8|2021-12-02|エンジニアになろう８巻|

上記の例ではテーブルを年ごとに分かれている。
データを挿入する際は年によって挿入するテーブルを選択する必要がある。

また、今回の例のように年ごとにテーブルを分割していた場合、2021年が終了して2022年になっていた際に「2022_release」の作成を忘れて「2022_release」にデータを挿入しようとした際アプリけーーションからエラーが発生するようになる。

## データの整合性管理

***2019_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|

***2020_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2020-01-03|エンジニアになろう２巻|
|2|2020-04-03|エンジニアになろう３巻|
|3|2020-12-31|エンジニアになろう４巻|

***2021_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2021-03-02|エンジニアになろう５巻|
|2|2021-06-02|エンジニアになろう６巻|
|3|2021-09-02|エンジニアになろう７巻|
|8|2021-12-02|エンジニアになろう８巻|


上記のテーブル構成で、新たに以下のデータを挿入したい。

|release_date|title|
|:---|:---|
|2021-12-30|エンジニアになろう番外編|

挿入したいテーブルは「2021_release」ですが、日時を見て自動的に挿入テーブルを選択する方法がないためcheck制約を設ける必要があります。

## データの同期


***2019_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|

***2020_release***

|book_id|release_date|title|
|:---|:---|:---|
|<font color="Red">1</font>|<font color="Red">2020-01-03</font>|<font color="Red">エンジニアになろう２巻</font>|
|2|2020-04-03|エンジニアになろう３巻|
|3|2020-12-31|エンジニアになろう４巻|

***2021_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2021-03-02|エンジニアになろう５巻|
|2|2021-06-02|エンジニアになろう６巻|
|3|2021-09-02|エンジニアになろう７巻|
|8|2021-12-02|エンジニアになろう８巻|

上記のデータにて、「2020_release」に挿入されている「エンジニアになろう２巻」が「2020-01-03」発売ではなく「2019-12-31」だった場合、該当データをUPDATEするだけでは別の年数のテーブルに格納されてしまうことになる。

***2020_release***

|book_id|release_date|title|
|:---|:---|:---|
|<font color="Red">1</font>|<font color="Red">2019-12-31</font>|<font color="Red">エンジニアになろう２巻</font>|
|2|2020-04-03|エンジニアになろう３巻|
|3|2020-12-31|エンジニアになろう４巻|

修正するためには対象テーブルからデータを削除して別のテーブルにインサートする必要がある。

## 一意性の保証

テーブルを分割した場合、主キーの値が一意であることを保証する必要がある。
以下の場合によって対応が異なる。

* シーケンスオブジェクトをサポートするデータベースを使用している場合、同一のシーケンスオブジェクトを使用して、分割されたテーブルに対して主キーの値を生成できる。

* シーケンスオブジェクトがなく、テーブルごとにID生成のみをサポートするデータベースを使用している場合主キーを作成するためにテーブルを定義する必要がある。

## テーブルをまたいだクエリ実行

複数テーブルをまたいでテーブルを参照する必要が出てきた。
その際はUNIONを使用して該当を参照する必要がある。

テーブルが追加された場合はその都度参照テーブルを追加する必要がある。

## メタデータの同期

***◯◯◯◯_release***

|book_id|release_date|title|<font color="Red">price</font>|
|:---|:---|:---|:---|

上記のテーブル構成に販売価格を管理する列を追加することとなった。
新しい列はALTER文を用いて追加できるが、年ごとにテーブルが分割されているため対象テーブル全てに対して共通する列を追加する必要がある。

## 参照整合性の管理

***2019_release***

|book_id|release_date|title|
|:---|:---|:---|
|1|2019-06-01|エンジニアになろう１巻|

上記のテーブルに各書籍に紐づいたコメントを管理するテーブルを作成したい。

しかし、[ポリモーフィック関連](https://qiita.com/fktnkit/items/d4c98f50642bac61fbd9)の記事で詳細に記載したが、親テーブルが複数になるため外部キーを設定することができない。

# 解決策

## パーティショニングの使用

パーティショニングを使用することでテーブル分割の欠点を解消しつつ巨大なテーブルを分割するメリットを得られる。
テーブルは物理的に分割されているが１つのテーブルを扱うようにSQLステートメントを実行できる。

詳しくはわかりやすい記事があったため詳細はこちらを参照してください。

[【MySQL】データベースのパーティショニングとはなんぞやという話](https://norikone.hatenablog.com/entry/2018/10/12/%E3%80%90MySQL%E3%80%91%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%8B%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF%E3%81%AA)

# まとめ

データにメタデータを増殖させない

# 次回

ラウンディングエラー

# 参考文献

[SQLアンチパターン](
https://www.amazon.co.jp/SQL%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3-Bill-Karwin/dp/4873115892/ref=sr_1_1?adgrpid=104444684056&dchild=1&gclid=CjwKCAjw4_H6BRALEiwAvgfzqy-f83NTsyQkauo7gn6ysgAPmJvKyvuI5FaXFnxFmhVfCzCRRACsaBoCY-oQAvD_BwE&hvadid=447984765572&hvdev=c&hvlocphy=1009332&hvnetw=g&hvqmt=e&hvrand=13417269104929133208&hvtargid=kwd-335484809678&hydadcr=27264_11561110&jp-ad-ap=0&keywords=sql%E3%82%A2%E3%83%B3%E3%83%81%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3&qid=1599934568&sr=8-1&tag=googhydr-22)

![参考文献.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257405/b46d45ab-75ec-a620-eeb2-a422f6b1e659.jpeg)