昨日、私は「趣味」のプログラマーと話し合っていました(私自身はプロのプログラマーです)。私たちは彼の仕事のいくつかに出会い、彼は彼のデータベースのすべての列を(実動サーバー/コード上でも)常に照会すると言いました。
私は彼にそうしないように説得しようとしたが、まだそれほど成功していなかった。私の意見では、プログラマーは、「可愛さ」、効率、およびトラフィックのために実際に必要なものだけを照会する必要があります。私の見方に誤りがありますか?
昨日、私は「趣味」のプログラマーと話し合っていました(私自身はプロのプログラマーです)。私たちは彼の仕事のいくつかに出会い、彼は彼のデータベースのすべての列を(実動サーバー/コード上でも)常に照会すると言いました。
私は彼にそうしないように説得しようとしたが、まだそれほど成功していなかった。私の意見では、プログラマーは、「可愛さ」、効率、およびトラフィックのために実際に必要なものだけを照会する必要があります。私の見方に誤りがありますか?
回答:
何を取り戻し、コード内の変数にどのようにバインドするかを考えてください。
次に、誰かがテーブルスキーマを更新して列を追加(または削除)したときに、直接使用していない列でもどうなるかを考えてみましょう。
クエリを手で入力するときにselect *を使用するのは問題ありません。コードのクエリを作成するときではありません。
foo
し、クエリ内の別のテーブルが列を追加するfoo
場合、これを処理する方法は正しい foo
列を取得しようとするときに問題を引き起こす可能性があります。いずれにせよ、スキーマの変更はデータの抽出に問題を引き起こす可能性があります。
さらに、使用されていた列がテーブルから削除されているかどうかを検討してください。select * from ...
結果セットからデータをプルしようとしたとき、まだ出て動作しますが、エラー。クエリで列が指定されている場合、クエリはエラーになり、代わりに何がどこに問題があるかを明確に示します。
一部の列には、大量のデータが関連付けられている場合があります。戻る*
を選択すると、すべてのデータが取得されます。ええ、varchar(4096)
これは、選択した1000行にあり、必要のない追加の4メガバイトのデータを提供しますが、とにかく回線を介して送信されます。
スキーマの変更に関連して、最初にテーブルを作成したときにそのvarcharが存在していなかった場合がありますが、現在はそこにあります。
戻るを選択して*
20列を取得し、そのうち2列のみが必要な場合、コードの意図を伝えていません。クエリを実行すると、クエリselect *
の重要な部分が何であるかがわかりません。これらの列を含めないことで高速化するために、代わりにこの他のプランを使用するようにクエリを変更できますか?クエリが返す内容の意図が明確でないため、わかりません。
これらのスキーマの変更をもう少し探求するいくつかのSQLフィドルを見てみましょう。
最初に、初期データベース:http : //sqlfiddle.com/#!2 / a67dd/1
DDL:
create table one (oneid int, data int, twoid int);
create table two (twoid int, other int);
insert into one values (1, 42, 2);
insert into two values (2, 43);
SQL:
select * from one join two on (one.twoid = two.twoid);
そして、あなたが戻って取得列はoneid=1
、data=42
、twoid=2
、とother=43
。
さて、表1に列を追加するとどうなりますか?http://sqlfiddle.com/#!2/cd0b0/1
alter table one add column other text;
update one set other = 'foo';
そして、前と同じクエリからの私の結果はoneid=1
、data=42
、twoid=2
、とother=foo
。
テーブルの1つを変更するselect *
とaの値が乱れ、突然「other」をintにバインドするとエラーがスローされ、その理由はわかりません。
代わりに、SQLステートメントが
select
one.oneid, one.data, two.twoid, two.other
from one join two on (one.twoid = two.twoid);
テーブル1を変更しても、データが混乱することはありません。そのクエリは、変更前と変更後に同じように実行されます。
を実行すると、条件に一致するすべてのテーブルからすべての行select * from
がプルされます。あなたが本当に気にしないテーブルですら。これは、より多くのデータが転送されることを意味しますが、スタックのさらに下に潜む別のパフォーマンスの問題があります。
インデックス。(SOに関連:selectステートメントでインデックスを使用する方法?)
多数の列をプルバックする場合、データベースプランオプティマイザーはインデックスの使用を無視する場合があります。とにかくすべての列をフェッチする必要があり、インデックスを使用してからクエリのすべての列をフェッチするのに時間がかかるためです。完全なテーブルスキャンを実行するだけの場合よりも。
たとえば、ユーザーの姓(多くの操作を行い、インデックスを付ける)を選択している場合、データベースはインデックスのみのスキャンを実行できます(postgres wiki index only scan、mysql full table scan vs fullインデックススキャン、インデックスのみのスキャン:テーブルアクセスの回避)。
可能であれば、インデックスからのみ読み取ることについてかなりの最適化があります。情報を各インデックスページにすばやく取り込むことができます。これは、取得する情報が少ないためですselect *
。インデックスのみのスキャンでは、100倍の速度で結果を返すことが可能です(ソース:Select * is bad)。
これは、完全なインデックススキャンが優れていると言っているのではなく、依然として完全なスキャンですが、完全なテーブルスキャンよりも優れています。select *
パフォーマンスを損なうすべての方法を追いかけ始めたら、新しい方法を見つけ続けます。
別の懸念:JOIN
クエリであり、クエリ結果を連想配列に取得する場合(PHPの場合など)、バグが発生しやすいです。
事はそれです
foo
に列がid
あり、name
bar
に列がid
ありaddress
、SELECT * FROM foo
JOIN bar ON foo.id = bar.id
誰かがテーブルに列name
を追加するとどうなるかを推測してbar
ください。
name
列が結果に2回表示されるようになり、結果を配列に保存している場合、2番目name
(bar.name
)のデータが最初のname
(foo.name
)を上書きするため、コードは突然正常に動作しなくなります!
それは非常に明白ではないため、非常に厄介なバグです。把握するのに時間がかかる場合があり、テーブルに別の列を追加する人がそのような望ましくない副作用を予想することはできません。
(実話)。
したがって、を使用しないでください*
。取得する列を制御し、必要に応じてエイリアスを使用してください。
SELECT
句に別の列を追加する必要があります。これは、名前が一意でないことを願うときです。ところで、大規模なデータベースを備えたシステムではそれほど珍しいとは思いません。私が言ったように、私はかつてPHPコードの大きな泥玉でこのバグを捜すのに数時間を費やしました。そして今、別のケースを見つけました:stackoverflow.com/q/17715049/168719
多くの場合、すべての列を照会することは完全に合法です。
すべての列を常に照会することはそうではありません。
データベースエンジンにとっては、より多くの作業が必要になります。実際にデータを取得して送り返すという実際のビジネスに取りかかる前に、内部のメタデータを調べて、どの列を処理する必要があるかを調べなければなりません。OK、これは世界最大のオーバーヘッドではありませんが、システムカタログはかなりのボトルネックになる可能性があります。
1つまたは2つのフィールドだけが必要な場合に任意の数のフィールドをプルバックするため、ネットワークにとってはより多くの作業が必要になります。他の人が数十の余分なフィールドを追加し、そのすべてに大きなテキストのチャンクが含まれる場合、スループットは突然床を通過します-明白な理由はありません。これは、 "where"句が特に適切でなく、大量の行を引き戻している場合、さらに悪化します。これは、潜在的に大量のデータがネットワークを介してユーザーに送信されることです(つまり、低速になります)。
おそらく気にしないこの余分なデータをすべて引き戻して保存する必要があるため、アプリケーションにとってはより多くの作業が必要になります。
列の順序が変わるリスクがあります。OK、これについて心配する必要はありません(必要な列のみを選択する場合は気にしません)が、一度にそれらをすべて取得し、誰かがテーブル内の列の順序を再配置することに決めた場合、慎重に作成された、ホールの下のアカウントに提供するCSVエクスポートは、突然すべてがポットに移動します。
ところで、私は「誰か」と数回言った。データベースは本質的にマルチユーザーであることを忘れないでください。あなたがあなたがそう思うと思うそれらを制御できないかもしれません。
TOP
制限を追加しました。コードが表示したい数を読み取り、クエリを破棄する場合、それがどれほど重要かはわかりません。詳細はわかりませんが、クエリの応答は多少遅延して処理されると思います。いずれにせよ、「合法ではない」と言うよりも、「...合法的にははるかに少ない」と言う方が良いと思います。基本的に、正当なケースは、ユーザーがプログラマーよりも意味のあるものをよりよく理解できるケースとして要約します。
簡単な答えは、使用するデータベースによって異なります。リレーショナルデータベースは、必要なデータを高速で信頼性の高いアトミックな方法で抽出するために最適化されています。大規模なデータセットや複雑なクエリでは、SELECT *よりもはるかに高速で安全であり、「コード」側での結合と同等の処理を行います。Key-Valueストアには、このような機能が実装されていないか、実稼働で使用できるほど成熟していない場合があります。
つまり、使用しているデータ構造をSELECT *で設定し、残りをコード内で解決することはできますが、拡張したい場合はパフォーマンスのボトルネックが見つかります。
最も近い比較はデータの並べ替えです。クイックソートまたはバブルソートを使用でき、結果が正しくなります。しかし、最適化されず、同時実行性を導入し、アトミックにソートする必要がある場合、間違いなく問題が発生します。
もちろん、SQLクエリを実行できるプログラマーに投資するよりもRAMとCPUを追加する方が安価で、JOINとは何なのかを理解することさえできます。
Customer customer = this._db.Customers.Where( “it.ID = @ID”, new ObjectParameter( “ID”, id ) ).First();
を参照してくださいオフェンス取る時間 2ページを
var cmd = db.CreateCommand(); cmd.CommandText = "SELECT TOP 1 * FROM Customers WHERE ID = @ID"; cmd.Parameters.AddWithValue("@ID", id); var result = cmd.ExecuteReader();
....のようなもので、次に各行からCustomerを作成します。LINQはそのズボンを打ち負かします。
var customer = _db.Customers.Where(it => it.id == id).First();
。
IMO、明示的か暗黙的かについて。コードを書くとき、すべての部分がたまたまそこにあるからというだけでなく、コードを機能させたので、機能させたいのです。すべてのレコードを照会し、コードが機能する場合、先に進む傾向があります。後で何かが変更されてコードが機能しなくなった場合、多くのクエリや関数をデバッグするのは大変な苦労であり、そこにあるべき値を探し、値の参照は*のみです。
また、N層アプローチでは、データベーススキーマの中断をデータ層に分離するのが最善です。データ層がビジネスロジックに*を渡し、ほとんどの場合プレゼンテーション層で渡される場合、デバッグ範囲を指数関数的に拡張しています。
select *
ははるかに悪いです!
オーバーヘッドとは別に、そもそも避けたいことは、プログラマーとして、データベース管理者が定義した列の順序に依存しないと言えます。すべてが必要な場合でも、各列を選択します。
データベースのすべての列を取得するという目的でビルドを使用しない理由はわかりません。次の3つのケースがあります。
データベースに列が追加され、コードでも必要になります。a)With *は適切なメッセージで失敗します。b)*がなくても動作しますが、期待どおりの動作をしません。
データベースに列が追加されますが、コードには必要ありません。a)With *は失敗します。つまり、セマンティクスは「すべてを取得する」ことを意味するため、*は適用されなくなります。b)*なしで機能します。
列が削除されましたコードはどちらの方法でも失敗します。
現在、最も一般的なケースはケース1です(*を使用しているため、おそらくすべてが必要です)。*がなければ、正常に動作するが期待どおりの動作をしないコードを作成できます。
私の意見ではエラーが発生しやすい列インデックスに基づいて列データを取得するコードを考慮していません。列名に基づいて取得するのは、はるかにロジックです。
Select *
は、アプリケーション開発目的ではなく、アドホッククエリの利便性を目的としています。またはselect count(*)
、クエリエンジンがインデックスを使用するかどうか、使用するインデックスなどを決定できるようにする統計構造で使用し、実際の列データを返さないようにします。または、のような句で使用する場合もwhere exists( select * from other_table where ... )
、クエリエンジンが独自に最も効率的なパスを選択するように誘い、サブクエリはメインクエリからの結果を制約するためにのみ使用されます。など
select *
は、すべての列を取得するというセマンティクスがあります。アプリケーションでこれが本当に必要な場合は、使用しない理由はわかりません。select *
ビルドの目的がすべての列を取得することではないことについて言及しているリファレンス(Oracle、IBM、Microsoftなど)を指すことができますか?
select *
すべての列を取得するために存在します...アドホッククエリのための便利な機能として、実稼働ソフトウェアでの素晴らしいアイデアではありません。理由はこのページの回答ですでに十分に説明されているため、独自の詳細な回答を作成しませんでした。 •)を選択限られたケースで、クエリプランの最適化の失敗(いくつかのケースでは、インデックスを使用するために失敗)、•)非効率的なサーバのI / Oを持つことができるだけで使用されるインデックスなどを
select *
実際の運用アプリケーションでの使用を正当化するエッジケースがあるかもしれませんが、エッジケースの性質は、一般的なケースではないということです。:-)
select *
です。すべての列が本当に必要な場合に私が言っていたことは、あなたが使用すべきではない理由はわかりませんselect *
。ただし、すべての列が必要なシナリオはほとんどありません。