2009-03-04 11 views
4

私は単純な正規化されたデータベースを使ってウェブサイトを作っています。合計を非正規化する必要がありますか?

PagesというテーブルとViewsというテーブルがあります。 Pageを表示するたびに、そのViewの一意のレコードがViewsテーブルに記録されます。

サイトにページを表示するときは、簡単なMySQL COUNT()を使用して、表示するビューの数を合計します。

この問題を除いて、データベースの設計はうまくいきます。何千もの中でトップ10のページを取得する方法を失っています。

Pages.views列を追加して各ページの総ビュー数を保持すると、ページテーブルを非正規化する必要がありますか?または、最も多く閲覧された上位10ページを検索する効率的な方法はありますか?

答えて

8
SELECT p.pageid, count(*) as viewcount FROM 
    pages p 
    inner join views v on p.pageid = v.pageid 
    group by p.pageid 
    order by count(*) desc 
    LIMIT 10 OFFSET 0; 

私はこれをテストすることはできませんが、これらの行に沿って何かをテストすることはできません。私はパフォーマンスの制約がある場合を除いて、値を保存しません(私はちょうど "時期尚早の最適化"という言葉を学びました。

+1

+1パフォーマンスに関する問題が発生するまでは、合計を格納しないことに言及してください。 – Thilo

1

おそらく、[ページ]テーブルに[ビュー]列が表示されます。

これは私にとって正規の完全に合理的な破損のようです。特に私はビューを削除すると想像することはできませんので、あなたはカウントが期待外れになるのを期待しません。この場合、参照整合性は非常に重要ではないようです。

1

データベースの正規化はすべて、データを格納する最も効率的な方法です。これはトランザクション処理には適していますが、効率的にデータを再度取得する必要性と直接競合することがよくあります。この問題は、通常、よりアクセスしやすく、前処理されたデータを持つ表(索引、マテリアライズド・ビュー、ロールアップ表...)を導出することによって対処されます。ここでの(少し古い)専門用語はデータウェアハウジングです。

私はあなたのページテーブルを正規化したままにしたいと思っていますが、合計で余分なテーブルがあります。これらのカウントの最近の状況に応じて、元のテーブルを更新するときにテーブルを更新することも、定期的に合計を再計算するバックグラウンドジョブを作成することもできます。

また、実際にパフォーマンスの問題に遭遇した場合にのみ、これを実行することもできます。これは、非常に多数のレコードまたは非常に多数の同時アクセスがある場合を除きます。あなたのコードを柔軟にしておけば、テーブルを持っていなくても切り替えることができます。

0

この場合、非正規化は間違いなく機能します。あなたの損失は余分な列によって消費された追加のストレージルームです。

また、トラフィックが少ない(x期間)たびに、夜間にこの情報を入力するようにスケジュールされたジョブを設定することもできます。

この場合、手動でこのクエリを実行しない限り、ページ数をすぐに知ることができなくなります。

パフォーマンスを向上させるために非正規化を使用することは間違いありません。

--Kris

+0

損失は余分な列であり、一貫性を維持する必要があります。私はそれがこの場合でも正当化されていることに同意する。 – thomasrutter

3

これは、維持しようとしている情報のレベルによって異なります。いつ見た人を記録したいのですか?その後、別のテーブルは正常です。それ以外の場合は、Viewsの列が表示されます。また、別の列を保持すると、各ページビューが対応する行の列を更新しようとするため、テーブルがより頻繁にロックされることがわかります。

Select pageid, Count(*) as countCol from Views 
group by pageid order by countCol DESC 
LIMIT 10 OFFSET 0; 
関連する問題