🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

teratail

teratail(テラテイル)は、プログラミングに特化した日本語Q&Aサイトです。

Q&A

解決済

3回答

2511閲覧

1対多のリレーションシップにおいて関連レコード数を列として保存することの是非

ockeghem

総合スコア11705

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

teratail

teratail(テラテイル)は、プログラミングに特化した日本語Q&Aサイトです。

1グッド

2クリップ

投稿2021/01/20 04:47

編集2021/01/20 06:19

Teratailの質問で、回答が-1件となっているものを見つけました(URL、下図)。
画面キャプチャ
一つの質問に対して回答は複数(0以上)あることから、1対多のリレーションシップということで回答の文書は質問とは別テーブルに保存していると想定されますが、それとは別に、質問テーブルに「現在の回答数」を列として持っているために「負の回答数」が生じたのだろうと解釈しました(この前提が間違っている場合はご指摘ください)。
一般的には、このようなデータ構造の実装として、「回答数」を列として定義するか、回答数の列は持たずに質問に対応する回答を都度数えるという2種類の方針があると思います。

この前提での質問ですが、以下の3点を伺いたいです。2番目の質問は意見が分かれることと予想していますが、様々な意見をいただきたいという意図です。

  1. 「教科書的な実装」では「回答数の列」は持たないものと認識しているが、それは正しいか?
  2. 「回答数を列として持つ」ことは現実の実装としては割とよくあるのか?
  3. このような「データベースの不整合」を未然に防ぐ、あるいは事後に検知する仕組みとしてはどのような候補があるか?

※ なお、teratailを非難する意図はまったくなく、長くサイトを運営していればこの程度の不整合はあるだろうなという貴重なサンプルとして使わせていただいております。

kaina👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

m.ts10806

2021/01/20 04:53 編集

どのように作られているかは設計、開発した人しか分からないので、置いといて、 見た目上どう見ても「おかしい」ので、それはそれで運営に報告したほうが良さそうに思います。 ※既に問い合わせ済みならスルーしてください
ockeghem

2021/01/20 04:58

ご指摘ありがとうございます。運営に報告しました。
m.ts10806

2021/01/20 05:00 編集

確かに出てる件数と実際の投稿数にずれがあるときはありますね。 高評価、低評価の数もそうですけど。当初の設計と運用にずれが出てきてるのかもしれません。 ※質問要件とは外れてしまうので、このあたりでやめときます
yambejp

2021/01/20 06:17

teratailタグがあったと思うので、合わせてつけていただいたほうがよいかもしれません (まぁteratail自体への質問ということではないようですが)
ockeghem

2021/01/20 06:19

ありがとうございます。teratailタグを追加しました
Zuishin

2021/01/20 07:29

私もクリップしていないのにクリップ数がずっと 1 です。新しくクリップして 1 になればいいですが、2 になるようだと更新の仕方に問題があるように思います。
guest

回答3

0

ベストアンサー

「教科書的な実装」では「回答数の列」は持たないものと認識しているが、それは正しいか?

私の認識でも「正しい」という認識です。
理由としては、その場でカウント出来る情報であり、第一正規化によって削除されるべき情報であるからです。
(特に根拠は無いですが)「教科書的な実装」では第三正規形までは正規化するのが良くある形だと思っています。

「回答数を列として持つ」ことは現実の実装としては割とよくあるのか?

単純に回答数だけであればあんまり無いと思いますが(レコード数の前提にもよるとは思います)、
teratailの場合、単純な回答数以外の要素を加味して回答数を導出している様に見えるので、
結果としてとても重い処理になり、(列に持っているか別途KVSに持っているかは不明であるものの)参照専用の、キャッシュデータとして持っている様に思います。

回答数で見かけたのは初めでですが、スコアでは変なデータをしばしば見かけます。

逐次導出が重い処理に関しては参照用のキャッシュデータを持つのはwebサービスにおいては割と良くある実装だとは思います。

このような「データベースの不整合」を未然に防ぐ、あるいは事後に検知する仕組みとしてはどのような候補があるか?

コストが許されるのであれば、キャッシュ作成時に閾値を設けて(今回のケースだと、自然数にならないのはおかしい)、明らかに不自然に見えるデータの場合は再度キャッシュを作成する くらいしか思いつきません。

おそらく、データをいろんなところから引っ張ってきて参照用データを作成する(と思われる)以上、
その瞬間のデータの整合性は取れない&許容するケースなのだと思います。

そのため、検知するよりは、「次回のキャッシュ作成時に自然と治る」を期待する方向かとは思います。

投稿2021/01/20 07:09

tanat

総合スコア18727

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ockeghem

2021/01/20 07:20

ありがとうございます。とても納得性の高い回答です。 回答数はキャッシュかなというのは思いました。ただ、今回の例はかなり古い投稿なので、単純なキャッシュというよりは、「高速参照用に生成されたデータ」という感じで、更新がない限り再生成されないのかなとも思いました。
tanat

2021/01/20 07:34

> ただ、今回の例はかなり古い投稿なので、単純なキャッシュというよりは、「高速参照用に生成されたデータ」という感じで、更新がない限り再生成されないのかなとも思いました。 確かにその可能性が高そうですね。 キャッシュ更新の条件に最終更新時間が含まれている(含めざるを得ない)と言う仕様は自然だと思います。
ockeghem

2021/01/20 08:53

そうですね。更新に合わせてキャッシュを更新するというのは自然ですので、キャッシュという線もありそうですね。
hentaiman

2021/01/20 12:23

> (今回のケースだと、自然数にならないのはおかしい) となった時に > 明らかに不自然に見えるデータの場合は再度キャッシュを作成する と考えるのは一般的ですか? 方向性として、キャッシュの再作成じゃなくてシステム不具合と判断して原因究明・修正・反映というものかと思ってましたが・・・ ※マイナスを許容している時点でシステムとしておかしいとかはおいておき
tanat

2021/01/20 13:47

> > (今回のケースだと、自然数にならないのはおかしい) > となった時に > > 明らかに不自然に見えるデータの場合は再度キャッシュを作成する > と考えるのは一般的ですか? 「色んなところからデータを引っ張ってきて参照用のキャッシュを作る」という仮説の上で割と無理矢理考えた案なので一般的かどうかは微妙なところですが、コストとの兼ね合いで参照系のデータの不整合をどこまで許容できるかの要件次第かなと思います。 というのも、そもそもデータ不整合を全く許さないのであれば、 (仮説であるところの)「色んなところからデータを引っ張ってきて参照用のキャッシュを作る」という手法が許容されないと思うんですよね。 完全な整合性を保証するのであれば、全データにロックをかけるか、スナップショット的な手法でその瞬間の整合性を確保しないといけないわけで、コストが結構かかると思います。 そこまでして完全な整合性を保つよりは、そこそこの低コスト&たまに多少データ不整合が起きても許容するというのが「有り」なケースは結構存在するんじゃないかなと。 今回のケースだと(仮説に仮説を重ねてアレですがw) 「一定以上古いデータで稀に不整合なデータがあったところで殆ど誰も困らないし、 限られたリソースの中で作られる不完全整合性を持つキャッシュの方が重要なので直さない」 という条件を重ねていけば、それなりに一般的に有り得る判断になるかとは思います。
hentaiman

2021/01/20 13:57

もちろんtwitterみたいな規模で「フォロー・フォロワー」をリアルタイムにしない&キャッシュを作るという話なら分かるんですが、この程度の規模で不整合を許容する考え方が一般的なのか気になっての質問でした。 ありがとうございました。 > という条件を重ねていけば たしかに、自分がteratailを触る側だとしたら 無償 && 金を生まない(現状生む可能性が無い) && 無くなっても良い という条件を元に雑でいいやって判断すると思います
tanat

2021/01/20 14:02

多分、えらい赤字垂れ流しながらサービス続けているでしょうし、サーバの規模を縮小しつつ後付けキャッシュでなんとかかんとか捌いている とかは有り得そうな気がしますね。。。
tanat

2021/01/23 05:41

今、元の質問を見てみたら、回答が1件になっていました。 手動で更新されたのか、閲覧数が増えて自動更新の対象になったのかは不明ですが、 何らかのトリガーで計算しなおしはされるようですね
ockeghem

2021/01/23 05:51

こちら、私から通報した結果修正されたようです。
guest

0

それとは別に、質問テーブルに「現在の回答数」を列として持っているために「負の回答数」が生じたのだろうと解釈しました(この前提が間違っている場合はご指摘ください)。

単純にカウント処理が適切でないだけと思います。
例えば自分の場合、タグLaravelがユーザーページのタグ一覧から見ると9位ですが、タグ別ランキングを見ると8位です。
さらに、既に退会済みのユーザーですがLaravelに比較的積極的に回答していて8位よりは上位だったと思われる回答者が過去にいます。

この事から推察出来るのは、存在するユーザーIDを持つレコードをカウント対象とする個所と、なんにも気にせずにレコードをカウント対象とする処理の二つが混在していると思われます。
また、回答データが物理削除であればこのような問題は起きない為、回答は論理削除している事が伺えます。

という事で原因は以下のいずれかかなと

  • リストひとつのシステムを複数人で開発している場合に携わる技術者のレベルに差があった。且つ、単純に無設計(共有できる設計書が無い)状態で作り始めたシステムだから起きたバグ

  • 最初の開発者がその辺りの知識に疎かったので作りが荒いが入れ替わりで入った開発者は整合性を意識できる人だった

投稿2021/01/20 07:54

hentaiman

総合スコア6426

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

  1. 「教科書的な実装」では「回答数の列」は持たないものと認識しているが、それは正しいか?

正しいと思います。二重管理になり、データ不整合が発生するため。

  1. 「回答数を列として持つ」ことは現実の実装としては割とよくあるのか?

列、はどうかと思いますが別テーブルや Redis 等に持っておくのはとてもよくあると思います。
Teratail は規模的に微妙ですが、Twitter で何百万件のいいねやリツイートをその場で数えるのがうまく動く気はしません。

つくりとしては例えば回答なりいいねが発生すると Pub/Sub 的なメッセージを送信し (Amazon SNS・Cloud Pub/Sub 等)、メッセージ受信側で Redis なり別 DB なりの値をインクリメント、というのはどうでしょうか。

  1. このような「データベースの不整合」を未然に防ぐ、あるいは事後に検知する仕組みとしてはどのような候補があるか?

例えば Pub/Sub はメッセージ重複が起こりえますので暫定カウント扱いとし、
バッチなどで1日1回確定データとして真面目に計算しなおすというのは作ったことがあります。
(同時アクセス数が読めないフロント起因でカウントしなおすとそこがボトルネックになるので)

また、回答追加による増加はよくありますが、回答削除による減算それほど頻繁に発生しないこと、
1 になるべきものが 0 でも違和感はないが、-1 になるのは違和感があるため、
減算は暫定データ計算の対象外、というのはありかもしれません。

投稿2021/01/20 07:51

68user

総合スコア2022

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問