select *は、SQL Server 2012ではまだ大きな問題ではありませんか?


41

過去の時代に戻って、それはやるべきことselect * from tableselect count(*) from tableパフォーマンスの打撃のために大きなノーと考えられていました。

SQL Serverの以降のバージョンでもこれは当てはまりますか(2012年を使用していますが、質問は2008〜2014年に適用されると思います)。

編集:ここで人々は私をわずかに軽視しているように見えるので、私はこれをベンチマーク/学問的観点から見ており、それが「正しい」ことであるかどうか(もちろんそうではありません)

回答:


50

SELECT COUNT(*) FROM TABLE1行(カウント)のみを返す場合は、比較的軽く、そのデータを取得する方法です。

そしてSELECT *、それは合法であり、許可されているという点で、物理的なノーノーではありません。

ただし、問題SELECT *は、より多くのデータ移動を引き起こす可能性があることです。テーブル内のすべての列を操作します。SELECT数列しか含まれていない場合は、1つまたは複数のインデックスから回答を取得できる場合があります。これにより、I / Oが削減され、サーバーキャッシュへの影響も軽減されます。

だから、はい、それはあなたのリソースの無駄であるので、それが一般的な慣行として反対を推奨します。

唯一の本当の利点は、SELECT *すべての列名を入力しないことです。ただし、SSMSからドラッグアンドドロップを使用して、クエリの列名を取得し、不要な列名を削除できます。

アナロジー:誰かが使用する場合はSELECT *、彼らはすべての列を必要としないとき、彼らは考え使用SELECTせずにWHERE、彼らはすべての行を必要としないとき(または他のいくつかの制限条項)?


24

既に答えられているプロバイダーに加えて、Entity Frameworkなどの最新のORMで作業する場合、開発者は面倒すぎることが多いことを指摘する価値があると感じています。DBAは回避するためSELECT *に最も努力しますが、開発者はc#Linqのような意味的に同等なものを書くことがよくあります。

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User").ToList();

本質的に、これは次の結果になります。

SELECT * FROM MyTable WHERE FirstName = 'User'

まだカバーされていない追加のオーバーヘッドもあります。それは、関連するオブジェクトに対して各行の各列を処理するために必要なリソースです。さらに、メモリに保持されているすべてのオブジェクトについて、そのオブジェクトをクリーンアップする必要があります。必要な列のみを選択した場合、100MBを超えるRAMを簡単に節約できます。それ自体は大規模な量ではありませんが、ガベージコレクションなどの累積的な効果がクライアント側のコストです。

そうです、少なくとも私にとっては、そうであり、これからもずっと大きなことです。また、これを行うための「隠された」コストについても教育する必要があります。

補遺

コメントで要求されたとおりに必要なデータのみをプルするサンプルを次に示します。

var someVariable = db.MyTable.Where(entity => entity.FirstName == "User")
                             .Select(entity => new { entity.FirstName, entity.LastNight });

13

パフォーマンス:SELECT *を使用したクエリは、おそらくカバークエリにはなりません(簡単な説明スタックオーバーフローの説明)。

将来への対応:クエリは現在7列すべてを返す可能性がありますが、来年に誰かが5列を追加すると、1年でクエリは12列を返し、IOとCPUを浪費します。

インデックス作成:ビューとテーブル値関数をSQL Serverのインデックス作成に参加させる場合は、それらのビューと関数をスキーマバインドで作成する必要があります。これにより、SELECT *の使用が禁止されます。

ベストプラクティス:運用SELECT *コードでは使用しないでください。

サブクエリの場合、が好きWHERE EXISTS ( SELECT 1 FROM … )です。

編集:以下のCraig Youngのコメントに対処するために、サブクエリで「SELECT 1」を使用することは「最適化」ではありません。クラスの前に立って、「SELECT *を使用しないでください」と言うことができます。 」

私が考えることができる唯一の例外については、クライアントが何らかのピボットテーブル操作を行っており、現在および将来のすべての列を必要とする場所です。

実行計画を確認したいのですが、CTEと派生テーブルに関する例外を受け入れる場合があります。

COUNT(*)これは、「*」の異なる構文上の使用法であるため、例外を考慮することに注意してください。


10

SQL Server 2012(または2005年以降のすべてのバージョン)ではSELECT *...、クエリの最上位のSELECTステートメントでパフォーマンスの問題が発生する可能性があります。

ビュー(*)で、サブクエリで、EXIST句では、CTEの中で、またして問題ではないが、それはそうSELECT COUNT(*)..、これはOracleのためにも、おそらく真実であることに注意してください、とDB2、およびなどなど多分 POSTGRES(ないように注意してください) 、しかし、MySqlの多くの場合、依然として問題である可能性が非常に高いです。

なぜ(そして、なぜそれがトップレベルSELECTで問題になる可能性がまだある)を理解するには、それが今まで使用しているためであるという問題であった理由を理解することが役に立つSELECT *..手段は、「すべての列を返します」。一般に、これは本当に必要なデータよりもはるかに多くのデータを返します。これにより、ディスクとネットワークの両方で、明らかに多くのIOが発生する可能性があります。

あまり明らかではないのは、これによりSQLオプティマイザーが使用できるインデックスとクエリプランが制限されることです。これは、最終的にすべてのデータ列を返す必要があることがわかっているためです。特定の列のみが必要であることが事前にわかっている場合、多くの場合、それらの列のみを持つインデックスを利用することで、より効率的なクエリプランを使用できます。幸いなことに、これを事前に知る方法があります。これは、列リストで必要な列を明示的に指定するためのものです。ただし、「*」を使用する場合は、「すべてを提供してください。必要なものがわかります」ということを支持して、これを忘れています。

はい、すべての列の処理に追加のCPUとメモリの使用もありますが、これらの2つのことと比較すると、ほとんど常にマイナーです:不要な列に必要なディスクとネットワーク帯域幅の大幅な増加と、すべての列を含める必要があるため、最適化されたクエリプラン。

それで何が変わったのですか?基本的に、SQLオプティマイザーは、クエリの上位レベルで実際に列を使用する場合に下位レベルのサブクエリで把握できることを意味する「列最適化」と呼ばれる機能をうまく組み込みました。

これの結果は、クエリの下位/内部レベルで「SELECT * ..」を使用しても問題にならないということです。代わりに、実際に重要なのは、最上位のSELECTの列リストにあるものです。SELECT *..最上部で使用しない限り、もう一度、すべての列が必要であると仮定する必要があるため、列の最適化を効果的に使用できません。

(*-ビューに*は、「*」を使用したときに列リストの変更が常に登録されない、別の小さなバインディング問題があることに注意してください。これに対処する他の方法があり、パフォーマンスに影響しません。)


5

使用しないもう1つの小さな理由がありますSELECT *。返される列の順序が変更された場合、アプリケーションが壊れます...運がよければ。そうでない場合、微妙なバグがあり、長い間検出されない可能性があります。テーブル内のフィールドの順序は実装の詳細であり、アプリケーションで考慮されるべきではありませんSELECT *


4
これは無関係です。アプリケーションコードの列インデックスで列にアクセスしている場合、アプリケーションが壊れているに値します。名前で列にアクセスすると、常にはるかに読みやすいアプリケーションコードが生成され、パフォーマンスのボトルネックになることはほとんどありません。
ライライアン14

3

物理的にも問題なく使用することもできますselect * from tableが、それは悪い考えです。どうして?

まず、不要な列(リソースが大量)を返していることがわかります。

第二に、*を選択すると、実際にデータベースから列名を選択し、「この他のリストに名前がある列に関連付けられているデータを教えてください」 」プログラマーにとってこれは簡単ですが、銀行のコンピューターでこれを実行すると、文字通り1分間に数十万の検索が行われる可能性があります。

第三に、実際にこれを行うと、開発者にとって難しくなります。すべての列名を取得するために、SSMSからVSにどのくらいの頻度で切り替える必要がありますか?

第4に、それは怠programmingなプログラミングの兆候であり、開発者がその評判を望んでいるとは思わない。


この現在の形式の2番目の引数には、いくつかの小さな間違いがあります。まず、すべてのRDBMSはテーブルのスキームをキャッシュします。これは主に、クエリ解析ステージでスキームが読み込まれ、クエリのテーブルに存在する列または存在しない列を判断するためです。そのため、クエリパーサーは既に列名リストを独自にクエリし、*を列のリストで即座に置き換えます。その後、ほとんどのRDBMSエンジンは、可能な限りすべてをキャッシュしようとするため、SELECT * FROMテーブルを発行すると、コンパイルされたクエリがキャッシュされるため、毎回解析は行われません。開発者は怠け者です:-)
Gabor Garami 14

あなたの第二引数に関しては、これは一般的な誤解です-あなたは、列に名前を付ける場合は、SQL Serverは、まだ自分の名前を検証するために持っているので、SELECTでの問題は、*など、チェックデータ型メタデータ検索ではありません
アーロン・ベルトラン

@Gabor SELECT *の問題の1つは、ビューに配置するときに発生します。基礎となるスキーマを変更すると、ビューが混乱する可能性があります。これは、テーブル自体とは異なるテーブルスキーマの概念(独自のスキーマ)を持つようになります。ここでこれについて話します
アーロンバートランド

3

Select * ...以前に指摘したように、データベースは時間の経過とともに変化し、クエリを作成したときに予想したよりも多くの列を持っている可能性があるため、プログラムにコードを配置すると問題になる可能性があります。これは、プログラムの失敗につながる可能性があります(ベストケース)。または、処理するために書き込まれなかったフィールド値を参照しているため、プログラムが陽気な方法でデータを破損する可能性があります。要するに、量産コードは常にで返されるフィールドを指定する必要がありますSELECT

とはいえ、プログラムに返されるのは選択の成功または失敗を示すブール値だけなのでSelect *EXISTS句の一部である場合は問題が少なくなります。他の人はこの立場に反対するかもしれません、そして私はそれについて彼らの意見を尊重します。句にSelect *「Select 1」をコーディングするよりも、コーディングする方がわずかに効率が悪い場合がEXISTSありますが、どちらにしてもデータ破損の危険性はないと思います。


実際、はい、EXISTS句を参照するつもりでした。私の間違い。
マーク・ロス

2

なぜselect *間違っているのかという答えがたくさんあるので、私はそれが正しいと感じたとき、または少なくとも大丈夫だと思います。

1)EXISTSでは、クエリのSELECT部分​​の内容は無視されるため、書き込みもできSELECT 1/0、エラーは発生しません。EXISTS一部のデータが返されることを確認し、それに基づいてブール値を返します。

IF EXISTS(
    SELECT * FROM Table WHERE X=@Y
)

2)これはファイヤーストームを開始する可能性がありselect *ますが、履歴テーブルトリガーで使用するのが好きです。によってselect *、メインテーブルに挿入/更新/削除されるとすぐにエラーになるため、履歴テーブルに列を追加せずにメインテーブルが新しい列を取得することを防ぎます。これにより、開発者が列を追加し、それを履歴テーブルに追加するのを忘れていた何度も防ぐことができました。


3
SELECT 1将来のコード管理者にあなたの意図を最も明確に通知するので、私はまだ好んでいます。それは要件ではありませんが、見れば、それが... WHERE EXISTS (SELECT 1 ...)真実テストであることを明らかに明らかにしています。
swasheck

1
@zlatan多くの人はSELECT 1、パフォーマンスがのより優れているという神話に基づいて使用していますSELECT *。ただし、両方のオプションは完全に受け入れられます。オプティマイザーがEXISTSを処理する方法によるパフォーマンスの違いはありません。真実性テストを明確に告知する「EXISTS」という言葉による読みやすさの違いもありません。
幻滅

ポイント#2で、私はあなたの推論を理解していますが、まだリスクがあります。「シナリオをペイントしてください」...開発者がColumn8メインテーブルに履歴テーブルを忘れて追加します。開発者は、列8に割り当てられた一連のコードを記述します。次にColumn9、メインテーブルに追加します。今回は、履歴にも追加することを忘れないでください。後でテストするとき、彼はColumn9履歴に追加するのを忘れたことに気づき(エラー検出手法のおかげで)、すぐに追加します。これでトリガーは機能しているように見えますが、8列目と9列目のデータが履歴に混同されています。:S
幻滅

続き...ポイントは、上記の「調合された」シナリオは、エラー検出トリックが失敗し、実際に事態を悪化させる可能性のある多くのシナリオの1つにすぎないということです。基本的に、より良いテクニックが必要です。選択するテーブル内の列の順序を仮定するトリガーに依存しないもの。提案:-よくある間違いのチェックリストを含む個人コードのレビュー。-ピアコードレビュー。-履歴を追跡するための代替手法(個人的には、トリガーベースのメカニズムは予防的ではなく事後対応的であるため、エラーが発生しやすいと考えています)。
幻滅

@CraigYoungそれは可能性です。しかし、誰かがそれをやった場合、私は誰かを絞るでしょう。それはあなたが簡単に作ることができるの間違いではありません
UnhandledExcepSean
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.