データベース内のテーブル間のリレーションを定義する必要がありますか、それともコードで定義する必要がありますか?


60

私の経験では、過去に読んだプロジェクトの多くは、データベースにリレーションシップ定義を持たず、代わりにソースコードでのみ定義していました。だから私は、データベースとソースコード内のテーブル間の関係を定義することの利点/欠点は何なのかと思っていますか?より広範な質問は、カスケード、トリガー、手順などの現代のデータベースの他の高度な機能に関するものです...私の考えにはいくつかのポイントがあります。

データベース内:

  • 設計からの正しいデータ。無効なデータを引き起こす可能性のあるアプリケーションエラーを防ぎます。

  • アプリケーションがデータの整合性をチェックするためにより多くのクエリを作成する必要があるため、データを挿入/更新する際のアプリケーションへのネットワークラウンドトリップを削減します。

ソースコード内:

  • より柔軟。

  • 複数のデータベースにスケーリングする場合は、リレーションがクロスデータベースになることがあるため、より良いです。

  • データの整合性をさらに制御します。データベースは、アプリケーションがデータを変更するたびに確認する必要はありません(複雑さはO(n)またはO(n log n)(?)です)。代わりに、アプリケーションに委任されます。また、アプリケーションでデータの整合性を処理すると、データベースを使用するよりも詳細なエラーメッセージが表示されると思います。たとえば、APIサーバーを作成するときに、データベースでリレーションを定義すると、何かが(参照されているエンティティが存在しないなど)うまくいかない場合、メッセージとともにSQL例外を受け取ります。簡単な方法は、「内部サーバーエラー」が発生したことをクライアントに500で返すことであり、クライアントは何が問題なのかわかりません。または、サーバーはメッセージを解析して何が間違っているのかを判断できます。これはmyい、エラーが発生しやすい方法です。アプリケーションにこれを処理させると、

他に何かありますか?

編集:Kilianが指摘しているように、パフォーマンスとデータの整合性についての私の論点は非常に間違っています。そこで、自分のポイントを修正するために編集しました。データベースで処理できるようにすることは、より効率的で堅牢なアプローチになることを完全に理解しています。更新された質問を確認して、それについて考えてください。

編集:みんなありがとう。私が受け取った答えはすべて、制約/関係をデータベースで定義する必要があることを指摘しています。:)。もう1つ質問がありますが、この質問の範囲外であるため、別の質問として投稿しました:APIサーバーのデータベースエラーを処理します。いくつかの洞察を残してください。


4
「アプリケーションがデータの整合性をチェックするためにさらにクエリを作成する必要があるため」必ずしも。データベースが完全にアプリケーションの制御下にある場合、データの整合性の追加チェックは、過度に防御的なプログラミングになる可能性があります。必ずしも必要ではありません。アプリケーションを適切にテストして、データベースに対して有効な変更のみが行われることを確認してください。

9
忘れてはならないことが1つあります。関係する全員が完全なソフトウェアを作成しない限り、チェックがソフトウェアにある場合、これらのチェックの1つが失敗し、制約が実施されなくなります。それは、もしいつかという問題ではありません。これにより、エラーを再現するのが難しくなり、ソフトウェアで強制された制約に再び適合するようにデータを長時間マッサージすることになります。
ダブ

10
言及する価値がある...データベースに整合性の問題を導入すると、Pandoraのボックスを開くことに似ています。異常に満ちたデータベースに整合性を再導入するのは悪夢です。データベースの厳密な制御を維持するのは面倒かもしれませんが、長期的には多くの苦痛を軽減できます。
DanK

3
ソースコード:最終的には、ほとんどのデータベースを作成することになります。
Blrfl

7
かつて非常に才能のあるプログラマーに、「車のブレーキのようなものです。ポイントは、車の速度を遅くすることではなく、車をより安全にすることです」という質問をしました。制約なしで実行することは可能ですが、不良データが何らかの形で
侵入

回答:


70

TL; DR:関係の制約はデータベースに入れる必要があります。


アプリケーションが十分に大きくありません。

確かに、データベース間で関係を強制するには、アプリケーションでそれらを強制する必要があるかもしれません。

ただし、まず、使用しているデータベースソフトウェアのドキュメントを確認し、既存の製品を確認する必要があることを指摘します。たとえば、PostgresとMySQLの上にクラスタリングオファーがあります。

そして、あなたが持っている必要が終わる場合でも、いくつかのアプリケーションでの検証を、お風呂の水で赤ちゃんを捨てないでください。結局のところ、やらなければならないことが少ないほど、より良い結果が得られます。

最後に、将来のスケーラビリティの問題を心配している場合、とにかくスケーリングする前にアプリケーションに大きな変更を加える必要があると思います。経験則として、10倍に成長するたびに再設計する必要があります。そのため、スケーラビリティの問題を予測できないことに多額の資金を注ぎ込まず、代わりにお金を使って実際にそれらの問題が発生するポイントに到達してください。

アプリケーションが十分に修正されていません。

使用しているデータベースにチェックの実装に欠陥がある可能性と、アプリケーションにチェックの実装に欠陥がある可能性との比較はどうですか?

そして、どれを最も頻繁に変更しますか?

私はいつでもデータベースが正しいと確信しています。

開発者が十分に分散しているとは考えていません。

アプリケーションがデータの整合性をチェックするためにより多くのクエリを作成する必要があるため、データの挿入/更新時のアプリケーションへのネットワークラウンドトリップを削減します。

赤旗1

考えている場合:

  • レコードが存在するかどうかを確認します
  • そうでない場合は、レコードを挿入します

その後、最も基本的な同時実行性の問題に失敗しました。別のプロセス/スレッドが進行中にレコードを追加している可能性があります。

考えている場合:

  • レコードが存在するかどうかを確認します
  • そうでない場合は、レコードを挿入します
  • レコードが複製として挿入されたかどうかを確認します

その後、MVCCを説明できませでした。所有しているデータベースのビューは、トランザクションが開始された時点のスナップショットです。発生しているすべての更新が表示されるわけではなく、コミットされていない場合もあります。

複数のセッションにわたって制約を維持することは本当に難しい問題です。データベースで解決できて嬉しいです。

1 データベースがSerializableプロパティを適切に実装していない場合。しかし、実際にはほとんどありません。


最終:

また、アプリケーションのデータ整合性を処理すると、データベースを使用するよりも詳細なエラーメッセージが表示されます。例:APIサーバーを作成するとき。データベースでリレーションを定義し、何かがおかしくなった場合(参照されたエンティティが存在しないなど)、メッセージとともにSQL例外が発生します。

エラーメッセージを解析しないでください。運用グレードのデータベースを使用する場合、構造化エラーを返す必要があります。少なくとも、エラーの可能性があることを示すエラーコードがあり、このコードに基づいて適切なエラーメッセージを作成できます。

ほとんどの場合、コードで十分であることに注意してください:参照された外部キーが存在しないことを伝えるエラーコードがある場合、このテーブルには1つの外部キーしかないため、問題はコードでわかります。

また、正直に言って、ほとんどの場合、とにかく適切にエラーを処理することはありません。それらの多くがあり、それらすべてを説明するのに失敗するという理由だけで...

...これは、上記の正確性のポイントに関連しているだけです。データベース制約が発生して処理されなかったために「500:Internal Server Error」が表示されるたびに、コードで処理するのを忘れたため、データベースが保存されたことを意味します。


3
ハハ、あなたは私がコメントを書いているときにこれを書いた。私は完全に同意します:非DBコードでは整合性や制約を行うことさえできません。トランザクションは、コミットされるまで他の結果を見ることができません(そうでない場合もあります)。整合性のような錯覚を覚えるかもしれませんが、それはロックによるタイミングまたは深刻なスケーラビリティの問題の影響を受けます。データベースのみがこれを正しく行うことができます。
LoztInSpace

8
すべての良い点。もう1つは、データベース内の関係が自己文書化されていることです。クエリを実行するコードでのみリレーションシップが定義されているデータベースをリバースエンジニアリングしなければならなかった場合、そのようなことをする人は誰でも嫌いでしょう。
キャット

1
@ Kat11:それは本当です。また、自己記述型には、ツールがデータを簡単に理解し、それに基づいて行動できるという利点があります。
マチューM.

1
MVCCに関するあなたの議論は、SERIALIZABLE分離を正しく実装するDBでは正確ではありません(たとえば、多くの主要なRDBMSはそうではありませんが、PostgreSQLの最新バージョンはそうします)。このようなDBでは、最初の単純なアプローチでも正しく動作します。書き込みが競合する場合、それらはシリアル化の失敗としてロールバックされます。
James_pic

1
SERIALIZABLEを正しく実装するDBでは、正常にコミットされたすべてのトランザクションを取得すると、順序付け(壁時計の順序付けとは異なる場合があります)があり、それらをすべて連続して実行した場合(並行性なし)その順序では、すべての結果はまったく同じでした。正しくするのはトリッキーで、SQLの仕様は曖昧なので、SERIALIZABLEレベルで書き込みスキューを許可してもよいと納得できるので、多くのDBベンダーはSERIALIZABLEをSNAPSHOT ISOLATIONとして扱います(私はOracleを見ています)。
James_pic

118

データベースは、アプリケーションがデータを変更するたびにデータの整合性をチェックする必要はありません。

これは、見当違いのポイントです。データベースは、まさにこの目的のために作成されました。データの整合性チェックが必要な場合(そして、必要ないと思われる場合はおそらく間違いです)、データベースにそれらを処理させることは、アプリケーションロジックで行うよりもほぼ確実に効率的でエラーが発生しにくいです。


5
@ dan1111私はあなたのコメントを理解していません...あなたは言っています:単純な制約はデータベースによって強制されているので、アプリケーションコードにとって問題ではありません。より複雑な制約は実装するのが難しいので、それらをあきらめますか?または、データベーストリガー(および同様のメカニズム)を使用して複雑な制約を実装するのは複雑すぎるため、アプリケーションコードに実装する方が良いと言っていますか?
バクリウ

47
非DBコードでは整合性や制約を行うことさえできません。トランザクションは、コミットされるまで他の結果を見ることができません(そうでない場合もあります)。整合性のような錯覚を覚えるかもしれませんが、それはロックによるタイミングまたは深刻なスケーラビリティの問題の影響を受けます。データベースのみがこれを正しく行うことができます。
LoztInSpace

17
逸話的に、@ LoztInSpaceのコメントをフォローするために、私はかつて(ひどい)会社で働いていました。その会社では、DBにIDを自動インクリメントさせるのではなく、アプリケーションが最後の行IDを取得するように設定されていました。それに1つを追加し、それを新しいIDとして使用しました。残念ながら、週に約1回重複IDが挿入され、アプリケーションがクラッシュしました。
Trotski94

9
@ dan1111アプリケーションにバグを書くことはありませんか?
user253751

4
@DavidPacker同意するかもしれませんが、データベースにアクセスする複数のクライアントがいると、データベースに制約を強制することしかできません。つまり、行ごとではなく大規模なテーブルのロックを開始し、パフォーマンスが低下する場合を除きます。
Iker

51

制約はデータベース内にある必要があります(世界最高の意志で)、このデータベースにアクセスするのアプリケーションだけではないからです。

ある時点で、データベース内にスクリプト化された修正が必要になる場合があります。または、展開時にテーブル間でデータを移行する必要がある場合があります。

さらに、「今日の午後、アプリケーションデータベースにインポートされたこの優れたデータシートをビッグカスタマーXが本当に必要としている」など、他の要件が発生する場合があります。時間内に。

これは、データベースレベルの整合性がベーコンを保存する場所です。


さらに、退職後にこの会社であなたの役割を引き受け、データベースの変更を担当する開発者を想像してください。

データベース内にFK制約がない場合、テーブルを変更する前にテーブルの関係を確認できるように、彼はあなたを嫌いますか?(手がかり、答えはイエスです


33
やあ兄弟。私はあなたを伝えることはできませんどのように私は、データベースが持っていることを人々に説明しなければならなかった多くの倍以上のクライアントを!現時点でクライアントが1つだけで、データがシステムに入る手段が1つしかない場合でも、この仮定に基づいてアプリケーションとスキーマを設計することが、Future Yoshiが過去のYoshiを嫌う最善の方法です。
グレッグブルクハート

9
@nikonyrh私はそれをしないだろう。制約があるため、アプリケーションは一貫したデータに依存できます。FKを無効にするのは「ただそれを取得するだけ」です。
水田

5
同意した。また、アプリケーションが唯一のクライアントである場合でも、アプリケーションの異なるバージョンがわずかに異なる制約を適用しようとする可能性があります。通常、陽気が続きます(まあ、そうではありません。「陽気」よりも「カオスと完全なフラストレーション」に似ています)。
Iker

5
これは絶対に証明できます。私の場合、実際には外部キーをサポートしていないMyISAMにとらわれていたため、アプリケーションによって整合性が強制された250 GBのデータになりました。バックログをより管理しやすいサイズにするためにデータのプルーニングを開始し、アプリケーション自体がこれをまったく処理できないことが明らかになったとき、混乱が続きました。なぜ過去形を使用しているのかわかりません。これは現在も発生しおり、問題(2年後)はまだ解決されていません。*スニフ*
モニカとの軽さのレース

1
まともなコードベースがあれば、少なくとも生のSQLを書くのと同じくらい迅速に、アプリケーションの永続化レイヤーを使用して1回限りのスクリプトを簡単に書くことができるはずです。「アプリケーションのコードの変更」は、1回限りのスクリプトには必要ありません。
ジョナサンキャスト

17

データベースにリレーションが必要です。

他の回答にあるように、制約チェックのパフォーマンスは、アプリケーション内よりもデータベース内ではるかに優れています。データベース制約チェックは、データベースが得意とするものの1つです。

追加の柔軟性が必要な場合(たとえば、データベースの相互参照を記録するなど)は、慎重に考慮して制約を削除できます。データベース内で一貫性があるということは、これらの制約を変更するオプションと、参照整合性の確実性があることを意味します。


1
本当です。制約チェックのパフォーマンスは、アプリケーションよりもデータベースでの方が適切に処理されると述べたはずです。
カークブロードハースト

13
  • 私たちはもはや1つのバックエンド<-> 1つのフロントエンドの世界に住んでいません。
  • ほとんどのソリューションには、Webフロントエンド、モバイルフロントエンド、バッチフロントエンド、iPadフロントエンドなどが含まれます。
  • データベースエンジンには、参照整合性を強制するために最適化された数千のコード行が既にあります。

書くべき問題を解決するドメインの問題があるときに、コードを強制する参照整合性を書いてテストする余裕は本当にありますか?


2
「私たちはもはや1つのバックエンド<-> 1つのフロントエンドの世界に住んでいません。」私たちは今までに?数年前、私は少なくとも20種類以上の異なる言語で作成されたプログラムがアクセスするデータベースシステムで作業していました。一部のプログラムは、1970年代に初めてリリースされました。
マイクシェリル 'キャットリコール'

2

データベースレベルでデータの整合性、制約、関係などを検証しない場合、本番データベースにアクセスできる人(DBアクセスツールを含む他のクライアントを使用)がデータを台無しにするのがはるかに簡単になります。

データベースレベルで可能な限り厳密なデータの整合性を強制することは素晴らしい習慣です。私を信じてください、これはどんな些細なシステムでも時間をかけてあなたに大きな頭痛を保存します。また、慎重に考えれば、アプリケーションの論理エラーやビジネス要件のエラーや不整合をすばやく見つけることができます。

これの補足として、可能な限り正規化されたアトミックな方法でデータベースを設計します。「神」テーブルはありません。データベースをできるだけシンプルに設計するために多大な労力を費やしてください。理想的には、個々の非常に明確に定義された単一の責任で、すべての列で慎重に検証される多くの小さなテーブルを使用します。データベースは、データの整合性の最後の保護者です。それは城の砦を表します。


1

ほとんどの人は基本的に「はい、一般的に言っているあなたのなかれ常にデータベース内のリレーションを定義します」。しかし、コンピュータサイエンスの分野が非常に簡単であれば、「ソフトウェアエンジニア」ではなく「ソフトウェアマニュアルリーダー」と呼ばれます。実際に、そうすべきでない正当な理由がない限り、制約をデータベースに入れることに同意するので、特定の状況で適切と考えられるいくつかの理由を提供します。

重複コード

データベースによって処理できる一定量の機能が、アプリケーションコードに自然に存在する場合があります。データベースに制約のようなものを追加することが冗長になる場合、DRYの原則に違反しているため、機能を複製しない方が良いかもしれません。また、データベースとアプリケーションコードの同期を保つジャグリング行為を悪化させるかもしれません。

努力

データベースが高度な機能を使用せずに必要なことを既に実行している場合、時間、お金、および労力をどこに配置すべきかを評価することができます。制約を追加することで壊滅的な障害を防ぎ、ひいては会社のコストを大幅に節約できる場合は、おそらくそれだけの価値があります。保持する必要があるが、違反しないことがすでに保証されている制約を追加する場合、時間を浪費し、コードベースを汚染します。ここで保証されているのは有効な言葉です。

効率

通常、これは正当な理由ではありませんが、場合によっては特定のパフォーマンス要件が必要になることがあります。アプリケーションコードが特定の機能をデータベースよりも高速に実装でき、追加のパフォーマンスが必要な場合は、アプリケーションコードに機能を実装する必要があります。

コントロール

効率にある程度関連しています。機能の実装方法について非常にきめ細かな制御が必要な場合があり、データベースでそれを処理することで、開く必要があるブラックボックスの後ろに隠されます。

クロージングポイント

  • データベースはコードで記述されています。独自のコードではできないことで、彼らが行う魔法はありません。
  • 無料ではありません。制約、関係などはすべてCPUサイクルを使用します。
  • NoSQLの世界の人々は、従来のリレーショナル機能がなくてもうまくいきます。たとえばMongoDBでは、JSONドキュメントの構造はデータベース全体をサポートするのに十分です。
  • 高度なデータベース機能を盲目的かつ無知に使用しても、メリットは保証されません。何かを誤って機能させて、後で壊れる可能性があります。
  • 特定の要件や制約をリストせずに、非常に一般的な質問をしました。あなたの質問に対する本当の答えは「依存する」です。
  • これが企業規模の問題であるかどうかを指定しませんでした。他の答えは、顧客やデータの整合性などについて語っていますが、時にはそれらのことは重要ではありません。
  • 従来のSQLリレーショナルデータベースについて話していると思います。
  • 私の見方は、小さな(最大50テーブル)プロジェクトで大量の制約と外部キーを使用することから脱却し、欠点に気づかないことです。

私が最後に言うことは、データベースに機能を配置するべきではないかどうかを知ることです。よくわからない場合は、データベース機能を使用することをお勧めします。通常、データベース機能は非常にうまく機能するためです。


1
人々がドグマに同意しないためによく考えられた答えを下票すると、SE StackExchangeはより悪い場所になります。
カールレス

5
この回答の前提は、DBから制約を除外する場合もあるという前提がありますが、説明は不十分であり、誤解を招く可能性があります。データベースはいくつかの制約に最適な場所ではないことに同意しますが、リレーショナルデータベースは基本キーおよび参照整合性制約なしではいけませんなし。これには例外はありません。すべてのデータベースには主キーが必要になり、大多数は外部キーが必要になります。これらは、たとえロジックが重複していても、データベースによって常に実施される必要あります。あなたがこれをglossめているという事実は、私が投票した理由です。
jpmc26

1
「データベースはコードで記述されています。独自のコードで実行できない魔法はありません。」、いいえ、アプリケーションコードで参照整合性を強制することはできません(強制する必要がない場合、なぜデータベースサーバーをまったく使用しないのですか?)。コードができることではなく、どこでできるかということです。
ハイド

0

いつものように、多くの答えがあります。私にとっては、単純なルールを見つけました(それはモデル中心のアプローチでのみ機能します)。通常、私はアプリケーションの異なる層にのみ焦点を合わせます。

モデルが複数のエンティティで構成され、エンティティ間に依存関係がある場合、永続化レイヤーはそれらの依存関係をその可能性とともに反映する必要があります。したがって、RDBMSを使用している場合は、外部キーも使用する必要があります。理由は簡単です。そうすれば、データは常に構造的に有効です。

この永続層で作業を行うインスタンスはすべて、これに依存できます。インターフェース(サービス)を介してこのレイヤーをカプセル化していると思います。だから、ここでデザインが終わり、現実の世界が始まります。

ポイント、特にデータベース間の参照を確認します。その場合、はい、RDBMS自体ではなくサービスに実装された参照が存在する必要があります。しかし、この方法に従う前に、設計中にすでにこれを検討する方が良いと思いませんか?

別のDBに保存する必要のあるパーツがあることを既に知っている場合は、そのパーツを既にそこに置いて別のモデルとして定義することができます。右?

また、コードでこれを実装するより柔軟であることを指摘しています。でも、不完全なデザインを扱っているように聞こえませんか?自問してみてください。なぜもっと柔軟性が必要なのですか?

DB整合性チェックによるパフォーマンスの問題は現実的ではありません。RDBMSは、ユーザーによる実装よりもはるかに高速にこのようなことを確認できます。どうして?メディアの混乱に対処する必要がありますが、RDBMSは対処しません。そして、その統計を使用して、そのようなチェックを最適化できます

ご覧のとおり、すべてデザインに戻ります。もちろん言うことができますが、未知の要件が現れた場合、ゲームチェンジャーはどうでしょうか?はい、それは起こるかもしれませんが、そのような変更は麻生として設計され、計画されるべきです。; o)


0

いくつかの非常に良い答えがありますが、さらにいくつかのポイントがあります

データの整合性は、データベースが実行するために設計されたものです

アプリケーションレベルでFK削除のような適切な同時実行を行うことは恐ろしいことです

データ整合性の専門知識はDBAにあります

プログラムレベルでは、挿入、更新、一括更新、一括挿入、一括削除...
シンクライアント、シッククライアント、モバイルクライアント....
データの整合性はプログラマの専門知識ではありません-多くの重複コードと誰かが混乱するそれを

あなたがハッキングされたとしましょう-あなたはどちらの方法でも問題を抱えていますが、データベースに完全性保護がない場合、ハッカーは小さな穴を介して多くの損害を与えることができます

SQLまたはTSQLを介してデータを直接操作する必要がある場合があり
ます。すべてのデータルールを覚えている人はいません


0

あなたの質問は意味がありません。データベースを変更できる場合はコードであり、データベースを変更できない場合は、他の場所で制約を作成する必要があります。

変更できるデータベースは、ruby、javascript、c#、adaの行と同じくらいのコードです。

システムのどこに制約を置くかについての質問は、信頼性、コスト、開発の容易さを要約する必要があります。


0

ここにはたくさんの良い答えがあります。さらに、言語Yで記述されたアプリがある場合、データベース制約のようなコードをYで作成できます。その後、誰かが言語Zを使用してデータベースにアクセスしたい場合は、同じコードを再度記述する必要があります。実装がまったく同じでない場合、神はあなたを助けます。または、知識のあるビジネスユーザーがMicrosoft Accessを使用してデータベースに接続する場合。

私の経験では、人々がデータベースの制約を使いたくないのは、実際には何か間違った方法でやろうとしているからです。たとえば、データをバルクロードしようとしており、しばらくの間、null以外の列をnullのままにします。非ヌル制約を重大にした状況は「この場合はおそらく発生しない」ため、「後で修正する」ことを意図しています。別の例として、2つの異なるタイプのデータを同じテーブルに挿入しようとしている場合があります。

より経験のある人は一歩後退し、制約を回避しようとすることを伴わない解決策を見つけます。解決策は、ビジネスが変化したため、制約がもはや適切ではないことだけです。

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