4

副選択を使用して結果セットに列を追加する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」、「wantsBusinesscar」の3つの追加列もあります。ここで私がやりたいのは、peopleテーブルのそれぞれの「wants.....」フィールドが「true」に設定されている場合にのみ、追加の各列の副選択を行うことです。つまり、P.wantsSportscarがその特定の人物に対してtrueに設定されている場合にのみ、最初の副選択を実行したいと思います。2番目と3番目の副選択も同様に機能するはずです。

したがって、このクエリが機能する方法は、特定の人の名前と、その人が所有したい車のタイプに使用できるモデルの数を表示することです。私の最終結果セットには、常に1つのレコード、つまり1人の特定のユーザーのレコードしか含まれないことに注意してください。

特定の種類の車に興味がない場合は、その種類の列が最終結果セットに含まれないことが重要です。これが明確であることを確認するための例:

人物Aがスポーツカーとファミリーカーを希望する場合、結果には「名前」、「スポーツカー」、「ファミリーカー」の列が含まれます。

人物Bがビジネスカーを希望する場合、結果には「name」列と「businesscar」列が含まれます。

IF、CASE、およびEXISTSステートメントとさまざまな組み合わせを使用しようとしていますが、これまでのところ、構文的に正しい解決策を得ることができませんでした。これが可能かどうか誰かが知っていますか?クエリはストアドプロシージャに保存されることに注意してください。

4

6 に答える 6

6

あなたの場合、8可能な列レイアウトがあり、これを行うには、個別のクエリが必要になります8(またはクエリを動的に作成します)。

1 つのクエリ内で結果セットのレイアウトを変更することはできません。

代わりに、次のようにクエリを設計できます。

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

あなたの場合、「この人のニーズは、これだけのスポーツカー、これだけのビジネスカー、そしてこれだけのファミリーカーで満たされる」という関係になります。

リレーショナル モデルは常に、この特定の質問に四次関係で答えます。

関係メンバーを省略しません。代わりに、関係のメンバーが定義されていない、適用できない、または意味がないことを示す方法NULLであるに設定するだけです。SQL

于 2009-06-18T12:04:46.260 に答える
0

この作業を簡単にするために、3 つの基礎を学びましょう。1 つ目はデータの正規化、2 つ目は GROUP BY、3 つ目は PIVOT です。

まず、データの正規化。people テーブルの設計は、第 1 正規形ではありません。「wantsports」、「wantfamily」、「wantbusiness」の列は、実際には繰り返しグループですが、同じようには見えないかもしれません。テーブルのデザインを変更できる場合は、personid と cartype の 2 つのキー列を持つ 3 番目のテーブル (「peoplewant」と呼びます) を作成すると有利です。必要に応じて、この設計がより柔軟で強力になる理由について詳しく説明できますが、今はスキップします。

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

この (テストされていない) クエリは、必要な結果を提供しますが、結果には、ユーザーが希望する車の種類ごとに個別の行が含まれます。

最後にピボット。DBMS の PIVOT ツールを使用すると、この結果をフォームに変換して、その人物を 1 行だけ表示し、その人物が希望する車の種類ごとに個別の列を作成できます。私自身は PIVOT を使用したことがないので、誰かにこの応答を編集して PIVOT を使用した例を提供してもらいます。

同じ手法を使用して 1 回のスイープで複数の人のデータを取得する場合、すべての人が希望するタイプごとに列が表示され、車のタイプを希望しない人の PIVOT 結果にはゼロが表示されることに注意してください。それは結果列にあります。

于 2009-06-18T12:33:59.243 に答える
0

最初に値を個別の行として一時テーブルに選択し、次にそのテーブルで PIVOT を実行する (行を列に変換する) ことで、必要なことを実行できる場合があります。

于 2009-06-18T12:08:44.177 に答える
0

私は主にオラクル派ですが、同じことが当てはまる可能性が高いです。私が誤解していない限り、あなたが望むことはそのレベルでは不可能です - あなたは常に静的な数の列を持っています. クエリは列が空かどうかを制御できますが、クエリの最も外側の部分で X 列の数を指定しているため、結果セットで X 列を取得することが保証されています。

私が言ったように、私は MS SQL Server に慣れていませんが、動的 SQL を実行する何らかの方法があると推測しています。

于 2009-06-18T12:02:43.690 に答える
0

ある人が特定のタイプの車に興味がない場合、そのタイプの列が最終的な結果セットに含まれないようにすることが重要です。これが明確であることを確認する例:

プレーン SQL では実行できません。この列を NULL または ZERO にすることをお勧めします。

新しい車が追加されたときにクエリを動的に拡張したい場合は、PIVOTing が多少役に立ちます。

于 2009-06-18T12:11:10.740 に答える
0

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)
于 2009-10-21T23:37:14.250 に答える