select *を使用しない理由は何ですか?


136

選択クエリで必要な各列に具体的に名前を付ける必要があると多くの人が主張するのを見てきました。

とにかくすべての列を使用すると仮定すると、なぜ使用しないのSELECT *ですか?

* SQLクエリ-ビューから*を選択するか、ビューからcol1、col2、…colNを選択*する問題を考慮しても、少し異なる視点から問題に取り組んでいるため、これは正確な重複ではないと思います。

私たちの原則の1つは、時間になる前に最適化しないことです。このことを念頭に置くと、リソースの問題であることが証明されるか、スキーマがほぼ確定するまではSELECT *使用することをお勧めします。これは、ご存じのとおり、開発が完全に完了するまで発生しません。

とはいえ、使用しないことの重要な問題はありますSELECT *か?

回答:


168

時期尚早の最適化ではないの引用の本質はシンプルでわかりやすいコードのために行くとすることで、次にあなたが効率的に最適化することができますホットスポットを、指摘してプロファイラを使用しています。

select *を使用すると、プロファイリングが不可能になるため、明確で簡単なコードを記述しておらず、引用の精神に反することになります。select *アンチパターンです。


したがって、列の選択は時期尚早の最適化ではありません。頭のてっぺんからいくつか...

  1. SQLステートメントで列を指定した場合、その列がテーブルから削除されてクエリが実行されると、SQL実行エンジンでエラーが発生します。
  2. その列が使用されているコードをより簡単にスキャンできます。
  3. 情報を最小限に戻すために、常にクエリを記述する必要があります。
  4. 他の人が序列アクセスを使用する場合は言及するように、select *を使用しないでください
  5. SQLステートメントがテーブルを結合する場合、select *は結合内のすべてのテーブルのすべての列を提供します

その結果はselect *...

  1. アプリケーションで使用される列は不透明です
  2. DBAとそのクエリプロファイラは、アプリケーションのパフォーマンスの低下を助けることができません
  3. 変更が発生すると、コードはよりもろくなります
  4. データベースとネットワークは、大量のデータ(I / O)を取り戻しているため、影響を受けています
  5. データベース・エンジンの最適化は、すべてのデータを(論理的に)関係なく戻すので、最小限です。

正しいSQLの記述は、記述と同じくらい簡単Select *です。したがって、本当の怠惰な人は適切なSQLを作成します。なぜなら、彼らはコードを再訪したり、それを行ったときに何をしていたのかを思い出したくないからです。彼らは、コードのすべてのビットについてDBAに説明したくありません。アプリケーションが犬のように動作する理由をクライアントに説明したくありません。


2
最初のセクションで、ポイント5は「select *は結合内のすべてのテーブルのすべてのを提供します」と読む必要があります。2番目のセクションでは、ポイント#2と#5は必ずしも正しいとは限らず、「select *」を使用しない理由としてリストされるべきではありません。
jimmyorr、2009

1
@uglysmurf-訂正に感謝しますが、2と5に関しては、すべてのデータベース/ dbaに必ずしも当てはまるとは限りませんが、それらは重要であり、ほとんどの場合に有効であり、そのままにします。 'select *'を使用しても、dbaの作業が容易になることはありません。
ロバートポールソン、

11
#3(脆弱なコード)は真実ではないと主張します。実装によっては、Select *を使用すると脆弱性が低下する可能性がありますが、それがどのように脆弱になるかはわかりません。
JohnFx 2009

2
@JohnFx、私はあなたが異なって脆性を定義すると思います。もろさは通常「壊れやすい」と定義されています。コードの各部分が異なる列を使用するため、不明な依存関係や見つけにくい依存関係があると、完全な回帰なしではデータレベルで何も簡単に変更できないことを意味します。
ロバートポールソン、

9
@ mavnn、wrt brittleness、これは私が選択したbrittleのセマンティクスの問題に発展することを恐れています。私の最後の言葉は、とにかくほとんど違いがないと言うことです。唯一のシナリオは、列の名前変更/削除です。SQLが実行されたとき(明示的)からブレークが移動するだけで、結果が消費されたときにブレークするだけです。クエリ結果が消費される方法はさまざまであり、コードは黙って失敗する場合と失敗しない場合がありますが、SQL実行エンジンは無効なSQLで確実に失敗します。select *はあなたを助けましたか?DBの問題の場合、DBに近いIMOの明示的な障害の方が優れています。Thx
ロバートポールソン、

42

コードが特定の順序の列に依存している場合、テーブルが変更されるとコードが壊れます。また、*を選択すると、特にテーブルにバイナリフィールドがある場合、テーブルからフェッチしすぎる可能性があります。

ここですべての列を使用しているからといって、他の誰かがテーブルに列を追加しないわけではありません。

また、*に含まれる列を知るためにテーブルに関するメタデータをフェッチする必要があるため、プラン実行キャッシュにオーバーヘッドが追加されます。


4
良い答えですが、「コードが壊れる」を「コードが壊れる可能性があります」に変更します。これが本当の問題です。「select *」を使用しても、常に重大な変更が発生するわけではありません。そして、ブレークが発生した場合、通常、ブレークしてしまう使用とは大きく切り離されます。
BQ。

4
コードで列を通常参照している場合、SELECT *を使用するかどうかに関係なく問題が発生します。プラン実行のオーバーヘッドは取るに足らないものであり、プランがキャッシュされれば、とにかく問題にはなりません。
MusiGenesis 2008年

1
次に、プログラマーのエラーは、列のシーケンスに依存するコードの記述にあります。あなたはそれをする必要はありません。
dkretz 2008年

1
@doofledorfer-決して言うことはありません。序数列にアクセスする方が速く、時々実用的です。通常のアクセスを使用するよりも、select *を使用する方が大きなエラーです。
ロバートポールソン

23

主な理由の1つは、テーブルに列を追加または削除すると、SELECT *呼び出しを行っているすべてのクエリ/プロシージャが、予想よりも多いまたは少ないデータ列を取得するようになることです。


3
とにかく返される列の数に依存するコードを書くべきではありません。
dkretz 2008年

4
しかし、誰もがプログラマーがどのデータが戻ってくるかを知る必要があるコードを書いています。SELECT *で非表示になっている列名をCtrl + Fすることはできません。
Lotus Notes、

17
  1. 回り道では、可能な限り厳密な型指定を使用するというモジュール性の規則に違反します。Explicitはほぼ例外なく優れています。

  2. テーブルのすべての列が必要になった場合でも、後でさらに追加することができ、クエリを実行するたびにプルダウンされ、パフォーマンスに悪影響を与える可能性があります。パフォーマンスが低下します

    • ネットワーク経由でより多くのデータを取得しています。そして
    • テーブル自体でルックアップを実行するのではなく、インデックスから直接データを引き出すオプティマイザの機能(すべてがインデックスの一部である列に対するクエリの場合)を無効にする可能性があるためです。

select *を使用する場合

テーブルのすべての列を明示的に必要とするとき、クエリを書き込んだときに存在していたテーブルのすべての列が必要になるのとは対照的です。たとえば、テーブルの内容全体を表示する必要があるDB管理アプリを作成している場合(それらが何であれ)、そのアプローチを使用できます。


1
使用SELECT *するもう1つの時間は、dbクライアントを使用してテストクエリを実行しているときです。
cdmckay 2009

質問のコンテキストを考えると、これは奇妙な例外のようです。いくつかの入力を保存する以外に、テストクエリでこれを行う利点は何ですか?
JohnFx 2009

また、SELECT * FROM(SELECT a、b、c FROMテーブル)も問題ありません。
kmkaplan

12

いくつかの理由があります。

  1. データベースの列の数が変化し、アプリケーションが特定の数があることを予期している場合...
  2. データベース内の列の順序が変更され、アプリケーションがそれらを特定の順序であると予期している場合...
  3. メモリのオーバーヘッド。8つの不要なINTEGER列は、32バイトの無駄なメモリを追加します。それは多くのように聞こえませんが、これは各クエリに対するものであり、INTEGERは小さな列タイプの1つです...余分な列はVARCHARまたはTEXT列である可能性が高く、合計が速くなります。
  4. ネットワークのオーバーヘッド。メモリオーバーヘッドに関連して:30,000クエリを発行し、8つの不要なINTEGER列がある場合、960kBの帯域幅を無駄にしました。VARCHAR列とTEXT列はかなり大きくなる可能性があります。

注:上記の例では、サイズが4バイトに固定されているため、INTEGERを選択しました。


1と2はコードのにおいで、3と4は時期尚早の最適化のようです
NikkyD

7

アプリケーションがSELECT *を使用してデータを取得し、データベースのテーブル構造が変更された場合(たとえば、列が削除された場合)、アプリケーションは、欠落しているフィールドを参照するすべての場所で失敗します。代わりに、クエリにすべての列を含めると、アプリケーションは(うまくいけば)最初にデータを取得する1つの場所で中断し、修正が容易になります。

そうは言っても、SELECT *が望ましい状況はいくつかあります。1つは常に遭遇する状況で、テーブル全体を別のデータベース(SQL ServerからDB2など)に複製する必要があります。もう1つは、テーブルを一般的に(つまり、特定のテーブルに関する知識なしで)表示するように作成されたアプリケーションです。


質問は「選択*常に望ましい」ではないため、回答の2番目の部分は無関係です。質問は、「select *」を使用することをお勧めしますが、これはもちろん完全なブロックです。
ロバートポールソン、

はい、私の第2部は無関係です。OQは質問をSELECT *が望ましいという状態に変更しました。
MusiGenesis 2008年

ええごめんなさい-質問はあなたの答えの後に方向を変えました。
ロバートポールソン、

それは大丈夫だ。モーツァルトでさえ編集者でした(stackoverflow.com/questions/292682/…)。私の元の投稿では、SELECT *の使用が共食いにつながることを示唆していました。:)
MusiGenesis 2008年

3

select *SQL Server 2005のビューで使用すると、奇妙な動作に実際に気付きました。

次のクエリを実行すると、意味がわかります。

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','c1'
union all select 'a2','b2','c2'
union all select 'a3','b3','c3'

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vStartest]'))
DROP VIEW [dbo].[vStartest]
go
create view dbo.vStartest as
select * from dbo.starTest
go

go
IF  EXISTS (SELECT * FROM sys.views WHERE object_id = OBJECT_ID(N'[dbo].[vExplicittest]'))
DROP VIEW [dbo].[vExplicittest]
go
create view dbo.[vExplicittest] as
select a,b,c from dbo.starTest
go


select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicitTest

IF  EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[starTest]') AND type in (N'U'))
DROP TABLE [dbo].[starTest]
CREATE TABLE [dbo].[starTest](
    [id] [int] IDENTITY(1,1) NOT NULL,
    [A] [varchar](50) NULL,
    [B] [varchar](50) NULL,
    [D] [varchar](50) NULL,
    [C] [varchar](50) NULL
) ON [PRIMARY]

GO

insert into dbo.starTest
select 'a1','b1','d1','c1'
union all select 'a2','b2','d2','c2'
union all select 'a3','b3','d3','c3'

select a,b,c from dbo.vStartest
select a,b,c from dbo.vExplicittest

最後の2つのselectステートメントの結果を比較します。あなたが見るものは、名前ではなくインデックスで列を参照するSelect *の結果だと思います。

ビューを再構築すると、再び正常に機能します。

編集

別の質問を追加しました。SQLServer 2005の興味深い動作「*テーブルからの選択」と「テーブルからのcolA、colBなどの選択」の興味深い動作 *を追加して、その動作を詳細に調べます。


2

2つのテーブルを結合して、2番目のテーブルの列Aを使用できます。後で列Aを最初のテーブルに追加すると(名前は同じであるが意味が異なる可能性があります)、以前のように2番目のテーブルではなく、最初のテーブルから値を取得する可能性があります。選択したい列を明示的に指定した場合、これは起こりません。

もちろん、列を指定すると、すべての選択句に新しい列を追加し忘れた場合にバグが発生することがあります。クエリを実行するたびに新しい列が必要ない場合は、バグに気付くまでに時間がかかることがあります。


2

時期尚早の最適化に関してあなたがどこへ向かっているのか理解しましたが、それは本当にほんの少しのことです。その意図は、最初に不必要な最適化を回避することです。テーブルにインデックスが付けられていませんか?nvarchar(4000)を使用して郵便番号を保存しますか?

他の人が指摘したように、クエリで使用する予定の各列を指定することには他にも良い点があります(保守性など)。


2

列を指定するときは、特定の列のセットに束縛され、柔軟性が低下し、フォイヤースタインがどこにいてもロールオーバーするようになります。ちょっとした考え。


1
フォイヤーシュタインが誰であるか私には全く分かりません。グーグルを試して、心理学者、テレビのキャラクター、ブロガーを見つけたので、思いついたのは冗談でした。
NotMe

PL / SQLに関するO'Reillyの本の著者。「feuerstein」だけでなく「feuerstein sql」をグーグルで試してみてください。
orbfish

2

SELECT *は常に悪であるとは限りません。私の意見では、少なくとも。テーブル全体といくつかの計算フィールドを返す動的クエリによく使用します。

たとえば、「通常の」テーブル、つまりジオメトリフィールドがなく、フィールドに座標が含まれているテーブルから地理的ジオメトリを計算したいとします。私はpostgresqlとその空間拡張postgisを使用しています。しかし、原則は他の多くの場合に適用されます。

例:

  • 場所のテーブル。座標はx、y、zのラベルが付いたフィールドに保存されています。

    CREATE TABLEプレース(place_id整数、x数値(10、3)、y数値(10、3)、z数値(10、3)、説明varchar);

  • いくつかの例の値でそれを供給しましょう:

    INSERT INTO Places(place_id、x、y、z、description)VALUES
    (1、2.295、48.863、64、 'Paris、Place de l \'Étoile ')、
    (2、2.945、48.858、40、' Paris、Tour Eiffel ')、
    (3、0.373、43.958、90、'コンドーム、カテドラルサンピエール ');

  • いくつかのGISクライアントを使用して、このテーブルのコンテンツをマップできるようにしたいと考えています。通常の方法は、ジオメトリフィールドをテーブルに追加し、座標に基づいてジオメトリを構築することです。しかし、私は動的クエリを取得したいと思います。このようにして、座標を変更すると(修正、より正確など)、マップされたオブジェクトは実際に動的に移動します。したがって、ここにSELECT *を使用したクエリがあります。

    ビューを作成または置換するPlaces_points AS
    SELECT *、
    GeomFromewkt( 'SRID = 4326; POINT(' || x || '' || y || '' || z || ')')
    FROM Places ;

    GeomFromewkt()関数の使用については、postgisを参照してください。

  • 結果は次のとおりです。

    SELECT * FROM Places_points;

place_id | x | y | z | 説明| geomfromewkt                            
---------- + ------- + -------- + -------- + ------------- ----------------- + -------------------------------- ------------------------------------  
        1 | 2.295 | 48.863 | 64.000 | パリ、エトワール広場| 01010000A0E61000005C8FC2F5285C02405839B4C8766E48400000000000005040  
        2 | 2.945 | 48.858 | 40.000 | パリ、エッフェル塔| 01010000A0E61000008FC2F5285C8F0740E7FBA9F1D26D48400000000000004440
        3 | 0.373 | 43.958 | 90.000 | コンドーム、サンピエール大聖堂| 01010000A0E6100000AC1C5A643BDFD73FB4C876BE9FFA45400000000000805640
(3ライン)

右端の列をGISプログラムで使用して、ポイントを適切にマッピングできるようになりました。

  • 将来的に、いくつかのフィールドがテーブルに追加された場合、心配する必要はありません。同じVIEW定義をもう一度実行する必要があります。

VIEWの定義を*を使用して「そのまま」保持できることを願っていますが、そうではありません。これは、postgresqlによって内部的に格納される方法です。

SELECT Places.place_id、places.x、places.y、places.z、places.description、geomfromewkt((((((( 'SRID = 4326; POINT(' :: text || Places.x)|| '': :text)|| Places.y)|| '' :: text)|| Places.z)|| ')' :: text)AS geomfromewkt FROM Places;


1

すべての列を使用するが、行インデックスを数値インデックスでアドレス指定する場合でも、後で別の行を追加すると問題が発生します。

つまり、基本的には保守性の問題です。*セレクタを使用しない場合は、クエリについて心配する必要はありません。


1

必要な列のみを選択すると、メモリ内のデータセットが小さく保たれるため、アプリケーションの速度が速くなります。

また、多くのツール(たとえば、ストアドプロシージャ)はクエリ実行プランもキャッシュします。後で列を追加または削除した場合(特にビューを選択していない場合は簡単です)、期待どおりの結果が得られない場合にツールがエラーになることがよくあります。


1

コードが曖昧になり、保守が難しくなります。未使用のデータをドメインに追加していて、意図したものとそうでないものは明確でないためです。(また、あなたが知らない、または気にしないかもしれないことを示唆しています。)


1

質問に直接回答するには:基になるテーブルへの変更に対してコードがより脆弱になる場合は、 "SELECT *"を使用しないでください。プログラムの要件に直接影響する変更がテーブルに加えられた場合にのみ、コードは壊れます。

アプリケーションでは、リレーショナルアクセスが提供する抽象化レイヤーを利用する必要があります。


1

SELECT *は使用していません。これは、取得するフィールドを確認して理解できるからです。


1

テーブルの列が変更された場合にビューを再コンパイルする必要があるため、ビュー内で「select *」を使用することは一般的に好ましくありません。ビューの基になるテーブルの列を変更すると、戻って再コンパイルするまで、存在しない列に対してエラーが発生します。


1

それexists(select * ...)は決して拡張されないので、あなたがやっているときにそれは問題ありません。それ以外の場合は、一時的な選択ステートメントを含むテーブルを探索する場合、または上記でCTEを定義していて、すべてを再入力せずにすべての列が必要な場合にのみ、本当に役立ちます。


1

他の誰も言及していないことを1つ追加するだけです。Select *すべての列を返します。誰かが後でデータを最後に更新した人やタイムスタンプ、またはマネージャのみがすべてのユーザーを見る必要はないというメモなど、必ずしもユーザーに表示したくない列を追加する可能性があります。

さらに、列を追加するときは、既存のコードへの影響を確認し、列に格納されている情報に基づいて変更が必要かどうかを検討する必要があります。を使用select *することにより、開発者は何も壊れないと想定するため、そのレビューはしばしばスキップされます。そして実際には、明示的に破損しているように見えるものはないかもしれませんが、クエリは間違ったものを返し始める可能性があります。明示的に壊れるものがないからといって、クエリに変更があったはずがないという意味ではありません。


0

すべてのフィールドが必要ない場合、「select *」はメモリを浪費するためです。ただし、SQLサーバーの場合、それらのパフォーマンスは同じです。

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