54

ユーザーが選択し、SQLパラメーターとして渡されたソート順とソート方向を持つオブジェクトのリストを返すストアドプロシージャを作成しようとしています。

次の列を持つ製品のテーブルがあるとします:product_id(int)、name(varchar)、value(int)、created_date(datetime)およびパラメーター@sortDirおよび@sortOrder

こんなことしたい

select *
from Product
  if (@sortOrder = 'name' and @sortDir = 'asc') 
  then order by name asc
  if (@sortOrder = 'created_date' and @sortDir = 'asc') 
  then order by created_date asc
  if (@sortOrder = 'name' and @sortDir = 'desc') 
  then order by name desc
  if (@sortOrder = 'created_date' and @sortDir = 'desc') 
  then order by created_date desc

caseステートメントで試してみましたが、データ型が異なるため問題が発生していました。誰か提案がありますか?

4

4 に答える 4

84

CASE値を返す式です。のように、フローの制御用ではありませんIFIFまた、クエリ内で使用することはできません。

残念ながら、表現にはいくつかの制限があり、CASEやりたいことをするのが面倒になります。たとえば、CASE式のすべてのブランチは同じタイプを返すか、暗黙的に同じタイプに変換可能である必要があります。文字列と日付では試しません。CASEソート方向の指定にも使用できません。

SELECT column_list_please
FROM dbo.Product -- dbo prefix please
ORDER BY 
  CASE WHEN @sortDir = 'asc' AND @sortOrder = 'name' THEN name END,
  CASE WHEN @sortDir = 'asc' AND @sortOrder = 'created_date' THEN created_date END,
  CASE WHEN @sortDir = 'desc' AND @sortOrder = 'name' THEN name END DESC,
  CASE WHEN @sortDir = 'desc' AND @sortOrder = 'created_date' THEN created_date END DESC;

間違いなく簡単な解決策(特にこれがより複雑になる場合)は、動的SQLを使用することです。SQLインジェクションを阻止するために、次の値をテストできます。

IF @sortDir NOT IN ('asc', 'desc')
  OR @sortOrder NOT IN ('name', 'created_date')
BEGIN
  RAISERROR('Invalid params', 11, 1);
  RETURN;
END

DECLARE @sql NVARCHAR(MAX) = N'SELECT column_list_please
  FROM dbo.Product ORDER BY ' + @sortOrder + ' ' + @sortDir;

EXEC sp_executesql @sql;

動的SQLのもう一つの利点は、それについてのすべての恐怖に満ちているにもかかわらず、最初に使用したソートバリエーションに最適化する単一のプランではなく、ソートバリエーションごとに最適なプランを取得できることです。また、私が実行した最近のパフォーマンス比較では、普遍的に最高のパフォーマンスを示しました。

http://sqlperformance.com/conditional-order-by

于 2013-03-25T18:07:07.617 に答える
20

複数のcaseステートメントを使用しますが、caseステートメントが必要です。

order by (case when @sortOrder = 'name' and @sortDir = 'asc' then name end)  asc,
         (case when @sortOrder = 'name' and @sortDir = 'desc' then name end) desc,
         (case when @sortOrder = 'created_date' and @sortDir = 'asc' then created_date end) asc,
         (case when @sortOrder = 'created_date' and @sortDir = 'desc' then created_date end) desc

4つの異なる句を使用すると、型間の変換の問題が解消されます。

于 2013-03-25T18:08:13.583 に答える
4

これを行うには複数の方法があります。1つの方法は次のとおりです。

SELECT *
FROM
(
  SELECT
  ROW_NUMBER() OVER ( ORDER BY
  CASE WHEN @sortOrder = 'name' and @sortDir = 'asc' then name
  END ASC,
  CASE WHEN @sortOrder = 'name' and @sortDir = 'desc' THEN name
  END DESC,
  CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'asc' THEN created_date
  END ASC,
  CASE WHEN i(@sortOrder = 'created_date' and @sortDir = 'desc' THEN created_date
  END ASC) RowNum
  *
)
order by 
RowNum

動的SQLを使用してそれを行うこともできます。

于 2013-03-25T18:10:37.850 に答える
1
declare @str varchar(max)
set @str = 'select * from Product order by ' + @sortOrder + ' ' + @sortDir
exec(@str)
于 2013-03-25T18:08:16.570 に答える