web-dev-qa-db-ja.com

クエリを高速化するために列を複製しますか?

タイトルはあまり意味がありませんが、この問題に適したタイトルは考えられませんでした。

次の表があります

プロジェクト

  • id
  • 名前

お客様

  • id
  • id_project
  • 名前

お支払い

  • id
  • id_customer
  • 日付

ユーザーがシステムに入ると、特定のプロジェクトにアクセスできます。ここで、そのプロジェクトのすべての支払いをリストしたいと思います。これは非常に簡単です。

SELECT FROM payments where id_customer in (SELECT id from customers where id_project = 5)

私の質問は次のとおりです:この方法で支払いテーブルにid_project列を追加する方が良い場合は、クエリがより簡単で高速になります。

31
Gabriel Solomon

非正規化 が理にかなっているかどうかを尋ねているようです。

非正規化は、冗長データを追加するか、データをグループ化することにより、データベースの読み取りパフォーマンスを最適化しようとするプロセスです。場合によっては、非正規化は、リレーショナルデータベースソフトウェアに固有の非効率性をカバーするのに役立ちます。リレーショナル正規化データベースは、高性能に調整されていても、データの物理ストレージに大きなアクセス負荷をかけます。

答えは常に「依存する」ため、以下が私の経験則です。

もし...

  • データ量が多くない
  • あなたはまだたくさんの結合を行っていません
  • および/またはデータベースのパフォーマンスは現在ボトルネックではありません

次に正規化します。はい、非正規化の方が高速ですが、システム内に冗長データ(維持して同期を維持する必要があるデータ)があることも意味します。そのデータの「1つのソース」ではなく、逸脱する可能性のある複数のソースがあります。これは時間の経過とともに危険を伴うため、いくつかのベンチマークに裏付けられた非常に正当な理由がない限り、行うべきではありません。

私は非正規化するときだけ...

  • データ量が非常に多い
  • 結合は高価であり、些細なクエリでさえ返すためには、多くのことをしなければなりません
  • データベースのパフォーマンスがボトルネックになっているか、できるだけ早く移動したい

結合は最新のハードウェアでは非常に高速ですが、結合は無料ではありません

53
Jeff Atwood

クエリを次のように書き直すことをお勧めします。

SELECT payments.*
FROM   customers
JOIN   payments 
ON     payments.id_customer = customers.id
WHERE  customers.id_project = 5

これは簡潔ではないように見え、優れたクエリプランナーは何をしようとしているかを確認し、代わりに上記の結合として相関サブクエリを実行しますが、悪いクエリプランナーはpayments.id_customerのインデックススキャンを実行してしまう可能性があります(仮定)より効率的な方法を実行する代わりに、関連するインデックス(または、さらに悪いことに、テーブルスキャン)があります。優れたクエリプランナーでも、このクエリの配置がより複雑なものにラップされていると、最適化を確認できない場合があります。サブクエリではなく結合として関係を表現すると、データ構造を変更するよりも大きな違いが生じる可能性があります。

Jeffが言うように、非正規化は慎重に検討する必要があります。これは、特に一部のレポート目的で簡単にパフォーマンスを向上させることができますが、サポートするビジネスロジックのバグが原因で一貫性が失われる可能性があります。

余談ですが、明らかに私はあなたのビジネスを知らないので何かが足りない可能性がありますが、あなたのテーブルの関係は私には奇妙に見えます。彼らは、同じ顧客に対して複数のプロジェクトを持つことは決してできないことを意味します。これは、通常、少なくとも長期間にわたって、私の経験では当てはまりません。

customer     project      payment
--------     --------     -------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer     

またはあまり正規化されていない場合(私はそれが必要になるとは思いませんが):

customer     project      payment
--------     --------     --------
                          pa_id
             pr_id    <-- payment
cu_id    <-- customer 
           `------------- customer    

もちろん、それでも2人の顧客との共同プロジェクトの可能性は無視されます...

10
David Spillett

一部のデータベースでは、複雑なクエリに基づいて、大量のデータを含む複雑なVIEWSの代わりに「マテリアライズドビュー」を作成する可能性があります。これを使用して、これまでに成長したアプリケーションシステムでの非正規化を回避できます。「マテリアライズドビュー」では、マテリアライズドビューで使用される更新方法とストレージ容量を明確に把握する必要があります...

4