ビューを作成するために「*」を使用するのはなぜですか?

StackOverflow https://stackoverflow.com/questions/262450

  •  06-07-2019
  •  | 
  •  

質問

なぜ「*」を使用してビューを作成するのが悪いのですか?

複雑な結合があり、すべてのフィールドがどこかで使用される可能性があるとします。

その後、必要なフィールドを選択するだけです。

SELECT field1, field2 FROM aview WHERE ...

ビュー「aview」」 SELECT table1。*、table2。* ... FROM table1 INNER JOIN table2 ...

table1とtable2の2つのフィールドの名前が同じ場合、問題が発生します。

これがビューで「*」を使用するのが悪い理由だけですか?

「*」を使用すると、情報が存在するため、異なるコンテキストでビューを使用できます。

不足しているものは何ですか?

よろしく

役に立ちましたか?

解決

ソフトウェアには「ただ悪い」ものはあまりないと思いますが、悪い方法で悪用されるものはたくさんあります:-)

あなたが与える例は、*があなたが期待するものをあなたに与えないかもしれない理由であり、他にもあると思います。たとえば、基礎となるテーブルが変更され、列が追加または削除された場合、*を使用するビューは引き続き有効ですが、それを使用するアプリケーションが破損する可能性があります。ビューが列に明示的に名前を付けていた場合、スキーマを変更するときに誰かが問題を見つける可能性が高くなりました。

一方、実際にはビューを 明るくしたいかもしれません 基礎となるテーブルへのすべての変更を受け入れます。その場合、*は 欲しいものをそのままにしてください。

更新: OPが特定のデータベースベンダーを念頭に置いているかどうかはわかりませんが、最後のコメントがすべてのタイプに当てはまるわけではないことが明らかになりました。これを指摘してくれたuser12861とJonny Leedsに感謝します。答えを編集するのに6年以上かかりました。

他のヒント

ここでのコメントの多くは非常に優れており、基になるテーブルが変更された場合にエラーや異なる結果を引き起こすなど、クエリでワイルドカードを使用する一般的な問題を参照していますが、カバーされていない別の問題は最適化です。テーブルのすべての列をプルするクエリは、実際に必要な列のみをプルするクエリほど効率的ではない傾向があります。確かに、すべての列が必要な場合があり、特に大きなテーブルでは、すべての列を参照する必要がある主要なPIAですが、サブセットのみが必要な場合は、必要以上の列でクエリを動かしません。

" * "の別の理由ビューだけでなくクエリでも、列が名前を変更したり、基になるテーブルの位置を変更したりする可能性があるというリスクがあります。ワイルドカードを使用すると、ビューを変更する必要なく、このような変更に簡単に対応できます。ただし、アプリケーションが結果セット内の位置で列を参照する場合、または列名をキーとする結果セットを返す動的言語を使用する場合、デバッグが難しい問題が発生する可能性があります。

常にワイルドカードを使用することは避けます。こうすることで、列の名前が変更された場合、すぐにビューまたはクエリでエラーが発生し、修正する場所がわかります。基になるテーブル内の列の位置が変更された場合、ビューまたはクエリで列の順序を指定すると、これが補正されます。

これらの他の回答にはすべて良い点がありますが、SQLサーバーでは少なくとも間違った点もいくつかあります。これを試してください:

create table temp (i int, j int)
go
create view vtemp as select * from temp
go
insert temp select 1, 1
go
alter table temp add k int
go
insert temp select 1, 1, 1
go
select * from vtemp

SQL Serverは" new"について学習しません。列が追加されたとき。あなたが望むものに応じて、これは良いことも悪いこともありますが、いずれにしても、それに依存することはおそらく良くありません。したがって、それを避けるのは良いアイデアのように思えます。

私にとって、この奇妙な振る舞いは、ビューでselect *を避ける最も説得力のある理由です。

コメントから、MySQLには同様の動作があり、Oracleにはないことがわかりました(テーブルへの変更について学習します)。この不整合は、ビューでselect *を使用しない理由です。

制作に「*」を使用するのは悪いことです。 1回限りのクエリには適していますが、運用コードでは常にできる限り明示的にする必要があります。

特にビューの場合、基礎となるテーブルに列が追加または削除されている場合、ビューは間違っているか、再コンパイルされるまで壊れます。

ビュー内で SELECT * を使用しても、ビューの外部で列が使用されない場合、パフォーマンスのオーバーヘッドはあまり発生しません。オプティマイザーは列を最適化します。 SELECT * FROM TheView は、ネットワーク接続を介して列を追加した場合と同様に、おそらく帯域幅を浪費する可能性があります。

実際、データウェアハウスの多数の巨大なテーブルのほとんどすべての列をリンクするビューでは、ビューの外部から要求される列が比較的少ないにもかかわらず、パフォーマンスの問題がまったく発生しないことがわかりました。オプティマイザーはそれを適切に処理し、外部フィルター基準をビューに非常にうまくプッシュダウンできます。

ただし、上記のすべての理由から、 SELECT * を使用することはほとんどありません。

いくつかのビジネスプロセスがあり、いくつかのCTEが互いの上に構築され、派生列から派生列から派生列を効果的に構築します(ビジネスがこれらの計算を合理化および簡素化するときにいつかリファクタリングされることを期待しています)その場合、毎回すべての列をドロップする必要があり、 SELECT * を使用しますが、 SELECT * はベースレイヤーでは使用されず、最初のCTEと最後のCTE。

SQL Serverの状況は、実際には@ user12861の答えが示すよりもさらに悪いです。複数のテーブルに対して SELECT * を使用すると、クエリの早い段階で参照されるテーブルに列を追加すると、実際に古い列を装って新しい列の値を返すビュー。以下の例を参照してください。

-- create two tables
CREATE TABLE temp1 (ColumnA INT, ColumnB DATE, ColumnC DECIMAL(2,1))
CREATE TABLE temp2 (ColumnX INT, ColumnY DATE, ColumnZ DECIMAL(2,1))
GO


-- populate with dummy data
INSERT INTO temp1 (ColumnA, ColumnB, ColumnC) VALUES (1, '1/1/1900', 0.5)
INSERT INTO temp2 (ColumnX, ColumnY, ColumnZ) VALUES (1, '1/1/1900', 0.5)
GO


-- create a view with a pair of SELECT * statements
CREATE VIEW vwtemp AS 
SELECT *
FROM temp1 INNER JOIN temp2 ON 1=1
GO


-- SELECT showing the columns properly assigned
SELECT * FROM vwTemp 
GO


-- add a few columns to the first table referenced in the SELECT 
ALTER TABLE temp1 ADD ColumnD varchar(1)
ALTER TABLE temp1 ADD ColumnE varchar(1)
ALTER TABLE temp1 ADD ColumnF varchar(1)
GO


-- populate those columns with dummy data
UPDATE temp1 SET ColumnD = 'D', ColumnE = 'E', ColumnF = 'F'
GO


-- notice that the original columns have the wrong data in them now, causing any datatype-specific queries (e.g., arithmetic, dateadd, etc.) to fail
SELECT *
FROM vwtemp
GO

-- clean up
DROP VIEW vwTemp
DROP TABLE temp2
DROP TABLE temp1

すべての変数が常に必要なわけではなく、また、特に必要なものについて考えていることを確認するためです。

たとえば、サイトでユーザーのリストを作成するときに、データベースからすべてのハッシュされたパスワードを取得しても意味がないため、select *は非生産的です。

かつて、別のデータベース(同じサーバー上)のテーブルに対してビューを作成しました

Select * From dbname..tablename

ある日、列がターゲットテーブルに追加されました。ビューは、再デプロイされるまでまったく間違った結果を返し始めました。


まったく間違っています:行がありません。

これはSql Server 2000にありました。

*を使用していても、ビューがキャプチャしたsyscolumns値が原因であると推測します。

SQLクエリは、基本的に、プログラマーが何らかのコンテキストで使用するために設計した機能単位です。長期的な安定性とサポート可能性(おそらくあなた以外の誰かによる)のために、機能ユニット内のすべてが目的のためにそこにあるべきであり、それが存在する理由、特にデータのすべての要素が合理的に明白(または文書化)である必要があります。

2年後にあなたのクエリを変更する必要性または要望があった場合、それを台無しにできると確信する前に、かなり徹底的に理解することを期待します。つまり、すべての列が呼び出される理由を理解する必要があるということです。 (これは、クエリを複数のコンテキストで再利用しようとしている場合はさらに明白です。一般的には、同様の理由で問題になります。)出力に何らかの目的に関係のない列が表示された場合、それが何をしたのか、なぜ、そしてそれを変更した場合の結果が理解できなかったと確信しています。

通常、*を使用するのは悪い考えです。一部のコード認証エンジンはこれを警告としてマークし、必要な列のみを明示的に参照することを推奨しています。 *を使用すると、すべてではなく一部の列のみが必要になるため、パフォーマンスが低下する可能性があります。しかし、一方で、*の使用が理想的な場合もあります。提供された例を使用して、このビュー(aview)に対して、これらのテーブルのすべての列が常に必要になるとしたらどうでしょう。将来、列が追加されたときに、ビューを変更する必要はありません。これは、対処しているケースに応じて、良い場合も悪い場合もあります。

使用している言語に依存すると思います。言語またはDBドライバーが結果のdict(Python、Perlなど)または連想配列(PHP)を返す場合、select *を使用することを好みます。配列のインデックスとしてではなく、名前で列を参照している場合、コードが理解しやすくなります。

他の誰も言及していないようですが、SQL Server内では、スキーマバインディング属性。

これにより、ビュー定義に影響を与えるベーステーブルの変更(削除を含む)が防止されます。

これは、状況によっては役立つ場合があります。私はあなたの質問に正確に答えていないことを理解していますが、それでもそれを強調したいと思いました。

また、select *を使用した結合がある場合、結合フィールドのデータが繰り返されるため、必要以上のデータが自動的に返されます。これは、データベースとネットワークリソースの無駄です。

他のビューを呼び出すビューを使用するのに十分な素朴な場合、select *を使用すると、パフォーマンスがさらに低下する可能性があります(これは、パフォーマンスに悪影響を及ぼす手法であり、必要のない複数の列を呼び出すと、さらに悪化します)。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top