SELECT *が有害と見なされるのはなぜですか?


256

SELECT *悪い習慣はなぜですか?必要な新しい列を追加した場合、変更するコードが少なくなるという意味ではないでしょうか。

SELECT COUNT(*)一部のDBではパフォーマンスの問題だと理解していますが、すべての列が本当に必要な場合はどうでしょうか。


30
SELECT COUNT(*)悪いことは信じられないほど古くて時代遅れです。詳細については、次をSELECT *参照してください。stackoverflow.com
OMGポニー

8
SELECT COUNT(*)SELECT COUNT(SomeColumn)列がNOT NULL列でない場合とは異なる回答を示します。そしてオプティマイザはSELECT COUNT(*)特別な扱いをすることができます-そして通常そうします。また、WHERE EXISTS(SELECT * FROM SomeTable WHERE ...)特別なケースの扱いが与えられることに注意してください。
ジョナサンレフラー

3
@Michael Mrozek、実際にはそれは質問の逆です。私はそれがこれまでに有害であったかどうかを尋ねています。
セオドアR.スミス

1
@Bytecode Ninja:具体的には、MyISAMエンジンを搭載したMySQLはCOUNT(*)に対して最適化されています。mysqlperformanceblog.com
2007

回答:


312

主に3つの主な理由があります。

  • データを消費者に移動する際の非効率性。 SELECT *を実行すると、アプリケーションが実際に機能するために必要な数よりも多くの列がデータベースから取得されることがよくあります。これにより、より多くのデータがデータベースサーバーからクライアントに移動し、アクセスが遅くなり、マシンの負荷が増加するだけでなく、ネットワーク上を移動するのに時間がかかります。これは、元のコンシューマーがデータアクセスをコーディングしたときに存在せず、不要であった基になるテーブルに誰かが新しい列を追加する場合に特に当てはまります。

  • インデックス作成の問題。 クエリを高レベルのパフォーマンスに調整するシナリオを考えます。*を使用すると、実際に必要な数よりも多くの列が返された場合、サーバーはデータを取得するために、通常よりも高価なメソッドを実行する必要があります。たとえば、SELECTリストの列を単純にカバーするインデックスを作成することはできません。(すべての列[ shudder ] を含めて)作成したとしても、周りに来て下に列を追加した次の人はtableを使用すると、オプティマイザは最適化されたカバリングインデックスを無視します。また、明らかな理由がなくても、クエリのパフォーマンスが大幅に低下する可能性があります。

  • バインドの問題。SELECT *を実行すると、2つの異なるテーブルから同じ名前の2つの列を取得することができます。これにより、データコンシューマがクラッシュすることがよくあります。「ID」という列を含む2つのテーブルを結合するクエリを想像してください。消費者はどちらがどれであるかをどのようにして知るでしょうか?SELECT *は、基になるテーブル構造が変更されると、ビュー(少なくとも一部のバージョンではSQL Server)を混乱させる可能性があります。ビューは再構築されず、返されるデータは意味がありません。そして、それの最悪の部分は、あなたが好きなようにあなたの列に名前を付けるように気を付けることができるということですが、次に来る人は、あなたがすでに開発した列と衝突する列を追加することについて心配する必要があることを知る方法がないかもしれません名前。

しかし、SELECT *のすべてが悪いわけではありません。私はこれらのユースケースのためにそれを寛大に使用します:

  • アドホッククエリ。 何かをデバッグしようとするとき、特に私がよく知らない狭いテーブルを離れて、SELECT *はしばしば私の親友です。これは、基になる列名が何であるかについて大量の調査を行わなくても、何が起こっているのかを確認するのに役立ちます。これは、列名が長くなるほど「プラス」が大きくなります。

  • *が「行」を意味する場合。 次の使用例では、SELECT *は問題なく、パフォーマンスキラーであるという噂は、何年も前にある程度の有効性があった可能性がある都市の伝説にすぎませんが、今はそうではありません。

    SELECT COUNT(*) FROM table;

    この場合、*は「行を数える」ことを意味します。*の代わりに列名を使用すると、その列の値がnullではない行がカウントされます。私にとってCOUNT(*)は、行をカウントしているという概念を理解し、集計からNULLが削除されることによって引き起こされる奇妙なエッジケースを回避します。

    同じことがこのタイプのクエリにも当てはまります。

    SELECT a.ID FROM TableA a
    WHERE EXISTS (
        SELECT *
        FROM TableB b
        WHERE b.ID = a.B_ID);

    そのソルトに値するデータベースでは、*は単に「行」を意味します。サブクエリに何を入れてもかまいません。SELECTリストでbのIDを使用する人もいれば、数字の1を使用する人もいますが、IMOのこれらの規則はほとんど無意味です。つまり、「行を数える」ということです。それが*の意味です。そこにあるほとんどのクエリオプティマイザーは、これを知るのに十分なほどスマートです。(正直なところ、SQL ServerとOracleでのみこれが当てはまることを知っています。)


17
"SELECT id、name"を使用すると、 "SELECT *"と同じように、結合を使用するときに2つの異なるテーブルから同じ名前の2つの列を選択できます。テーブル名を前に付けると、どちらの場合も問題が解決します。
のMichałTatarynowicz

1
私はこれが古いことを知っていますが、グーグルでプルアップされたものなので、質問します。「*が「行」を意味する場合。次の使用例では、SELECT *は問題なく、パフォーマンスキラーであるという噂は都市の伝説にすぎません...」ここに参照がありますか?このステートメントは、ハードウェアがより強力であることによるものですか(それが事実である場合でも、気づかない可能性が低いだけで非効率的ではないという意味ではありません)。それ自体を推測するつもりはありませんが、このステートメントがどこから来るのか疑問に思っています。
Jared

6
参照に関する限り、クエリプランを調べることができます。サブクエリに "*"がある場合と列を選択する場合は同じです。コストベースのオプティマイザが意味的に「認識」するので、これらは同じです。つまり、基準を満たすすべての行について話しているのです。これは、ハードウェアや速度の問題ではありません。
Dave Markle、

4
使用するもう1つの利点*は、状況によってはMySQLのキャッシュシステムをよりよく活用できることです。あなたは、同様の多数の実行している場合select(別のカラム名を要求するクエリselect A where Xselect B where X使用して、...)select * where Xキャッシュが実質的なパフォーマンスの向上につながることができますクエリの大きな数を処理できるようになりますが。これはアプリケーション固有のシナリオですが、覚えておく価値があります。
ベンD

2
8年以上後、言及されていないあいまいさについてポイントを追加したい。データベースで200以上のテーブルを操作し、命名規則が混在している。クエリ結果とやり取りするコードを確認する場合、SELECT *開発者は関連するテーブルスキーマを調べて、foreachや内など、影響を受ける/使用可能な列を特定しますserialize。何が起こっているのかを追跡するためにスキーマを繰り返し参照するタスクは、関連するコードのデバッグと開発の両方に関わる合計時間を必然的に増加させます。
fyrye

91

SELECTステートメントのアスタリスク文字「*」は、クエリに関連するテーブルのすべての列の省略形です。

パフォーマンス

以下の*理由により、略記は遅くなる可能性があります。

  • すべてのフィールドにインデックスが作成されるわけではないため、全テーブルスキャンが強制されます-効率が低下します
  • ネットワーク経由で送信するために保存するとSELECT *、テーブル全体がスキャンされる危険性があります
  • 必要以上のデータを返す
  • 可変長データ型を使用して末尾の列を返すと、検索オーバーヘッドが発生する可能性があります

メンテナンス

使用する場合SELECT *

  • コードベースに不慣れな人は、ドキュメントを調べて、適切な変更を行う前に、どの列が返されているかを知る必要があります。コードを読みやすくし、コードに不慣れな人に必要な曖昧さや作業を最小限に抑えることで、長期的に見ればより多くの時間と労力を節約できます。
  • コードが列の順序に依存しているSELECT *場合、テーブルの列の順序が変更された場合に発生するのを待っているエラーを非表示にします。
  • クエリが書き込まれるときにすべての列が必要な場合でも、将来はそうならない可能性があります
  • 使用法がプロファイリングを複雑にする

設計

SELECT *あるアンチパターン

  • クエリの目的はそれほど明確ではありません。アプリケーションが使用する列は不透明です
  • 可能な場合は常に厳密な型指定の使用に関するモジュール性の規則に違反します。明示的にはほぼ普遍的に優れています。

「SELECT *」はいつ使用する必要がありますか?

SELECT *クエリが作成されたときに存在していたすべての列とは対照的に、関係するテーブルのすべての列が明示的に必要な場合に使用できます。データベースは内部的に*を列の完全なリストに展開します-パフォーマンスの違いはありません。

それ以外の場合は、クエリで使用するすべての列を明示的にリストしてください。できればテーブルエイリアスを使用してください。


20

ここですべての列を選択したい場合でも、誰かが1つ以上の新しい列を追加した後ですべての列を選択したくない場合があります。一緒にクエリを作成する場合SELECT *、ある時点で誰かがテキストの列を追加して、実際にはその列が必要ない場合でもクエリの実行が遅くなるリスクを負っています。

必要な新しい列を追加した場合、変更するコードが少なくなるという意味ではないでしょうか。

おそらく、新しい列を実際に使用したい場合は、とにかくコードに他の多くの変更を加える必要があります。保存するのはわずか, new_column数文字です。


21
特に、その新しい列が3メガバイトのBLOBである場合
Matti Virkkunen

2
@Matti-しかし、うまくいけば、彼らは「ちょっとこの巨大なBLOB列をこのテーブルにプロップできるようにする」よりも多くのことを考えてくれるでしょう(はい、愚か者は私が知っていることを望みますが、男は夢を見ることはできませんか?)
ChaosPandion

5
パフォーマンスは1つの側面ですが、多くの場合、正確さの側面もあります。投影された結果の形状*が予期せず変更され、アプリケーション自体に大混乱をもたらす可能性があります。序数で参照される列(例:sqldatareader.getstring(2))が突然取得する別の列には、いずれかがINSERT ... SELECT *壊れるなどなどでしょう。
Remus Rusanu

2
@カオス:テーブルにBLOBを配置しても、パフォーマンスに大きな影響はありません... SELECT * ...を使用しない限り、;-)
Dave Markle

2
実際の問題が発生するまで、パフォーマンスについて心配する必要はありません。また、SELECT *いくつかの文字を保存することも問題ではありません。新しく追加された列を指定することを忘れがちなので、デバッグ時間を節約することは問題です。
ルイス

4

SELECTステートメントで列に名前を付けると、指定した順序で返されるため、数値インデックスで安全に参照できます。"SELECT *"を使用すると、列を任意の順序で受け取ることになり、名前でのみ安全に列を使用できます。データベースに追加される新しい列で何をしたいのかを事前に知らない限り、最も可能性のある正しいアクションはそれを無視することです。データベースに追加される新しい列を無視する場合は、それらを取得しても何のメリットもありません。


「これ安全に数値インデックスによって参照することができる」誰がために愚か十分だろう、これまで試してみて、代わりにそれの名前の数値インデックスで列を参照!これは、ビューでselect *を使用するよりもはるかに悪いアンチパターンです。
MGOwen 2016

@MGOwen:使用select *した後、インデックスによってカラムを使用することは恐ろしいことになるが、使用してselect X, Y, Z、またはselect A,B,C、その後、列0のデータに何かを期待するコードに得られたデータリーダを通過し、1、及び2に完全に合理的な方法と思われます同じコードがX、Y、ZまたはA、B、Cのいずれかに作用することを許可します。列のインデックスは、データベース内の順序ではなく、SELECTステートメント内の位置に依存することに注意してください。
スーパーキャット'19

3

多くの場合、SELECT *を使用すると、設計時ではなく、アプリケーションの実行時にエラーが発生します。これにより、列の変更に関する知識、またはアプリケーションの不適切な参照が隠されます。


1
では、列に名前を付けるとどのように役立ちますか?SQL Serverでは、コードまたはSPに埋め込まれている既存のクエリは、列に名前を付けていても、実行されるまで文句を言いません。新しいものをテストすると失敗しますが、多くの場合、テーブルの変更の影響を受けるSPを探す必要があります。設計時に捕捉されるであろう、どのような状況を参照していますか?
ChrisA

3

すべての列が本当に必要な場合は、選択(*)と列の名前付けの間にパフォーマンスの違いは見られません。列に名前を付けるためのドライバーは、コードに表示されると予想される列を明示するだけの場合があります。

しかし、多くの場合、すべての列が必要なわけではなく、select(*)を使用すると、データベースサーバーに不必要な作業が発生したり、不必要な情報がネットワーク経由で渡されたりする可能性があります。システムの使用率が高いか、ネットワーク接続が遅い場合を除き、顕著な問題が発生することはほとんどありません。


3

アプリとデータベースの間の結合を減らすことと考えてください。

「コードのにおい」の側面を要約する
SELECT *と、アプリとスキーマの間に動的な依存関係が作成されます。使用を制限することは、依存関係をより明確にする1つの方法です。それ以外の場合、データベースを変更すると、アプリケーションがクラッシュする可能性が高くなります。


3

テーブルにフィールドを追加すると、それらは使用するすべてのクエリに自動的に含まれます select *。これは便利に思えるかもしれませんが、必要以上のデータをフェッチしているため、アプリケーションが遅くなり、実際にアプリケーションがクラッシュする場合があります。

結果の各行でフェッチできるデータの量には制限があります。結果がその制限を超えてしまうようにテーブルにフィールドを追加すると、クエリを実行しようとするとエラーメッセージが表示されます。

これは、見つけるのが難しい種類のエラーです。ある場所で変更を加えると、実際には新しいデータをまったく使用しない別の場所で爆破します。クエリの使用頻度は低く、誰かが使用するまでに時間がかかるため、エラーを変更に関連付けるのがさらに困難になります。

結果に必要なフィールドを指定すると、この種のオーバーヘッドオーバーフローから安全になります。



2

この記事から引用した参考資料。

「SELECT *」を使用しないでください。

「SELECT *」を使用する理由を1つだけ見つけました

列を追加または削除するときに特別な要件があり、動的環境を作成した場合、アプリケーションコードによって列が自動的に処理されます。この特別なケースでは、アプリケーションとデータベースのコードを変更する必要はありません。これは、本番環境に自動的に影響します。この場合、「SELECT *」を使用できます。


1

一般的に、あなたはあなたの結果に合わせる必要があります SELECT * ...をさまざまなタイプのデータ構造。結果が到着する順序を指定しないと、すべてを適切に整列させるのが難しくなる可能性があります(また、あいまいなフィールドは見落としやすくなります)。

このように、アプリケーション全体でSQLアクセスコードを壊すことなく、さまざまな理由でテーブルにフィールドを(テーブルの途中であっても)追加できます。


1

SELECT *数列しか必要ないときに使用すると、必要以上に多くのデータが転送されます。これにより、データベースでの処理が追加され、クライアントにデータを取得する際のレイテンシが増加します。これに加えて、ロード時により多くのメモリを使用します。大きなBLOBファイルなど、場合によっては大幅に多くのメモリを使用します。これは主に効率に関するものです。

ただし、これに加えて、ロードされている列をクエリで確認すると、テーブルの内容を調べる必要がないため、より簡単に確認できます。

はい、列を追加する方が高速ですが、ほとんどの場合、クエリを使用してコードを変更し、とにかく新しい列を受け入れるようにする必要があります。そうしないと、列が取得されない可能性があります。 t want / expectは問題を引き起こす可能性があります。たとえば、すべての列を取得し、ループ内の順序に依存して変数を割り当ててから、変数を追加した場合、または列の順序が変更された場合(バックアップからの復元時に発生することがわかります)、すべてを破棄できます。

これはINSERT、列を指定する必要がある理由も同じです。


1

私はこれに全面的なルールがあるはずがないと思います。多くの場合、私はSELECT *を避けましたが、SELECT *が非常に有益なデータフレームワークも使用しました。

すべてのものと同様に、メリットとコストがあります。メリットとコストの方程式の一部は、データ構造をどれだけ制御できるかだと思います。SELECT *が適切に機能した場合、データ構造は厳密に制御され(小売ソフトウェアでした)、そのため、誰かが巨大なBLOBフィールドをテーブルに潜入させるリスクはほとんどありませんでした。


1

列名で選択すると、データベースエンジンがテーブルデータをクエリするのではなく、インデックスからデータにアクセスできる可能性が高くなります。

SELECT *を使用すると、コードがその新しいデータを使用または提示する準備ができていなくても、テーブルに新しい列が追加されるため、データベーススキーマが変更された場合に、システムが予期しないパフォーマンスと機能の変更にさらされます。


1

より実用的な理由もあります:お金。クラウドデータベースを使用していて、処理したデータに料金を支払う必要がある場合、すぐに破棄するデータを読み取るための説明はありません。

例:BigQuery

クエリの料金

クエリの料金とは、SQLコマンドとユーザー定義関数を実行するコストを指します。BigQueryは、1つの指標、つまり処理されたバイト数を使用してクエリの料金を請求します。

制御射影-SELECT *を避けます

ベストプラクティス:プロジェクションを制御する-必要な列のみをクエリします。

射影は、クエリによって読み取られる列の数を指します。過剰な列を投影すると、追加の(無駄な)I / Oと実体化(結果の書き込み)が発生します。

SELECT *の使用は、データを照会する最も高価な方法です。SELECT *を使用すると、BigQueryはテーブル内のすべての列のフルスキャンを実行します。


0

スキーマを設計する前に(可能であれば)要件を理解します。

データについて学びます。1)インデックス作成2)使用されるストレージのタイプ、3)ベンダーエンジンまたは機能。すなわち...キャッシング、インメモリ機能4)データタイプ5)テーブルのサイズ6)クエリの頻度7)リソースが共有されている場合の関連するワークロード8)テスト

A)要件は異なります。ハードウェアが予想されるワークロードをサポートできない場合は、ワークロードの要件を提供する方法を再評価する必要があります。表への追加欄について。データベースがビューをサポートしている場合は、特定の名前付きの列を持つ特定のデータのインデックス付き(?)ビューを作成できます(選択 '*'に対して)。定期的にデータとスキーマを見直して、「ガベージイン」->「ガベージアウト」シンドロームに遭遇しないようにしてください。

他の解決策がないと仮定します。以下を考慮に入れることができます。問題には常に複数の解決策があります。

1)インデックス作成:select *はテーブルスキャンを実行します。さまざまな要因に応じて、ディスクシークや他のクエリとの競合が発生する場合があります。テーブルが多目的の場合は、すべてのクエリがパフォーマンスを発揮し、目標時間を下回って実行されることを確認します。大量のデータがあり、ネットワークまたはその他のリソースが調整されていない場合。これを考慮に入れる必要があります。データベースは共有環境です。

2)ストレージのタイプ。つまり、SSD、ディスク、またはメモリを使用している場合。I / O時間とシステム/ CPUの負荷は異なります。

3)DBAは、より高いパフォーマンスを得るためにデータベース/テーブルを調整できますか?何らかの理由で想定すると、チームは問題の最良の解決策として「*」を選択することを決定しました。DBまたはテーブルをメモリにロードできますか。(または他の方法...おそらく応答は2〜3秒の遅延で応答するように設計されましたか?---会社の収入を得るために広告が再生されている間...)

4)ベースラインから開始します。データ型と、結果がどのように表示されるかを理解します。データ型が小さく、フィールド数が多いと、結果セットで返されるデータの量が少なくなります。これにより、リソースを他のシステムニーズに利用できるようになります。通常、システムリソースには制限があります。「常に」これらの制限を下回って、安定性と予測可能な動作を保証します。

5)テーブル/データのサイズ。select '*'は小さなテーブルでは一般的です。通常、これらはメモリに収まり、応答時間は高速です。繰り返しますが、要件を確認します。機能のクリープを計画します。常に、現在および可能な将来のニーズについて計画します。

6)クエリの頻度。システム上の他のワークロードに注意してください。このクエリが毎秒実行され、テーブルが小さい場合。結果セットは、キャッシュ/メモリにとどまるように設計できます。ただし、クエリがギガバイト/テラバイトのデータを使用する頻繁なバッチプロセスである場合は、他のワークロードが影響を受けないように、追加のリソースを専用にする方がよい場合があります。

7)関連するワークロード。リソースの使用方法を理解します。ネットワーク/システム/データベース/テーブル/アプリケーションは専用ですか、それとも共有ですか?関係者は誰ですか?これは本番、開発、QAのどれですか?これは一時的な「迅速な修正」ですか。シナリオをテストしましたか?現在のハードウェアにどれほど多くの問題が存在するかに驚かれるでしょう。(はい、パフォーマンスは高速です...しかし、設計/パフォーマンスはまだ低下しています。)システムは、1秒あたり5-10クエリではなく、1秒あたり10Kクエリのパフォーマンスを必要としますか。データベースサーバーは専用ですか、または他のアプリケーションを実行して、共有リソースで監視を実行しますか。一部のアプリケーション/言語。O / Sはメモリを100%消費し、さまざまな症状/問題を引き起こします。

8)テスト:理論をテストし、できる限り理解します。選択した「*」の問題は大きな問題である場合もあれば、心配する必要がないものである場合もあります。

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