web-dev-qa-db-ja.com

SQL Server 2012でselect *はまだ大きな禁止事項ですか?

かつての時代に戻ると、パフォーマンスが低下したため、_select * from table_またはselect count(*) from tableを実行することは非常に難しいと考えられていました。

これは、SQL Serverの以降のバージョンでも同じです(2012を使用していますが、問題は2008〜2014に当てはまると思います)?

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

41

SELECT COUNT(*) FROM TABLEを実行すると、1つの行(カウント)のみが返され、比較的軽く、そのデータを取得する方法になります。

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

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

したがって、はいリソースを浪費するため、一般的な方法としてはお勧めできません。

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

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

50
RLF

回答済みのプロバイダーに加えて、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 });
24
Stuart Blackler

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

将来性:クエリは今日7列すべてを返す可能性がありますが、翌年に誰かが5列を追加した場合、1年でクエリは12列を返し、IOとCPUを浪費します。

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

ベストプラクティス:プロダクションコードでは_SELECT *_を使用しないでください。

サブクエリの場合、WHERE EXISTS ( SELECT 1 FROM … )を使用します。

編集:以下のクレイグヤングのコメントに対処するために、サブクエリで「SELECT 1」を使用することは「最適化」ではありません。これは、クラスの前に立ち、 「SELECT *を使用しないでください。例外はありません!」

考えられる唯一の例外は、クライアントが何らかのピボットテーブル操作を実行していて、現在および将来のすべての列が必要な場合です。

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

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

13

SQL Server 2012(または2005以降の任意のバージョン)では、_SELECT *..._の使用は、クエリの最上位のSELECTステートメントで発生する可能性のあるパフォーマンスの問題にすぎません。

したがって、ビュー(*)、サブクエリ、EXIST句、CTE、SELECT COUNT(*)..などでは問題になりません。これは、おそらくOracleやDB2にも当てはまることに注意してください。および多分 PostGres(不明)、ただし、MySqlの多くのケースでまだ問題である可能性が非常に高いです。

理由(およびそれがトップレベルのSELECTで依然として問題である理由)を理解するには、これが問題である理由を理解することが役立ちます。これは、_SELECT *.._を使用すると、「すべてを返す)列の。一般に、これはlot本当に必要以上のデータを返します。これにより、ディスクとネットワークの両方でIOが大幅に増加する可能性があります。

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

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

何が変わったのですか?基本的に、SQLオプティマイザは「列の最適化」と呼ばれる機能を正常に組み込んでいます。つまり、クエリの上位レベルで実際に列を使用する場合、下位レベルのサブクエリでそれらを理解できるようになります。

この結果、クエリの下位レベルまたは内部レベルで 'SELECT * ..'を使用しても問題はなくなります。代わりに、本当に重要なのは、トップレベルのSELECTの列リストにあるものです。先頭で_SELECT *.._を使用しない限り、もう一度、[〜#〜] all [〜#〜]が必要であることを前提とする必要があるため、列の最適化を使用できません効果的に。

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

10
RBarryYoung

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

5

物理的および問題でselect * from tableの使用は許可されていますが、それはお勧めできません。どうして?

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

次に、大きなテーブルでは、列に名前を付けるよりも時間がかかります。*を選択すると、実際にはデータベースから列名が選択され、「この他のリストにある名前を持つ列に関連付けられているデータを教えてください」 」これはプログラマにとっては簡単ですが、1分間に文字通り何十万ものルックアップを行う銀行のコンピュータでこのルックアップを行うことを想像してみてください。

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

第4に、これは遅延プログラミングの兆候であり、開発者がその評判を望んでいるとは思いません。

3
CharlieHorse

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

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

3
Mark Ross

なぜselect *が間違っているので、それが正しい、または少なくともOKだと感じたときにカバーします。

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

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

2)これにより、嵐が始まるかもしれませんが、私はselect *履歴テーブルのトリガー。沿って select *、メインテーブルに挿入/更新/削除されるとすぐにエラーが発生するため、履歴テーブルに列を追加せずにメインテーブルが新しい列を取得するのを防ぎます。これにより、開発者が列を追加したり、履歴テーブルに追加するのを忘れたりすることが何度もありました。

2