Ordre conditionnel T-SQL par

J'essaie d'écrire une procédure stockée qui renvoie une liste d'objets avec l'ordre de tri et la direction de tri sélectionnés par l'utilisateur et transmis en tant que paramètres sql.

Disons que j'ai une table de produits avec les colonnes suivantes: product_id (int), name (varchar), value (int), created_date(datetime) et paramètres @sortDir et @sortOrder

Je veux faire quelque chose comme

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

J'ai essayé de le faire avec des instructions case mais j'avais des problèmes puisque les types de données étaient différent. Quelqu'un a des suggestions?

32
demandé sur RiceRiceBaby 2013-03-25 22:00:42

4 réponses

CASE est une expression qui renvoie une valeur. Ce n'est pas pour le contrôle du flux, comme IF. Et vous ne pouvez pas utiliser IF dans une requête.

Malheureusement, il y a certaines limitations avec les expressions CASE qui rendent difficile de faire ce que vous voulez. Par exemple, toutes les branches d'une expression CASE doivent renvoyer le même type, OU être implicitement convertibles au même type. Je n'essaierais pas ça avec des cordes et des dates. Vous ne pouvez pas non plus utiliser CASE pour spécifier le tri direction.

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;

Une solution sans doute plus facile (surtout si cela devient plus complexe) consiste à utiliser dynamic SQL. Pour contrecarrer L'injection SQL, vous pouvez tester les valeurs:

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;

Un autre avantage pour le SQL dynamique, malgré toute la peur qui s'y répand: vous pouvez obtenir le meilleur plan pour chaque variation de tri, au lieu d'un seul plan qui optimisera la variation de tri que vous avez utilisée en premier. Il a également obtenu les meilleurs résultats universellement dans une comparaison de performance récente I ran:

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

55
répondu Aaron Bertrand 2013-03-26 11:01:58

Vous avez besoin d'une instruction case, bien que j'utiliserais plusieurs instructions 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

Avoir quatre clauses différentes élimine le problème de la conversion entre les types.

18
répondu Gordon Linoff 2013-03-25 18:08:13

Il y a plusieurs façons de le faire. Une façon serait:

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

Vous pouvez également le faire en utilisant dynamic sql.

2
répondu Pertinent Observer 2013-03-25 18:10:37
declare @str varchar(max)
set @str = 'select * from Product order by ' + @sortOrder + ' ' + @sortDir
exec(@str)
1
répondu ljh 2016-06-22 00:57:43