6

これは、ストアドプロシージャからのスニペットです

SELECT  NULL AS StoryID
      , AlbumID
      , CAST(NULL as varchar) AS StoryTitle
      , AlbumName
      , (SELECT URL FROM AlbumPictures AS AlbumPictures_3 WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')) AS AlbumCover
      , Votes
      , CAST(NULL as Int) AS PictureId
      , 'albums' AS tableName
      , (SELECT NestedAlbums.AlbumID FROM NestedAlbums WHERE (AlbumID = Albums.AlbumID)) AS Flag
INTO #Results2
FROM Albums WHERE AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID))

上記のクエリでネストされた選択を使用しました。ORNested Selectsよりも優れているかどうか知りたいのですが、使用する必要がありますか?LEFT/Right JOINSJOINS

テーブルアルバム:

ここに画像の説明を入力してください

テーブルNestedAlbums:

ここに画像の説明を入力してください

4

5 に答える 5

8

一般に、これを明示的に書くOUTER JOIN方が良いでしょう。

SQL Serverは、サブクエリが最大で1行のみを返すことを確認するサブクエリバージョンを使用して、プランにアサーションを追加する必要があります(これが一意のインデックスによって保証されている場合を除く)。これにより、使用可能な変換が制限される可能性があります。詳細については、スカラーサブクエリを参照してください。

また(両方のサブクエリが異なるため、質問の例には関係ありません)明示的に書き込むとJOIN、結合されたテーブルの複数の列を1つのルックアップで使用できますが、同様のサブクエリを個別に使用することはできません(SQL Serverには検出するロジックがありません)一般的なサブ式)。

編集:

コメントでの議論に続いて

SELECT NULL                      AS StoryID,
       A.AlbumID,
       CAST(NULL AS VARCHAR(30)) AS StoryTitle,
       A.AlbumName,
       AP.URL                    AS AlbumCover,
       A.Votes,
       CAST(NULL AS INT)         AS PictureId,
       'albums'                  AS tableName,
       CASE
         WHEN EXISTS (SELECT *
                      FROM   NestedAlbums NA
                      WHERE  NA.AlbumID = A.AlbumID
                             AND ( AccountId = @AccountId )) THEN 1
         ELSE 0
       END                       AS Flag
INTO   #Results2
FROM   Albums A
       LEFT OUTER JOIN AlbumPictures AP
         ON ( AP.AlbumID = A.AlbumID )
            AND ( AP.AlbumCover = 'True' )
WHERE  A.AlbumID IN (SELECT StringVal
                     FROM   funcListToTableInt(@whereAlbumID)) 

これにはまだSELECTリストにサブクエリがありますCASE ... EXISTS が、半結合として効率的に実装されることに気付くかもしれません。

現時点では、クエリはアルバムごとに最大で1つの一致する行が返されるAlbumPicturesと想定しており、この想定が当てはまらない場合はエラーになります。これにより、エラーが返されず、さまざまなURLsで複数の行が取得されるというセマンティクスが変更されます。それを望まない場合は、どちらURLを使用するかを定義し、GROUP BY

于 2013-01-06T16:46:23.083 に答える
4

最初の違いは、このFROM句が結果の最初のカーディナリティを管理することです。

あなたの場合、結果はアルバムの行ごとに1行になります。SELECT句のスカラーサブクエリはこれを変更できません。サブクエリがたまたま複数の行を返す場合、SQLServerは例外をスローします。結果に追加されることはありません。

この論理結合をFROM句に移動すると、最初のカーディナリティが再定義されます。アルバムの行ごとに1行ではなく、Album LEFT OUTER JOIN AblumPictures ON...などの行ごとに1行になります。アルバムの行ごとに複数の行が生成される場合、SQLServerは副選択の場合のように例外をスローしません。むしろ、結果に行を追加します。

したがって、この点で、サブクエリは意図を表現するためのより良い仕事をし、その意図に違反するデータからあなたを積極的に保護するより良い仕事をします:「アルバムごとに1行を与え、アルバムごとに、ここのURL、ネストされたIDを含めますそこから」など。

ただし、機能的に言​​えば、大きな欠点があります。スカラーサブクエリはタプル全体を返すことができません。サブクエリを作成するためにこのすべての作業を実行し、SQL Serverがそれを実行するためにこのすべての作業を実行しました。これで、この1つのスカラー戻り値に制限されます。それで問題ない場合もありますが、さらに必要な場合もあります。さらに必要な場合は、FROM句が必要です。

FROMスカラーサブクエリに相当する最も近い句はOUTER JOINではなく、不思議なOUTER APPLYです。OUTER APPLY スカラー式ではありません。タプル全体と任意の数の行を返します。

最初の近似:

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID
FROM Albums
OUTER APPLY (
  SELECT TOP (1) * FROM AlbumPictures
  WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')
  ) AlbumPictures
OUTER APPLY (
  SELECT TOP (1) * FROM NestedAlbums 
  WHERE (AlbumID = Albums.AlbumID)
  ) NestedAlbums 
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID))

したがって、のおかげでTOP (1)、アルバムは依然として結果の最初のカーディナリティを支配します。ただし、関連するテーブルのすべての列にアクセスできるようになりました。これはすばらしいことです。

次に、それが必要ではないと確信している場合(キーとインデックスにより、サブクエリは1行しか返すことができない)、より単純な形式を使用して書き直すことができます。TOP (1)

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID
FROM Albums
OUTER APPLY (
  SELECT * FROM AlbumPictures
  WHERE (AlbumID = Albums.AlbumID) AND (AlbumCover = 'True')
  ) AlbumPictures
OUTER APPLY (
  SELECT * FROM NestedAlbums 
  WHERE (AlbumID = Albums.AlbumID)
  ) NestedAlbums 
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID))

これは論理的に次と同等OUTER JOINです:

SELECT Albums.*, AlbumPictures.URL, NestedAlbums.AlbumID
FROM Albums
LEFT OUTER JOIN AlbumPictures 
  ON AlbumPictures.AlbumID = Albums.AlbumID 
 AND AlbumPictures.AlbumCover = 'True'
LEFT OUTER JOIN NestedAlbums 
  ON NestedAlbums.AlbumID = Albums.AlbumID
WHERE Albums.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID))

そして、あなたはそれを持っています。どちらが良いですか?さて、あなたが何をするにしても、それを単純にしてください。

パフォーマンスワイズ、一般的に、フォーム間に大きな違いはありません。特定のテーブルとインデックスの実行プランを並べて比較できます。SQLServerが論理的に同等のクエリをどのように書き換えるかを確認することは良い学習経験です。OUTER APPLY(w / o TOP (1))と。については同じ計画が見られると思いLEFT OUTER JOINます。

于 2013-01-06T18:20:39.937 に答える
2

これは、OUTERJOINを使用するというMartinの提案に基づく推測クエリです。彼が「明示的な」参加を言ったとき、彼は技術的なタイプの参加ではなく、単にその技術を選択することを指していたと私は信じています。以下のクエリは、単一のアルバムIDを検索するときにNestedAlbumsテーブルとAlbumCoverテーブルから1行しか返されないことを前提としています。これが当てはまらない場合は、「重複行」が発生し、さらにいくつかの条件を1つに追加する必要があります。それらを削除するための結合句の。このクエリは、AlbumCoverがAlbumsテーブルにあることも前提としています。そうでない場合は、テキストa.AlbumCoverを次のように変更する必要がありますac.AlbumCover

SELECT  NULL AS StoryID
      , a.AlbumID
      , CAST(NULL as varchar) AS StoryTitle
      , a.AlbumName
      , ac.URL AS AlbumCover
      , a.Votes
      , CAST(NULL as Int) AS PictureId
      , 'albums' AS tableName
      , na.AlbumID AS Flag
INTO #Results2
FROM Albums a
LEFT OUTER JOIN NestedAlbums na ON a.AlbumID = na.AlbumID
LEFT OUTER JOIN AlbumCover ac ON a.AlbumID = ac.AlbumID AND a.AlbumCover = 'True'
WHERE a.AlbumID IN (SELECT StringVal FROM funcListToTableInt(@whereAlbumID))

このクエリはAlbumsから結果を取得し、AlbumIDが一致するNestedAlbumsの行と、AlbumCoverテーブルのAlbumIDが一致する行を含みます(ただし、AlbumsのAlbumCoverフィールドがその行で「True」の場合のみ)。 。演算子としてLEFTOUTERJOINを選択したため、NestedAlbumsまたはAlbumCoverのいずれかに一致する行がない場合、SQL Serverはそれらのフィールドに対してNULLを返しますが、とにかくクエリによって行が返されます。結合がINNERJOINであった場合、結合されたテーブルに一致する行がなかった場合、メインテーブルの行も除外されます。

于 2013-01-06T17:12:22.500 に答える
1

私のお金では、ネストされたselectの代わりに結合を使用して記述されたクエリは、人々が読みやすく理解しやすいため(メンテナンス、サポート、将来の変更など)、すべてのテーブル結合ロジックが1か所にあるため「優れています」(from句)、および返されるすべての列が1つの別の場所(select句)にあるため、何が起こっているのかを簡単に把握できます。

他の人は、クエリを実行するパフォーマンスヒットがあるかもしれないし、ないかもしれないと指摘しました(いつものように、あいまいですが非常に関連性のある詳細を指摘するために@Martin Smithに+1します)、そしてそれらの答えはあなたが探しているものですここで...しかし、彼らにダンプされたコードが実際に何をしようとしているのかを理解しなければならない将来の貧しい開発者を検討するために1、2分かかります。結局のところ、それはあなたかもしれません...

于 2013-01-06T17:11:40.927 に答える
0

あなたの場合、パフォーマンスを考慮すれば、両方のアプローチはほぼ同じです。(データベースに適切にインデックス付けされたテーブルがあることを考慮して)

必要なJOINが多すぎる場合、JOINは使いやすく効率的です。JOINは、参加中に重複データを作成する場合がありますが、ネストされたクエリは作成しません。

ただし、最終的にパフォーマンスは、データの量と構成に基づいて決定されます。SQLパフォーマンスモニターツールを使用して、データに基づいて検証できます。

于 2013-01-06T16:51:36.073 に答える