select * vs select column


124

2/3列が必要なだけSELECT *で、それらの列を選択クエリで提供する代わりにクエリを実行する場合、I / Oまたはメモリの増減に関するパフォーマンスの低下はありますか?

必要なく*を選択すると、ネットワークオーバーヘッドが発生する可能性があります。

しかし、選択操作では、データベースエンジンは常にディスクからアトミックタプルをプルするのですか、それとも選択操作で要求された列のみをプルするのですか?

常にタプルをプルする場合、I / Oオーバーヘッドは同じです。

同時に、タプルをプルする場合、要求された列をタプルから取り除くためにメモリが消費される可能性があります。

その場合、select someColumnはselect *よりも多くのメモリオーバーヘッドを持ちます。


あなたが尋ねている特定のRDBMSはありますか?SELECTクエリの実行/処理方法はデータベースごとに異なる可能性があります。
Lèseはmajesté

10
余談ですが、PostgreSQLでと言った場合CREATE VIEW foo_view AS SELECT * FROM foo;、後で列をテーブルfooに追加すると、それらの列はfoo_viewに期待どおりに自動的に表示されません。つまり、*このコンテキストでは、SELECTごとではなく、(ビューの作成時に)1回だけ展開されます。ALTER TABLEから生じる複雑さのため、(実際には)*有害と見なされていると私は言うでしょう。
ジョーイアダムス

@JoeyAdams-PostgresQLだけでなく、これもOracleの動作です。
2010

1
@OMGポニー:私は同様の投稿を知りませんでした。ただし、これらは実際には似ていません。@Lèsemajesté:私はGeneric RDBMSについて話しています。特定のベンダーについてではなく@Joey Adams:うーん*は安全でないことは知っています。パフォーマンスに関する問題について話し合いたいだけです。
Neel Basu

回答:


31

常にタプルをプルします(テーブルが垂直にセグメント化されている場合を除き、列の断片に分割されます)。したがって、あなたが尋ねた質問に答えるには、パフォーマンスの観点からは問題ではありません。ただし、他の多くの理由から(以下)、必要な列は常に名前で具体的に選択する必要があります。

(私がよく知っているすべてのベンダーのRDBMSで)すべて(テーブルデータを含む)の基礎となるディスク上のストレージ構造は、定義されたI / Oページに基づいている ため(SQL Serverでは、たとえば、各ページは8キロバイト)。そして、すべてのI / O読み取りまたは書き込みはページごとです。つまり、すべての書き込みまたは読み取りは、データの完全なページです。

この根本的な構造上の制約のため、結果として、データベース内のデータの各行は常に1つのページにのみ存在する必要があります。データの複数のページにまたがることはできません(実際のblobデータが個別のページチャンクに格納され、実際のテーブル行の列がポインターのみを取得するblobなどの特別なものを除く)。ただし、これらの例外は単なる例外であり、特別な場合(特別なタイプのデータ、または特別な状況に対する特定の最適化)を除いて、通常は適用されません。
これらの特別な場合でも、通常、実際のデータのテーブル行自体( Blobなどの実際のデータへのポインター)、それは単一のIOページに保存する必要があります...

例外。Select *OKである唯一の場所は、次のように、Existsor Not Exists述語句の後のサブクエリです。

   Select colA, colB
   From table1 t1
   Where Exists (Select * From Table2
                 Where column = t1.colA)

編集:@マイクシェラーのコメントに対処するために、はい、それは技術的に、あなたの特別なケースのための少しの定義で、そして審美的に両方とも真実です。まず、要求された列のセットがインデックスに格納されている列のサブセットである場合でも、クエリプロセッサは、同じ理由で、要求された列だけでなく、そのインデックスに格納されているすべての列をフェッチする必要があります。すべてのI / Oは、ページとインデックスデータは、テーブルデータと同じようにIOページに保存されます。したがって、インデックスに格納された列のセットとしてインデックスページの「タプル」を定義した場合でも、ステートメントは真になります。
要点ではなく、I / Oページに格納されているものに基づいてデータをフェッチすることがポイントであり、ベーステーブルのI / Oページとインデックスのどちらにアクセスしているかに関係なく、ステートメントは審美的に真実ですI / Oページ。

を使用しないその他の理由についてはSelect *、「有害SELECT *見なされる理由」を参照してください


「それは常にタプルを引っ張る」のでよろしいですか?うーん大丈夫だから私は正しかった。その場合、同じI / Oオーバーヘッドselect *よりもメモリオーバーヘッドが少なくなりますselect column。したがって、ネットワークのオーバーヘッドを残す場合。select *それよりオーバーヘッドが少ない場合select column
Neel Basu

10
本当じゃない。私の頭の上の一例は、MySQLのインデックス付き列の値のみが必要な場合(たとえば、行の存在を確認するためだけ)であり、MyISAMストレージエンジンを使用している場合は、 MYIファイル。これはメモリにある可能性があり、ディスクに移動することすらできません。
Mike Sherov

要求されたタプルのセットがメモリにある場合、I / Oはありませんが、それは特別なケースです。だから夏は何ですか。インデックス付きの列を選択すると、タプル全体が読み込まれませんか?そうでなければタプル全体が読み込まれますか?
Neel Basu

MySqlがどのようにキャッシュを実行するかは正確にはわかりませんが、SQL ServerとOracleでは、データがメモリ内キャッシュにある場合でも、ディスクからアクセスする場合と同じページ構造を使用してアクセスします。つまり、データのページごとに1つのメモリI / Oが必要になります...ディスクからの場合とまったく同じです。(もちろん、メモリI / OはディスクI / Oよりもはるかに高速です)。確かに、それはアクセスプロセスをデータの場所に完全に依存しないようにするキャッシュ設計の目標です。
Charles Bretana、2010

2
「他の多くの理由」について詳しく説明していただけますか?それらが私にははっきりしなかったからです。パフォーマンスが重要でない場合、なぜ列名のリクエストを気にするのですか?
デニス

111

SELECT *本番用コードで使用してはならない(決してしない)理由はいくつかあります。

  • データベースに必要なヒントを与えていないため、最初にテーブルの定義を確認して、そのテーブルの列を特定する必要があります。そのルックアップにはある程度の時間がかかります-単一のクエリではそれほどではありません-しかし、時間の経過とともに増加します

  • 列の2/3だけが必要な場合は、ディスクから取得してネットワーク経由で送信する必要があるデータの1/3が多すぎます

  • 返される列の順序など、データの特定の側面に依存し始めた場合、テーブルが再編成されて新しい列が追加された(または既存の列が削除された)場合、厄介な驚きが生じる可能性があります。

  • SQL Server(他のデータベースについては不明)では、列のサブセットが必要な場合、非クラスター化インデックスがその要求をカバーしている可能性があります(必要なすべての列が含まれています)。を使用するとSELECT *、最初からその可能性をあきらめています。この特定のケースでは、データはインデックスページから取得され(必要な列がすべて含まれている場合)、クエリを実行する場合と比較して、ディスクI / O メモリのオーバーヘッドははるかに少なくなりSELECT *....ます。

はい、最初は少しタイプする必要があります(SQL ServerのSQLプロンプトのようなツールはそこでも役立ちます)-しかし、これは例外がないルールがある実際の1つのケースです:プロダクションコードでSELECT *を使用しないでください。これまで。


13
実際にあなたと同意している間は、テーブルから列データをフェッチするとき、この質問が対応しているため、すべてのケースで確かに正しいです。具体的には、(のようにWhere Exists (Select * From ...)EXISTS述語の後のサブクエリでの使用です。Select *確かにの使用は問題にならず、一部のサークルではベストプラクティスと見なされています。
Charles Bretana、2010

3
@Charles Bretana:はい、これIF EXISTS(SELECT *...は特別なケースです-データがないため、実際にはデータが取得されませんが、存在の確認にすぎません。SELECT*は問題ではありません...
marc_s

1
テーブルの1つからデータを取得できるAPIを開発している場合はどうでしょうか。ユーザーがどのデータに関心があるのか​​わからないので、SELECT *は受け入れられると思いますか?
Simon Bengtsson、2014

1
@SimonBengtsson:私はまだこれに反対します-顧客に公開したくない、テーブルの特定の列にいくつかの「管理」データがあるとしますか?私は常に明示的にフェッチする列のリストを指定します
marc_s

1
それは本当だ。APIで使用するように特別に設定されたビューをクエリする場合はどうですか?
Simon Bengtsson 2014

21

あなたは、必要があり、常に唯一selectの列あなたが実際に必要とすること。選択する量を増やすのではなく少なくすることは決して効率的ではありません。また、クライアント側の結果列にインデックスでアクセスし、新しい列をテーブルに追加することでこれらのインデックスが正しくなくなるなど、予期しない副作用も少なくなります。

[編集]:アクセスを意味します。愚かな脳がまだ目を覚ます。


3
クライアント側のインデックスと追加/変更された列-一見して多くの人が考えないであろうと思われるエッジケースの+1。
Tomas Aschan

1
そうですが、列に数値インデックスを使用することは一般的ですか?ORMを使用する場合は、常に文字列キーまたはプロパティ名を使用して列データにアクセスしました。
Lèseはmajesté

11
これをずっと前に見て、ジュニアプログラマーがテーブルから*を選択し、列の順序について仮定しました。他の誰かがテーブルを変更するとすぐに、彼のコードはすべて壊れました。どんなに楽しかった。
ポールマッケンジー2010

7
コードを読みやすくするためだけに列の順序を使用することは、おそらく悪い考えSELECT *です。
Lèseはmajesté

2
わあ、クライアントコードでインデックスを使用して列にアクセスするのは、驚くほど悪い考えのようです。さらに言えば、結果セット表示される列の順序に依存することは、私にとって非常に不愉快に感じます。
マットピーターソン

7

大きなblobを保存しているのでない限り、パフォーマンスは問題になりません。SELECT *を使用しない主な理由は、返された行をタプルとして使用している場合、スキーマが指定した順序で列が返されるためです。その場合、すべてのコードを修正する必要があります。

一方、辞書形式のアクセスを使用する場合、常に名前でアクセスするため、列がどの順序で返されるかは関係ありません。


6

これはすぐに、私が使用していたタイプの列を含むテーブルを思い起こさせますblob。通常は数Mb秒のJPEG画像が含まれていました。

言うまでもありませんがSELECT本当に必要な場合を除いて、このコラムを省略しました。特に複数の行を選択した場合は、そのデータをフロートさせるのは面倒でした。

ただし、通常はテーブルのすべての列を照会することを認めます。


20
LOB列は、常にSELECT *の危険のお気に入りの例です。それで、3番目の段落を読むまで、私はあなたを賛成しようとしていました。Tsk、tsk。他の開発者が、現在そのような列を持たないテーブルにBLOBを追加するとどうなりますか?
APC 2010

1
@APC、私はあなたのコメントをもっと賛成できればいいのに。パフォーマンスを大幅に低下させることなく列を追加したい貧しい同僚を考えてみてください。彼らが数時間後にあなたの無邪気に見える選択を発見したとき、彼らがどれほど怒るかを考えてください*。
Mike Sherov

1
@ user256007、はい、BLOBがなくても... BLOBは極端な例を示しています。Charlesへの私の応答を確認してください。特定の列を選択すると、ディスクにアクセスすることなくメモリからデータを取得できる場合があります。
Mike Sherov

1
@リチャード、私はDBパフォーマンスの最適化が主な関心事ではない場合、つまり99%の時間に最適です。ほとんどのフレームワークと同様に、純粋なパフォーマンスを犠牲にしながら開発を高速化できるように一般化する傾向があります。クヌースが言ったように:「時期尚早な最適化はすべての悪の根源です。」選択列と選択*のパフォーマンスを気にする必要があるところまで来たら(RoRについてTwitterに質問してください)、心配して最適化することができます。フレームワークがそれをサポートするのに十分堅牢でない場合は、間違ったフレームワークを使用していると私は言うでしょう。
Mike Sherov

1
@ user256007 -一般的なルールは、SELECT *」を使用していない」であるmarc_sからの答えは、このような場合は、なぜ、すべてのreasosnを持っている。。
APC

6

SQLの選択中、DBはSELECT a、b、cのSELECT *であるかどうかに関係なく、常にテーブルのメタデータを参照します。なぜですか?システム上のテーブルの構造とレイアウトに関する情報がそこにあるためです。

この情報を読まなければならない理由は2つあります。1つは、ステートメントをコンパイルするだけです。少なくとも既存のテーブルを指定することを確認する必要があります。また、最後に文が実行されてからデータベース構造が変更されている可能性があります。

さて、明らかに、DBメタデータはシステムにキャッシュされますが、実行する必要があるのはまだ処理中です。

次に、メタデータを使用してクエリプランが生成されます。これは、ステートメントがコンパイルされるたびにも発生します。繰り返しますが、これはキャッシュされたメタデータに対して実行されますが、常に実行されます。

この処理が行われないのは、DBが事前にコンパイルされたクエリを使用しているか、以前のクエリをキャッシュしている場合のみです。これは、リテラルSQLではなくバインディングパラメータを使用するための引数です。「SELECT * FROM TABLE WHERE key = 1」は「SELECT * FROM TABLE WHERE key =?」とは異なるクエリです。「1」はコールにバインドされています。

DBはそこでの作業をページキャッシングに大きく依存しています。最近のDBの多くは、メモリに完全に収まるほど小さい(または、おそらく、現代のメモリは、多くのDBに収まるほど大きい)です。次に、バックエンドでの主なI / Oコストは、ロギングとページフラッシュです。

ただし、DBのディスクを使用している場合、多くのシステムで行われる主な最適化は、テーブル自体ではなく、インデックス内のデータに依存することです。

あなたが持っている場合:

CREATE TABLE customer (
    id INTEGER NOT NULL PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    city VARCHAR(30),
    state VARCHAR(30),
    zip VARCHAR(10));

CREATE INDEX k1_customer ON customer(id, name);

次に、 "SELECT id、name FROM customer WHERE id = 1"を実行すると、DBがこのデータをテーブルからではなく、インデックスからプルする可能性が非常に高くなります。

どうして?とにかく、クエリを満たすためにインデックスを使用します(テーブルスキャンに対して)。また、where句で 'name'が使用されていなくても、そのインデックスはクエリにとって最良のオプションです。

これで、データベースにはクエリを満たすために必要なすべてのデータが揃ったので、テーブルページ自体をヒットする必要はありません。インデックスを使用すると、一般的にテーブルと比べてインデックスの行密度が高くなるため、ディスクトラフィックが減少します。

これは、一部のデータベースで使用されている特定の最適化手法についての波状の説明です。多くの場合、いくつかの最適化およびチューニング手法があります。

最後に、SELECT *は、手動で入力する必要がある動的クエリに役立ちます。「実際のコード」には使用しません。個々の列の識別により、クエリを最適化するために使用できるより多くの情報がDBに提供され、スキーマの変更などに対するコードの制御が向上します。


ええと、私があなたの答えを否定したのは、あなたが主キーと共にNOT NULLを使用したからです。あなたがこのように書く正当な理由はありますか?
学習者2015年

4

アプリを維持するためのパフォーマンスと機能は熟考されているため、質問に対する正確な回答はないと思います。Select columnはより高性能select *ですが、指向のオブジェクトシステムを開発している場合は、使用object.propertiesを好み、アプリの任意の部分でプロパティが必要になる可能性があります。そうでない場合は、特別な状況でプロパティを取得するためのメソッドをさらに記述する必要があります。select *すべてのプロパティを使用して入力します。アプリは、使用して良いパフォーマンスを持っている必要があり、select *場合によっては、select columnを使用してパフォーマンスを改善する必要があります。次に、パフォーマンスが必要なときにアプリとパフォーマンスを作成および維持するための2つの世界のどちらかを使います。


4

ここで受け入れられた答えは間違っています。これの複製として別の質問が閉じられたときにこれに遭遇しました(私がまだ私の答えを書いている間に-grr-したがって、以下のSQLは他の質問を参照しています)。

常にSELECT属性、attribute ...を使用する必要があります。

それは主にパフォーマンスの問題のためです。

ユーザーから名前を選択WHERE name = 'John';

あまり便利な例ではありません。代わりに検討してください:

SELECT telephone FROM users WHERE name='John';

(name、telephone)にインデックスがある場合、テーブルから関連する値を検索する必要なくクエリを解決できます- カバリングインデックスがあります。

さらに、テーブルにユーザーの写真を含むBLOB、アップロードされたCV、およびスプレッドシートがあるとします。SELECT*を使用すると、この情報がすべてDBMSバッファーに戻されます(キャッシュから他の有用な情報を強制的に取り出します)。次に、ネットワークの稼働時間と冗長なデータ用のクライアントのメモリを使用して、すべてクライアントに送信されます。

また、クライアントが列挙型配列(PHPのmysql_fetch_array($ x、MYSQL_NUM)など)としてデータを取得した場合、機能上の問題が発生する可能性もあります。多分、コードが「telephone」と書かれたとき、SELECT *によって返される3番目の列でしたが、誰かがやって来て、「telephone」の前に配置されたテーブルに電子メールアドレスを追加することにしました。目的のフィールドが4列目に移動します。


2

どちらの方法でも物事を行うには理由があります。私はPostgreSQLでSELECT *をよく使用します。これは、PostgreSQLでSELECT *を使用して実行できる多くのこと、特にストアドプロシージャでは明示的な列リストを使用して実行できないためです。同様に、Informixでは、継承されたテーブルツリーに対するSELECT *を使用すると、ぎざぎざの行が表示されますが、子テーブルの追加の列も返されるため、明示的な列リストは表示されません。

PostgreSQLでこれを行う主な理由は、テーブルに固有の整形式を確実に取得できるためです。これにより、結果を取得してPostgreSQLのテーブルタイプとして使用できます。これにより、固定列リストよりも多くのオプションをクエリで使用できます。

一方、厳密な列リストは、dbスキーマが特定の方法で変更されていないことをアプリケーションレベルで確認できるため、これが役立ちます。(私は別のレベルでそのようなチェックを行います。)

パフォーマンスについては、VIEWと、タイプを返すストアドプロシージャ(およびストアドプロシージャ内の列リスト)を使用する傾向があります。これにより、返されるタイプを制御できます。

ただし、通常はSELECT *をベーステーブルではなく抽象化レイヤーに対して使用していることに注意してください。


2

この記事から引用したリファレンス:

SELECT *なし: その時点で” SELECT *”を使用している場合、データベースからさらに列を選択しており、この列の一部はアプリケーションで使用されない可能性があります。これにより、追加のコストとデータベースシステムの負荷が発生し、より多くのデータがネットワーク上を移動します。

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


0

ここに表示されていないディスカッションにニュアンスを追加するだけです:I / Oに関して、列指向のストレージを備えたデータベースを使用している場合、特定のクエリのみを実行すれば、I / Oを大幅に削減できます。列。SSDに移行すると、行指向のストレージと比較して、メリットは少し小さくなる可能性がありますが、a)気になる列を含むブロックのみを読み取るb)圧縮により、一般にディスク上のデータのサイズが大幅に削減されるため、ディスクから読み取られたデータの量。

列指向のストレージに慣れていない場合、Postgresの実装の1つはCitus Dataによるもの、別の実装はGreenplum、別のParaccel、別の(大まかに言えば)Amazon Redshiftです。MySQLにはInfobrightがあり、これは今ではほとんど機能していないInfiniDBです。その他の商用製品には、HPのVertica、Sybase IQ、Teradata ...などがあります。


-1
select * from table1 INTERSECT  select * from table2

等しい

select distinct t1 from table1 where Exists (select t2 from table2 where table1.t1 = t2 )

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