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


30

タイトルはあまり意味がありませんが、この問題に対してより良いタイトルを考えることはできませんでした。

私は次の表を持っています

プロジェクト

  • id

お客さま

  • id
  • id_project

お支払い

  • id
  • id_customer
  • 日付

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

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

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


1
そのため、クエリは最新のRDBMSの問題ではありません(または、結合を使用するのが良い)。
ガリック

4
、同意加入対副選択のためのクエリプランを取得し、1が優れているかを確認
ガイウス

1
@igorがJOINまたはINの使用について言及しているため、このSO投稿は一見の価値があると思います
-CoderHawk

回答:


52

非正規化が意味をなすかどうかを尋ねているようです。

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

答えは常に「依存する」ので、ここに私の経験則があります:

もし...

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

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

次の場合にのみ非正規化します...

  • データ量が非常に多い
  • 結合は高価であり、些細なクエリを返すためにそれらの多くを行う必要があります
  • データベースのパフォーマンスがボトルネックである、および/またはできるだけ速く行きたい

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


9

クエリを次のように書き換えた方が良いでしょう。

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

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

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

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

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

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

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

もちろん、それはまだ2人の顧客との共同プロジェクトの可能性を割り引いています...


3
パフォーマンスの最初のルール:本番環境では*を使用しないでください!
ブライアンボールサンスタントン

@ブライアン:非常に有効なポイント。また、select句で*を回避することでパフォーマンスに影響する可能性があるほか、の代わりにDROP VIEW+ CREATE VIEWが使用されているためにsys.dependsがkilterから外れた場合、MSSQLのビューオンビューでの列の順序に関する問題も回避されますALTER VIEW
デビッドスピレット

@ブライアンは書きやすいように*を付けました。
ガブリエルソロモン

このプロジェクトは、ドメインの詳細の持つ独立したアプリケーションとして考えられていると、顧客が別のプロジェクトに同じアカウントを持つことはできませんすることはできませんので、別の顧客に属している
ガブリエルソロモン

4

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.