アプリケーション開発者によるデータベース開発の誤り[終了]


566

アプリケーション開発者が犯す一般的なデータベース開発の間違いは何ですか?


回答:


1002

1.適切なインデックスを使用しない

これは比較的簡単な方法ですが、それでも常に発生します。外部キーにはインデックスが必要です。でフィールドを使用しているWHERE場合は、(おそらく)インデックスが必要です。このようなインデックスは、多くの場合、実行する必要のあるクエリに基づいて複数の列をカバーする必要があります。

2.参照整合性を強制しない

データベースはここで異なる場合がありますが、データベースが参照整合性をサポートしている場合(つまり、すべての外部キーが存在するエンティティを指すことが保証されている場合)、それを使用する必要があります。

MySQLデータベースでこの障害が発生するのはよくあることです。MyISAMでサポートされているとは思いません。InnoDBはそうします。MyISAMを使用している人や、InnoDBを使用しているがとにかくそれを使用していない人を見つけるでしょう。

詳細はこちら:

3.代理(技術)主キーではなく自然な主キーを使用する

自然キーは、(表向きに)一意である、外部的に意味のあるデータに基づくキーです。一般的な例は、製品コード、2文字の州コード(US)、社会保障番号などです。代理または技術主キーは、システムの外部ではまったく意味がないものです。これらは純粋にエンティティを識別するために発明され、通常は自動インクリメントフィールド(SQL Server、MySQL、その他)またはシーケンス(特にOracle)です。

私の意見では、常に代理キーを使用する必要があります。この問題はこれらの質問で出てきました:

これは、議論の余地のあるトピックであり、普遍的な合意は得られません。自然キーで問題がないと考える人もいますが、間違いなく不必要である以外に、代理キーに対する批判はありません。あなたが私に尋ねるなら、それはかなり小さな欠点です。

存在しなくなる可能性があることを覚えておいてください(たとえば、ユーゴスラビア)。

4.動作する必要DISTINCTがあるクエリを書く

これはORMで生成されたクエリでよく見られます。Hibernateからのログ出力を見ると、すべてのクエリが次で始まることがわかります。

SELECT DISTINCT ...

これは、重複した行を返さないようにして、重複したオブジェクトを取得しないようにするためのちょっとしたショートカットです。あなたは時々、これをしている人々も見るでしょう。あなたがそれをあまりにも多く見るならば、それは本当の赤い旗です。それDISTINCTは悪くないか、有効なアプリケーションを持っていません。それは(両方の点で)行いますが、正しいクエリを作成するための代理または一時的なギャップではありません。

私はDISTINCT嫌い

私の意見では事態が悪化し始めているのは、開発者が実質的なクエリを作成し、テーブルを結合しているときに、突然、彼は重複(またはそれ以上)の行と即時の応答を取得しているように見えることに気づきました...この「問題」に対する彼の「解決策」は、DISTINCTキーワードを使用 して、彼のすべての問題を解決することです。

5.結合よりも集約を優先する

データベースアプリケーション開発者によるもう1つの一般的な間違いはGROUP BY、結合と比較してはるかに高価な集計(つまり、句)を比較できないことです。

これがどれだけ広まっているかを理解するために、ここでこのトピックについて何度か書いてきましたが、多くの反対意見が出されました。例えば:

SQLステートメントから-「結合」対「グループ化および所有」

最初のクエリ:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

クエリ時間:0.312秒

2番目のクエリ:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

クエリ時間:0.016秒

そのとおり。私が提案した結合バージョンは、集約バージョンよりも20倍高速です。

6.ビューを通じて複雑なクエリを単純化しない

すべてのデータベースベンダーがビューをサポートしているわけではありませんが、ビューをサポートしている場合は、慎重に使用すればクエリを大幅に簡略化できます。たとえば、あるプロジェクトでは、CRMの一般的なPartyモデルを使用しました。これは非常に強力で柔軟なモデリング手法ですが、多くの結合につながる可能性があります。このモデルには次のものがありました。

  • :人と組織;
  • パーティーの役割:従業員や雇用主など、それらのパーティーが行ったこと。
  • パーティーの役割関係:それらの役割が互いにどのように関連しているか。

例:

  • TedはPersonであり、Partyのサブタイプです。
  • Tedには多くの役割があり、そのうちの1つは従業員です。
  • Intelは組織であり、Partyのサブタイプです。
  • インテルには多くの役割があり、そのうちの1つは雇用主です。
  • IntelはTedを採用しています。つまり、それぞれの役割には関係があります。

したがって、テッドを雇用主にリンクするために5つのテーブルが結合されています。すべての従業員は個人(組織ではない)であると想定し、次のヘルパービューを提供します。

CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id

そして、突然、必要なデータの非常に単純なビューが得られますが、非常に柔軟なデータモデルに基づいています。

7.入力を無害化しない

これは巨大なものです。今はPHPが好きですが、何をしているのかわからない場合は、攻撃に対して脆弱なサイトを作成するのは非常に簡単です。小さなボビーテーブルの話ほど、それをうまくまとめたものはありません。

URL、フォームデータ、Cookieを使用してユーザーが提供するデータは、常に敵対的で無害化されたものとして扱う必要があります。期待どおりの結果が得られていることを確認してください。

8.準備済みステートメントを使用しない

準備済みステートメントとは、クエリをコンパイルして、挿入、更新、およびWHERE句で使用されるデータを差し引いて、後で提供する場合です。例えば:

SELECT * FROM users WHERE username = 'bob'

SELECT * FROM users WHERE username = ?

または

SELECT * FROM users WHERE username = :username

プラットフォームによって異なります。

これを実行することでデータベースがひどい目に遭うのを見てきました。基本的に、最新のデータベースが新しいクエリに遭遇するたびに、それをコンパイルする必要があります。以前に見たクエリに遭遇した場合は、データベースに、コンパイルされたクエリと実行プランをキャッシュする機会を与えています。多くのクエリを実行することで、データベースにそれを把握し、それに応じて最適化する機会をデータベースに与えます(たとえば、コンパイルされたクエリをメモリに固定することによって)。

準備されたステートメントを使用すると、特定のクエリが使用される頻度に関する意味のある統計も得られます。

準備済みステートメントは、SQLインジェクション攻撃からの保護も強化されます。

9.十分に正規化していない

データベースの正規化は、基本的にはデータベース設計を最適化するプロセス、またはデータをテーブルに編成する方法です。

ちょうど今週、私は誰かが配列を爆破してデータベースの単一のフィールドに挿入したコードに遭遇しました。それを正規化すると、その配列の要素は子テーブルの個別の行として扱われます(つまり、1対多の関係)。

これは、ユーザーIDのリストを保存するための最良の方法でも発生しました。

他のシステムでは、リストがシリアライズされたPHP配列に格納されているのを見てきました。

しかし、正規化の欠如にはさまざまな形があります。

もっと:

10.正規化しすぎ

これは前の点と矛盾しているように見えるかもしれませんが、正規化は多くの場合と同様にツールです。それは目的そのものであり、目的ではありません。多くの開発者はこれを忘れて、「手段」を「終わり」として扱い始めると思います。ユニットテストは、この典型的な例です。

私はかつて、次のようなクライアントのための巨大な階層を持つシステムに取り組みました。

Licensee ->  Dealer Group -> Company -> Practice -> ...

つまり、意味のあるデータを取得する前に、約11のテーブルを結合する必要がありました。これは、あまりにも多く行われた正規化の良い例でした。

要点は、注意深く検討された非正規化はパフォーマンスに大きなメリットをもたらす可能性がありますが、これを行う場合は十分に注意する必要があります。

もっと:

11.排他的なアークの使用

排他的弧は、テーブルが2つ以上の外部キーで作成され、そのうちの1つだけが非nullになる可能性がある一般的な間違いです。 大ミス。 1つには、データの整合性を維持することがはるかに困難になります。結局のところ、参照整合性があっても、これらの外部キーの2つ以上の設定を妨げるものはありません(複雑なチェック制約にもかかわらず)。

以下からのリレーショナルデータベース設計の実用ガイド

コードを書くのが面倒で、メンテナンスがより困難になるという正当な理由から、可能な限り排他的なアークの構築はしないよう強くお勧めします。

12.クエリのパフォーマンス分析をまったく行わない

プラグマティズムは、特にデータベースの世界で最高権力を握っています。原則に固執して、それがドグマになった場合は、おそらく間違いを犯しているでしょう。上記の集計クエリの例を見てみましょう。集約バージョンは「見栄えがいい」ように見えるかもしれませんが、そのパフォーマンスは悲惨です。パフォーマンスの比較は議論を終わらせるべきでした(しかし、それはしませんでした)より多くの点で:最初にそのような悪質な情報のあるビューを噴出することは無知であり、危険ですらあります。

13. UNION ALL、特にUNIONコンストラクトへの過度の依存

SQL用語のUNIONは、合同なデータセットを連結するだけです。つまり、同じタイプと同じ数の列を持っています。それらの違いは、UNION ALLは単純な連結であり、可能な限り優先される必要があるのに対し、UNIONは重複したタプルを削除するために暗黙的にDISTINCTを実行することです。

UNITINはDISTINCTのようにその場所を持っています。有効なアプリケーションがあります。しかし、特にサブクエリでそれらの多くを実行している場合は、おそらく何かが間違っています。これは、クエリの構成が不十分であるか、データモデルが適切に設計されておらず、そのようなことを強いられている場合です。

UNIONは、特に結合または従属サブクエリで使用される場合、データベースを不自由にする可能性があります。可能な限りそれらを避けるようにしてください。

14.クエリでのOR条件の使用

これは無害に見えるかもしれません。結局、ANDはOKです。または大丈夫でしょうか?違う。基本的にAND条件を制限し、一方、データセットをOR条件は、成長ではなく、最適化に役立つ方法でそれを。特に、さまざまなOR条件が交差する可能性があるため、オプティマイザが結果のDISTINCT操作を効果的に実行する必要がある場合。

悪い:

... WHERE a = 2 OR a = 5 OR a = 11

より良い:

... WHERE a IN (2, 5, 11)

これで、SQLオプティマイザが最初のクエリを2番目のクエリに効果的に変換できます。しかし、そうではないかもしれません。しないでください。

15.高性能ソリューションに適したデータモデルを設計していない

これは、定量化するのが難しいポイントです。それは通常、その効果によって観察されます。比較的単純なタスクに対して危険なクエリを作成している場合や、比較的単純な情報を見つけるためのクエリが効率的でない場合は、おそらくデータモデルが不十分です。

いくつかの点で、このポイントは以前のすべてを要約していますが、クエリの最適化などの処理は、2番目に実行する必要があるときに最初に実行されることが多いという、より注意深い話です。何よりもまず、パフォーマンスを最適化する前に、適切なデータモデルがあることを確認する必要があります。クヌースが言ったように:

時期尚早の最適化はすべての悪の根源です

16.データベーストランザクションの不適切な使用

特定のプロセスのすべてのデータ変更はアトミックである必要があります。つまり、操作が成功すると、完全に成功します。失敗した場合、データは変更されません。-「半完成」の変更の可能性があってはなりません。

理想的には、これを実現する最も簡単な方法は、システム全体の設計が、単一のINSERT / UPDATE / DELETEステートメントを通じてすべてのデータ変更をサポートするように努力することです。この場合、データベースエンジンが自動的に行う必要があるため、特別なトランザクション処理は必要ありません。

ただし、データを一貫した状態に保つためにプロセスが1つの単位として複数のステートメントを実行する必要がある場合は、適切なトランザクション制御が必要です。

  • 最初のステートメントの前にトランザクションを開始します。
  • 最後のステートメントの後でトランザクションをコミットします。
  • エラーが発生した場合は、トランザクションをロールバックします。そして、非常に注意してください!エラーの後に続くすべてのステートメントをスキップ/中止することを忘れないでください。

この点に関して、データベース接続層とデータベースエンジンがどのように相互作用するかについて、細心の注意を払うこともお勧めします。

17.「セットベース」のパラダイムを理解していない

SQL言語は、特定の種類の問題に適した特定のパラダイムに従います。ベンダー固有のさまざまな拡張機能にもかかわらず、言語は、Java、C#、Delphiなどの言語では些細な問題に対処するのに苦労しています。

この理解の欠如は、いくつかの方法で現れます。

  • データベースに手続きロジックまたは命令ロジックを不適切に課すこと。
  • カーソルの不適切または過度の使用。特に、単一のクエリで十分な場合。
  • 複数行の更新で影響を受ける行ごとに1回トリガーが起動すると誤って想定します。

責任の明確な分担を決定し、適切なツールを使用して各問題を解決するよう努めます。


9
外部キーに関するMySQLステートメントでは、MyISAMがそれらをサポートしていないことは正しいですが、MyISAMを使用するだけでは設計が良くないことを意味します。私がMyISAMを使用した理由は、InnoDBがフルテキスト検索をサポートしていないことであり、それが不当であるとは思わない。
Derek H

1
#6について質問します。このようなビューを使用することは、私の好きなことの1つですが、最近、恐ろしいことに、基になるテーブルのMySQLインデックスでは、ビューの構造がマージアルゴリズムの使用を許可する場合にのみ従うことを学びました。それ以外の場合は、一時テーブルが使用され、すべてのインデックスが役に立たなくなります。一連の操作がこの動作の原因であることを理解すると、さらに警戒感が高まります。これは、.01秒のクエリを100秒のクエリに変換する優れた方法です。ここの他の誰かがこれについての経験がありますか?次のコメントのリンクを確認してください。
ピーターベイリー

5
#3に完全に同意しない。はい、国は存在しなくなる可能性がありますが、国コードは引き続き同じものを表します。通貨コードまたは米国の州と同じです。これらの場合に代理キーを使用するのは無意味であり、追加の結合を含める必要があるため、クエリのオーバーヘッドが増加します。私はあなたが言うことをする方が安全であると言うでしょう、おそらくユーザー固有のデータ(したがって、ない国、通貨やアメリカの州)のサロゲートを使用するべきです。
Thomas

1
RE:#11データの整合性を適用するために必要なチェック制約は簡単です。その設計を回避する他の理由がありますが、「複雑な」チェック制約の必要性はそれらの1つではありません。
Thomas

2
#3では正直ではありません。「あなたがそれを必要としないかもしれない」よりも人工キーには多くの欠点があります。具体的には、自然キーを使用すると、テーブル内のデータがディスクに書き込まれる順序を制御できます。テーブルのクエリ方法がわかっている場合は、同時にアクセスされる行が同じページで終了するようにインデックスを作成できます。さらに、一意の複合インデックスを使用してデータの整合性を適用できます。これが必要な場合は、人工キーインデックスに加えて追加する必要があります。上記の複合インデックスがあなたのpkeyである場合、2羽の鳥が1石で殺されます。
シェーンH

110

開発者による主要なデータベース設計とプログラミングの誤り

  • 利己的なデータベースの設計と使用法。 多くの場合、開発者はデータベースをデータ内の他の利害関係者のニーズを考慮せずに個人の永続オブジェクトストアとして扱います。これは、アプリケーションアーキテクトにも適用されます。データベースの設計とデータの整合性が低いと、サードパーティがデータを操作することが難しくなり、システムのライフサイクルコストが大幅に増加する可能性があります。レポートとMISは、アプリケーション設計のいとこではない傾向があり、後付けとしてのみ行われます。

  • 非正規化データの悪用。非正規化データをやりすぎて、アプリケーション内でそれを維持しようとすることは、データの整合性の問題のレシピです。非正規化は慎重に使用してください。クエリに結合を追加したくないことは、非正規化の言い訳にはなりません。

  • SQLを書くのが怖い。 SQLはロケット科学ではなく、実際にはその仕事をするのが非常に得意です。O / Rマッピングレイヤーは、単純でそのモデルによく適合する95%のクエリを実行するのに非常に適しています。場合によっては、SQLが最適な方法です。

  • 独断的な「ストアドプロシージャなし」ポリシー。 ストアドプロシージャが悪であると信じるかどうかに関係なく、このような独断的な態度はソフトウェアプロジェクトには当てはまりません。

  • データベース設計を理解していない。 正規化はあなたの友人であり、ロケット科学ではありません。 結合とカーディナリティーはかなり単純な概念です。データベースアプリケーションの開発に携わっている場合、それらを理解しない言い訳はありません。


2
トランザクションはトランザクションデータベースで実行し、レポートとMISは別の分析データベースで実行する必要があると主張する人もいます。したがって、両方の長所を手に入れ、誰もが幸せになります(前者から後者を構築するためにデータ変換スクリプトを作成する必要がある貧弱なマグを除く)。
Chris Simpson、

ETLの作成が不十分なマグカップだけでなく、システムのデータを使用している人、いくつかの主要な関係が実際にソースで記録されていないためにボックス化されたMISアプリケーションの質の低いデータ、続いて発生するエンドレスリコンシリエーションバンファイトに関与している人データ品質が悪いため。
ConcernedOfTunbridgeWells

ポイント1にこれ以上反対することはできません。データベースは永続化のためのものであり、プロセス間通信のためのものではありません。ほとんどの場合、その問題にはより良い解決策があります。明示的な要件がない限り、アプリケーションを除いて誰もデータベースを使用しないかのように、データベースを絶対に扱う必要があります。明示的な要件がある場合でも、ユーザーストーリーと根本原因の分析を行うと、要求者の意図を満たすためのより優れた方法が見つかることがよくあります。次に、CQRSというフレーズがいくぶん一般的である会社で働いています
George Mauer

3
簡単な例:私は保険契約管理システムを持っており、500万件の保険金請求の状態を出再保険システムにロードして、潜在的な回収を計算する必要があります。システムは古いクライアントサーバーCOTSパッケージであり、古いメインフレームシステムとのインターフェイスとしても設計されています。どちらも、財務管理のために調整する必要があります。このジョブは、月に1回行われます。あなたの論理によって、私は要件を定義する一連のユーザーストーリーを書き、ベンダーに既存の製品にWebサービスラッパーを追加することについて引用するように依頼します。
ConcernedOfTunbridgeWells 2010

2
次に、DBAは怠惰または無能です。
ConcernedOfTunbridgeWells

80
  1. データベーススキーマでバージョン管理を使用していない
  2. ライブデータベースに対して直接作業する
  3. より高度なデータベースの概念(インデックス、クラスター化インデックス、制約、マテリアライズドビューなど)を読んで理解していない
  4. スケーラビリティのテストに失敗します... 3行または4行のテストデータだけでは、実際のライブパフォーマンスの実態がわかりません

1
2つ目は、#1と#2です。DBに変更を加えるときはいつでも、そのスキーマをダンプしてバージョン管理します。私は3つのデータベースをセットアップしています。開発データベース、ステージングデータベース、およびライブデータベースです。ライブDBで「テスト」されることはありません。
Ixmatus

ここRed Gateでは、SQLソース管理を使用して最初のポイントを改善するための措置を講じました。私が研究中に行った会話から、人々はもう本番データベースに対して開発を行っていないと思いますが、多くの場合、「緊急」の修正が行われ、通常は開発環境に戻る方法が見つかります。これは別の問題です。
David Atkinson

46

ストアドプロシージャへの過度の使用や依存。

一部のアプリケーション開発者は、ストアドプロシージャを中間層/フロントエンドコードの直接の拡張と見なしています。これは、Microsoftスタック開発者(私はその1つですが、私はそれから成長しました)に共通の特性であると思われ、複雑なビジネスロジックとワークフロー処理を実行する多くのストアドプロシージャを生成します。これは他の場所で行う方がはるかに優れています。

ストアドプロシージャは、実際の技術的要因がその使用を必要とすることが実際に証明されている場合(たとえば、パフォーマンスやセキュリティ)に役立ちます。

私は最近、ビジネスロジックとルールの70%が1400 SQL Serverストアドプロシージャ(UIイベントハンドラーの残りの部分)に実装された大規模なDelphiデスクトップアプリケーションの維持と強化を支援する必要がありました。これは、主にTSQLへの効果的なユニットテストの導入の難しさ、カプセル化の欠如、不十分なツール(デバッガー、エディター)による悪夢でした。

過去にJavaチームと協力して、私はすぐに、その環境ではまったく正反対のことが成り立つことがすぐにわかりました。Javaアーキテクトがかつて私に言った:「データベースはデータ用であり、コードではありません。」

最近では、ストアドプロシージャをまったく考慮しないのは間違いだと思いますが、有用な利点を提供する状況では(デフォルトではなく)慎重に使用する必要があります(他の回答を参照)。


4
ストアドプロシージャは、それらが使用されるすべてのプロジェクトで問題の島になる傾向があるため、一部の開発者は「ストアドプロシージャなし」というルールを作成します。したがって、それらの間にオープンな競合があるように見えます。あなたの答えは、実際にどちらの方法を選択するかについての良い例になります。
ウォーレンP

利点:セキュリティ-アプリケーションに「...から*を削除」する機能を与える必要はありません。微調整-DBAは、アプリケーション全体を再コンパイル/デプロイする必要なく、クエリを微調整できます。分析-データモデルを変更した後、多数のprocを再コンパイルして、それらがまだ有効であることを確認するのは簡単です。そして最後に、SQLがデータベースエンジン(アプリケーションではなく)によって実行されることを考えると、「データベースはデータ用であり、コードではない」という概念は単に遅れています。
NotMe

それで、操作されているデータから切り離されたUIにビジネスロジックを組み込みますか?これは、特にUIからの往復ではなく、データベースサーバーによって実行される場合にデータ操作が最も効率的であるため、それほど良い考えのようには思えません。これは、データベースがそのデータを制御していることに依存することができず、異なるデータ操作が行われているUIの異なるバージョンが存在する可能性があるため、アプリケーションの制御がより困難であることも意味します。良くない。ストアドプロシージャを使用しない限り、自分のデータには何も触れさせません。
デビッドT.マックネット

ビジネスロジックをUIから分離する必要がある場合は、多層アーキテクチャを使用できます。または、さまざまなアプリ/ UIで使用されるビジネスオブジェクトとロジックを含むライブラリ。ストアドプロシージャはデータ/ビジネスロジックを特定のデータベースにロックします。この場合、データベースの変更は非常にコストがかかります。そして、莫大なコストは悪いです。
あまりにも

@too:ほとんどの場合、データベースの変更は非常にコストがかかります。特定のDBMSが提供するパフォーマンスとセキュリティの機能を失うことを忘れないでください。さらに、層を追加すると複雑さが増し、パフォーマンスが低下し、追加の層が特定の言語に関連付けられます。最後に、使用されている言語がデータベースサーバーよりも変更される可能性が高くなります。
NotMe 2011

41

一番の問題?彼らはおもちゃのデータベースでのみテストします。したがって、データベースが大きくなったときにSQLがクロールすることを彼らは理解しておらず、誰かが一緒に来て修正する必要があります(聞こえる音は私の歯を磨く音です)。


2
データベースのサイズは重要ですが、より大きな問題は負荷です。実際のデータセットでテストしたとしても、データベースに本番負荷がかかっているときはクエリのパフォーマンスをテストしていません。
davidcl 2011年

データベースのサイズは、負荷よりも大きな問題だと思います。メモリ内にあるため、データベース全体のフィット感、テスト中にパフォーマンスの問題ではありませんでし-私は行方不明の重要な指標があったことを、何度も見てきた
ダニューブセーラー


28

相関サブクエリが原因のパフォーマンスの低下

ほとんどの場合、相関サブクエリは避けたいものです。サブクエリ内に、外部クエリからの列への参照がある場合、サブクエリは相関しています。これが発生した場合、サブクエリは返された行ごとに少なくとも1回実行され、相関サブクエリを含む条件が適用された後に他の条件が適用されると、さらに実行される可能性があります。

不自然な例とOracleの構文は許してください。しかし、ある店舗で1日の売上が$ 10,000未満になったときから、いずれかの店舗で雇用されたすべての従業員を検索したいとします。

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

この例のサブクエリは、store_idによって外部クエリに関連付けられ、システム内のすべての従業員に対して実行されます。このクエリを最適化できる1つの方法は、サブクエリをインラインビューに移動することです。

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

この例では、from句のクエリはインラインビュー(これもOracle固有の構文)であり、一度だけ実行されます。データモデルによっては、このクエリはおそらくはるかに速く実行されます。従業員数が増えるにつれて、最初のクエリよりもパフォーマンスが向上します。最初のクエリは、従業員が少なく店舗数が多く(おそらく、店舗の多くに従業員がいない場合)、daily_salesテーブルにstore_idのインデックスが作成されている場合、実際にはパフォーマンスが向上します。これはありそうなシナリオではありませんが、相関クエリが代替クエリよりもパフォーマンスが優れている可能性があることを示しています。

ジュニア開発者がサブクエリを何度も関連付けているのを見てきましたが、通常、パフォーマンスに深刻な影響を与えています。ただし、相関サブクエリを削除する場合は、パフォーマンスの低下を防ぐために、前後の説明計画を必ず確認してください。


1
すばらしい点であり、関連する点の1つを強調するには、変更をテストします。EXPLAIN PLANの使用方法(および、データベースがクエリを実行するために実際に行っていること、およびコストを確認する方法)を学び、大規模なデータセットでテストを実行し、最適化のためにSQLを過度に複雑にしたり、読み取り/保守不能にしたりしないでください。実際のパフォーマンスは向上しません。
Rob Whelan

21

私の経験では:
経験豊富なDBAと通信していません。


17

「実際の」データベースの代わりにAccessを使用する。SQL ExpressMySQLSQLiteのような、小さくて無料のデータベースがたくさんあります。多くの場合、アプリは予期しない方法でスケーリングする必要があります。


16

テーブル間の関係を設定するのを忘れています。私が現在の雇用主で働き始めたとき、私はこれを片付けなければならなかったことを覚えています。


14

Excelを使用して(大量の)データを保存する。

私は数千行を保持し、複数のワークシートを使用している企業を見てきました(以前のバージョンのExcelでは行数が65535に制限されていたため)。


Excelはレポート、データ表示、その他のタスクに適していますが、データベースとして扱うべきではありません。


14

追加したいのは、パフォーマンスの高いコードよりも「エレガントな」コードを優先することです。データベースに対して最適に機能するコードは、多くの場合、アプリケーション開発者にとって醜いものです。

時期尚早の最適化についてのナンセンスを信じています。データベースは、元の設計とその後の開発におけるパフォーマンスを考慮する必要があります。私の意見では、パフォーマンスはデータベース設計の50%(40%はデータの整合性、最後の10%はセキュリティ)です。実際にユーザーと実際のトラフィックがデータベースに対して配置されると、実行するためにボトムアップで構築されていないデータベースのパフォーマンスが低下します。時期尚早の最適化は、最適化がないという意味ではありません!それはあなたがそれが簡単であると思うのでほとんどいつも悪いパフォーマンスをするコードを書くべきだという意味ではありません(例えば、他のすべてが失敗しない限り、本番データベースでは許可されるべきでないカーソル)。つまり、必要になるまで、最後の少しのパフォーマンスを絞り出すことを検討する必要はありません。データベースでのパフォーマンスが向上することについては多くのことが知られていますが、


2
+1-データベースプログラミングには、機械コンポーネントの動作の最適化が含まれます。ただし、Knuthは時期尚早な最適化がすべての悪の原因の約97%(またはその効果を表す言葉)の根源であると述べていることに注意してください。データベース設計は、これについて前もって考えなければならない分野の1つです。
ConcernedOfTunbridgeWells

2
Ahem ...あなたが話しているのは時期尚早ではない最適化です。データベースの設計(および実際にはアプリケーションの設計)の最初から、実際の使用方法をある程度考慮する必要があります。何が時期尚早で何がそうでないかを決定する必要があるため、Knuthの規則は実際には従うのは簡単ではありません。つまり、「データなしで最適化を実行しない」ということになります。あなたが話している初期のパフォーマンス関連の決定にデータがあります-特定の設計は将来のパフォーマンスに許容できない制限を設定し、それらを計算することができます。
Rob Whelan、

13

パラメータ化されたクエリを使用しません。SQLインジェクションの停止に非常に便利です。

これは、別の回答で言及されている、入力データをサニタイズしない具体的な例です。


3
入力のサニタイズが間違っていることを除いて。消毒とは、危険な場所に置くことを意味します。パラメータ化とは、それを完全に危害の経路から遠ざけることを意味します。
ダスティン

12

開発者がネストされた選択ステートメントを使用したり、クエリの「SELECT」部分内の選択ステートメントの結果を返す関数を使用したりするのは嫌です。

@adamにも同様の問題が指摘されていますが、ここでは他の場所でこれを見たことがないことに本当に驚いています。おそらく見落としました。

例:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

このシナリオでは、MyTableが10000行を返す場合、最初のクエリとその他の各テーブルのクエリを結果の行ごとに1回ずつ実行する必要があるため、クエリは20001クエリを実行したかのようになります。

開発者は、数行のデータのみを返し、サブテーブルには通常少量のデータしかない開発環境でこれを回避できますが、本番環境では、この種のクエリは指数関数的にコストがかかる可能性があります。データがテーブルに追加されます。

より良い(必ずしも完璧ではない)例は次のようなものです:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

これにより、データベースオプティマイザーは、メインテーブルの各レコードを再クエリするのではなく、データを一緒にシャッフルできます。通常、この問題が発生したコードを修正する必要がある場合、通常、クエリの速度が100%増加します。 CPUとメモリの使用量を同時に削減しながらより多く。


12

SQLベースのデータベースの場合:

  1. CLUSTERED INDEXESを利用していないか、CLUSTERに間違った列を選択しています。
  2. 親/子テーブルリレーションシップの外部キー(INT)に結合するための主キーとして、シリアル(自動番号)データ型を使用していません。
  3. 多くのレコードがINSERTEDまたはDELETEDされた場合、テーブルの統計を更新しません。
  4. 多くの行が挿入または削除された場合、テーブルを再編成(つまり、アンロード、ドロップ、再作成、ロード、および再インデックス)しません(一部のエンジンは、削除された行を削除フラグでテーブルに物理的に保持します)。
  5. トランザクション率が高い大きなテーブルでFRAGMENT ON EXPRESSION(サポートされている場合)を利用しない。
  6. 列に間違ったデータ型を選択しています!
  7. 適切な列名を選択しない。
  8. テーブルの最後に新しい列を追加しない。
  9. 頻繁に使用されるクエリをサポートするための適切なインデックスを作成しない。
  10. 可能な値がほとんどない列にインデックスを作成し、不要なインデックスを作成する。
    ...さらに追加されます。

1
問題点:2)は実際には悪い習慣です。私はあなたが何をしているのかわかります-その自動番号に一意のインデックスが必要で、それを代理キーとして使用します。しかし、主キーは自動番号であってはなりません。これは主キーとは異なります。主キーは「レコードの内容」であり、これは(販売トランザクションなどを除いて)自動番号ではなく、いくつかの一意のビットですモデル化されているエンティティに関する情報の。
David T. Macknet

主キーと外部キーに自動番号を使用する主な理由は、他の列の変更に関係なく、親子結合を維持できることを保証するためです。顧客名やその他のデータなど、別の主キーを使用するとリスクが高まります。
フランクR.

@David:私は修正しました!..自動番号を主キーとして使用する必要はありません。親のインデックス付きシリアル列を使用して、子のサロゲートを結合し、関係が切断されないことを保証します。行を見つけるための意味のあるプライマリとしての列!
フランクR.

結局のところ、これはセマンティクスの問題です。Microsoftは、主キーが意味を持つのではなく、意味を持たないことを望んでいます。それについての議論は激化していますが、私は「意味のある」キャンプに陥ります。:)
デビッドT.マックネット

9
  • 本番データベース内の問題を修正する前にバックアップを取らない。

  • ストアドプロシージャのストアドオブジェクト(テーブル、ビューなど)に対するDDLコマンドの使用。

  • ストアドプロシージャを使用することへの恐怖、またはORMクエリを使用するのがより効率的で適切な場所での使用に対する恐怖。

  • ORMクエリが最終的に何に変換されているかを正確に伝えることができるデータベースプロファイラーの使用を無視し、ロジックを検証するか、ORMを使用していない場合のデバッグも可能にします。


8

正しいレベルの正規化を行っていません。データが重複していないこと、および必要に応じてデータを別のデータに分割していることを確認する必要があります。また、パフォーマンスに悪影響を与えるので、正規化にあまり従わないようにする必要があります。


遠すぎますか?データが重複していない場合、どうすればそれをさらに進めることができますか?
finnw 2009年

正規化は、冗長データの削除と柔軟性の向上、パフォーマンスの低下と複雑さの増加のバランスです。正しいバランスを見つけるには経験が必要であり、それは時間とともに変化します。いつ非正規化するかについては、en.wikipedia.org
/ wiki / Database_normalization

8

データベースを単なるストレージメカニズム(つまり、栄光のあるコレクションライブラリ)として扱い、そのアプリケーションに従属する(データを共有する他のアプリケーションは無視する)


これの当然の結果は、それが属しているデータベースに保持するのではなく、アプリケーションに過剰なクエリ作業をオフロードすることです。LINQは特にこの点で問題があります。
3Dave

8
  • 「上に存在しない「それはあまりにも魔法だ」などの理由で、手からHibernateのようなORMを閉じると、私のデータベース」。
  • HibernateのようなORMに過度に依存し、適切ではない場所でそれをシューホーンしようとする。

8

1- where句の値に関数を不必要に使用し、そのインデックスの結果が使用されていない

例:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

の代わりに

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

そして、より少ない程度に:それらを必要とするそれらの値に機能インデックスを追加しない...

2-データの有効性を保証するためにチェック制約を追加しません。制約はクエリオプティマイザーで使用できます。制約は、不変条件を信頼できることを確認するのに役立ちます。それらを使用しない理由はありません。

3-純粋な怠惰または時間のプレッシャーからテーブルに正規化されていない列を追加します。物事は通常、このように設計されていませんが、これに発展します。最終結果は、間違いなく、将来の進化で失われたデータの整合性に悩まされたときに、混乱を解消しようとする膨大な作業です。

このことを考えてみてください。データのないテーブルは、再設計するのが非常に安価です。整合性のない数百万のレコードを持つテーブル...再設計するのにそれほど安くはありません。したがって、列またはテーブルを作成するときに正しい設計を行うと、スペードで償却されます。

4-データベース自体についてはそれほどではありませんが、実際に迷惑です。SQLのコード品質を気にしません。SQLがテキストで表現されているからといって、文字列操作アルゴリズムのヒープにロジックを隠しても問題ありません。同僚のプログラマーが実際に読み取り可能な方法でSQLをテキストで書くことは完全に可能です。


7

これは以前に言われましたが、 インデックス、インデックス、インデックスです。パフォーマンスの低いエンタープライズWebアプリの多くのケースを見て、小さなプロファイリング(ヒットしたテーブルを確認する)を行い、それらのテーブルにインデックスを追加するだけで修正されました。これはSQLを書くための知識をあまり必要とせず、その見返りは莫大です。

ペストのようなデータの重複を避けてください。一部の人々は、少しの複製が害を及ぼすことはなく、パフォーマンスを向上させると主張しています。ちょっと、スキーマが非常に抽象的で、DBAでさえ何が起こっているのかわからなくなるまで、スキーマを第3正規形に拷問する必要があるとは言っていません。一連の名前、郵便番号、または配送コードを複製すると、コピーは最終的に相互に同期しなくなることを理解してください。それは起こります。そして、毎週のメンテナンススクリプトを実行するときに、自分をキックするでしょう。

そして最後に:明確で一貫した直感的な命名規則を使用します。適切に記述されたコードが読み取り可能であるのと同じように、優れたSQLスキーマまたはクエリは読み取り可能であり、コメントがなくても実際に何が行われているかを伝える必要があります。テーブルのメンテナンスが必要な6か月後に感謝します。 "SELECT account_number, billing_date FROM national_accounts"「SELECT ACCNTNBR、BILLDAT FROM NTNLACCTS」よりもはるかに簡単に操作できます。


あなたがそれらを正しく設定すればそれらはそうしませんが、これは多くの人々がアレルギーを起こすトリガーの使用を含みます。
HLGEM 2009年

6

DELETEクエリを実行する前に、対応するSELECTクエリを実行しない(特に本番データベース)。


5

私が20年間で見た最も一般的な間違い:事前に計画を立てていない。多くの開発者は、データベースとテーブルを作成し、アプリケーションを構築しながら、テーブルを継続的に変更および拡張します。最終結果は、多くの場合、混乱して非効率になり、後でクリーンアップまたは単純化することが困難になります。


1
このような状況で起こる恐怖を想像できます...スキーマレスデータベースは、迅速なプロトタイピングと反復開発にはるかに適していますが、他のすべてと同様に、このような柔軟性にはさまざまなトレードオフが伴います。
ZsoltTörök11年

4

a)クエリ値を文字列にハードコーディングする
b)Windowsフォームアプリケーションの "OnButtonPress"アクションにデータベースクエリコードを配置する

両方見ました。


4
「Windowsフォームアプリケーションの「OnButtonPress」アクションにDBクエリコードを配置する」ここでデータベースの間違いは何ですか?
再帰

@recursive:これはSQLインジェクションの巨大な脆弱性です。誰でも任意のSQLをサーバーに送信でき、そのまま実行されます。
ビルカーウィン

@recursiveに同意します。これらは実際にはDBの問題とは関係ありません。
p.campbell 2009

b)はアーキテクチャの誤りです。もちろん、アプリでクエリを直接コーディングするのは悪い考えです。
3Dave、

4

アプリケーションでのデータベース接続の管理に十分な注意を払っていません。次に、アプリケーション、コンピューター、サーバー、およびネットワークが詰まっていることがわかります。


4
  1. 彼らがDBAであり、データモデラー/デザイナーであると考えているのは、それらの分野でいかなる種類の正式な知識もない場合です。

  2. 彼らのプロジェクトはすべてDBAを必要としないと考えています。

  3. データベースで実行する必要がある作業と、アプリで実行する必要がある作業を適切に区別できない。

  4. バックアップを検証しない、またはバックアップしない。

  5. 生のSQLをコードに埋め込む。



3

データベースの同時実行モデルと、これが開発にどのように影響するかを理解していない。事後のインデックスの追加やクエリの調整は簡単です。ただし、ホットスポット、リソースの競合、および正しい操作を考慮せずに設計されたアプリケーション(今読んだものがまだ有効であると想定)は、後で修正するためにデータベースおよびアプリケーション層内で大幅な変更が必要になる場合があります。


3

DBMSが内部でどのように機能するかを理解していません。

クラッチのしくみを理解しないと、スティックを正しく動かすことはできません。そして、実際にハードディスク上のファイルに書き込んでいるだけであることを理解せずに、データベースの使い方を理解することはできません。

具体的には:

  1. クラスタ化インデックスとは何か知っていますか?スキーマを設計するときにそれについて考えましたか?

  2. インデックスを適切に使用する方法を知っていますか?インデックスを再利用するには?カバリングインデックスとは何かご存知ですか。

  3. すばらしいですね。インデックスがあります。インデックスの1行はどのくらいの大きさですか?大量のデータがある場合、インデックスはどのくらい大きくなりますか?それは簡単にメモリに収まりますか?そうでない場合は、インデックスとしては役に立ちません。

  4. MySQLでEXPLAINを使用したことがありますか?すごい。自分に正直に言ってください。見たものの半分でも理解できましたか?いいえ、おそらくしませんでした。修正してください。

  5. クエリキャッシュを理解していますか?クエリをキャッシュできない理由を知っていますか?

  6. MyISAMを使用していますか?全文検索が必要なら、とにかくMyISAMはくだらない。Sphinxを使用します。次に、Innoに切り替えます。


2
より良い例えは、クラッチを理解せずにマニュアルトランスミッションを適切にトラブルシューティングできないことです。多くの人が、クラッチがどのように機能するかを知らずに、スティックシフトを適切に運転しています。
マイケルイースター

3
  1. ORMを使用して一括更新を行う
  2. 必要以上のデータを選択しています。繰り返しますが、通常はORMを使用するときに行われます
  3. ループ内でSQLを実行する。
  4. 良好なテストデータがなく、ライブデータでのみパフォーマンスの低下に気づいている。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.