質問
サブクエリを使用して結果セットに列を追加するSQLクエリ(MSSQLSERVER)があります:
SELECT P.name,
(select count(*) from cars C where C.type = 'sports') AS sportscars,
(select count(*) from cars C where C.type = 'family') AS familycars,
(select count(*) from cars C where C.type = 'business') AS businesscars
FROM people P
WHERE P.id = 1;
上記のクエリは、ちょっとしたナンセンスなテスト設定からのものですが、私が考える例としては十分に役立ちます。私が実際に取り組んでいるクエリは、目前の問題から気を散らすだけの複雑なテーブルにまたがっています。
上記の例では、テーブル" people"の各レコードまた、「wantsSportscar」、「wantsFamilycar」の3つの追加列があります。および「wantsBusinesscar」。今、私がしたいのは、それぞれの「欲しい」.....」の場合、追加の各列の副選択のみを行うことですpeopleテーブルのフィールドは「true」に設定されています。つまり、P.wantsSportscarがその特定の人物に対してtrueに設定されている場合にのみ、最初の副選択を行います。 2番目と3番目の副選択も同様に機能するはずです。
このクエリが機能する方法は、特定の人の名前と、所有したい車の種類に使用できるモデルの数を表示することです。最終結果セットには常に1つのレコード、つまり1人の特定のユーザーのレコードのみが含まれることに注意してください。
特定のタイプの車に興味がない人は、そのタイプの列が最終結果セットに含まれないことが重要です。これが明確であることを確認する例:
Aがスポーツカーとファミリーカーを必要とする場合、結果には「名前」、「スポーツカー」の列が含まれます。および「ファミリーカー」。
人Bがビジネスカーを望んでいる場合、結果には列「名前」が含まれます。および「businesscar」」。
IF、CASE、およびEXISTSステートメントでさまざまな組み合わせを使用しようとしましたが、これまでのところ、構文的に正しい解決策を得ることができませんでした。これが可能かどうかは誰にもわかりますか?クエリはストアドプロシージャに保存されることに注意してください。
解決
あなたの場合、 8
の列レイアウトが可能です。これを行うには、 8
の個別のクエリが必要になります(またはクエリを動的に構築します)。
単一のクエリ内で結果セットのレイアウトを変更することはできません。
代わりに、次のようにクエリを設計できます。
SELECT P.name,
CASE WHEN wantssport = 1 THEN (select count(*) from cars C where C.type = 'sports') ELSE NULL END AS sportscars,
CASE WHEN wantsfamily = 1 THEN (select count(*) from cars C where C.type = 'family') ELSE NULL END AS familycars,
CASE WHEN wantsbusiness = 1 THEN (select count(*) from cars C where C.type = 'business') ELSE NULL END AS businesscars
FROM people P
WHERE P.id = 1
人が必要としない場合は適切な列で NULL
を選択し、クライアント側でこれらの NULL
を解析します。
リレーショナルモデルは、 relations
の観点からクエリに応答します。
あなたの場合、関係は次のとおりです。「この人のニーズは、この多くのスポーツカー、この多くのビジネスカー、この多くのファミリーカーに満足しています」
リレーショナルモデルは、常にこの特定の質問に4次の関係で回答します。
リレーションメンバを省略しません。代わりに、 NULL
に設定します。これは、 SQL
の方法で、関係が定義されていない、適用できる、意味がない。
他のヒント
私は主にOracleの男ですが、同じことが当てはまる可能性が高いです。私が誤解しない限り、あなたが望むものはそのレベルでは不可能です-あなたは常に静的な数の列を持っています。クエリは列が空かどうかを制御できますが、クエリの最も外側の部分でX個の列を指定したため、結果セットでX列を取得することが保証されます。
私が言ったように、私はMS SQL Serverに慣れていませんが、動的SQLを実行する方法があると推測しています。 p>
最初に個別の行として値を一時テーブルに選択し、次にそのテーブルでPIVOTを実行(行を列に変換)することで、必要な処理を実行できる場合があります。
もし人が 特定の種類の車に興味がある、 そのタイプの列は 最終結果セットに含まれます。あ これが明確であることを確認する例:
通常のSQLでは実行できません。この列をNULLまたはゼロにすることをお勧めします。
新しい車が追加されたときにクエリを動的に拡張したい場合は、PIVOTingが多少役立ちます。
この作業を簡単にするために学びたい3つの基本事項があります。 1つ目はデータの正規化、2つ目はGROUP BY、3つ目はPIVOTです。
最初に、データの正規化。 Peopleテーブルの設計は、最初の通常の形式ではありません。列「wantsports」、「wantfamily」、「wantbusiness」実際には繰り返しグループですが、1つのようには見えません。テーブルのデザインを変更できる場合は、3番目のテーブルを作成し、「peoplewant」と呼び、2つのキー列、personidとcartypeを作成すると便利です。必要に応じてこの設計がより柔軟で強力になる理由について詳しく説明できますが、ここではそれをスキップします。
GROUP BYへ。これにより、結果の1行に各グループを要約した結果を作成できます。
SELECT
p.name,
c.type,
c.count(*) as carcount
FROM people p,
INNER JOIN peoplewant pw ON p.id = pw.personid
INNER JOIN cars c on pw.cartype = c.type
WHERE
p.id = 1
GROUP BY
p.name,
c.type
この(テストされていない)クエリは、ユーザーが望む車の種類ごとに個別の行があることを除いて、必要な結果を提供します。
最後に、PIVOT。 DBMSのPIVOTツールを使用すると、この結果を、その人の行が1行だけで、その人が望むカータイプごとに個別の列があるフォームに変換できます。私は自分でPIVOTを使用したことがないので、他の人にこの応答を編集して、PIVOTを使用した例を提供してもらいます。
同じ手法を使用して1回の掃引で複数の人のデータを取得する場合、任意の人が必要とする必要なタイプごとに列が表示され、不要な人の場合はPIVOT結果にゼロが表示されることに注意してください結果列にある車の種類。
Googleの検索でこの投稿に出くわしたので、このパーティーに少し遅れていることに気付きましたが、..これは本当に可能です。通常は非常に悪いこと(tm)と見なされるため、この方法で実際に実行することはお勧めしません。
動的SQLが答えです。
その方法を言う前に、これを前置きしたいのですが、アプリケーションからの入力をサニタイズしていない場合、ダイナミックSQLは非常に危険なものです。
したがって、注意して続行してください:
declare @sqlToExecute nvarchar(max);
declare @includeSportsCars bit;
declare @includeFamilyCars bit;
declare @includeBusinessCars bit;
set @includeBusinessCars = 1
set @includeFamilyCars = 1
set @includeSportsCars = 1
set @sqlToExecute = 'SELECT P.name '
if @includeSportsCars = 1
set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''sports'') AS sportscars, ';
if @includeFamilyCars = 1
set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''family'') AS familycars, ';
if @includeBusinessCars = 1
set @sqlToExecute = @sqlToExecute + '(select count(*) from cars C where C.type = ''business'') AS businesscars '
set @sqlToExecute = @sqlToExecute + ' FROM people P WHERE P.id = 1;';
exec(@sqlToExecute)