2010-11-11 27 views
22

Wie lautet die richtige MS SQL-Syntax zum Auswählen mehrerer ORDER BY-Spalten, wenn ORDER BY auf einer CASE-Anweisung basiert?Dynamische Reihenfolge von SELECT mit mehreren Spalten

Die unten arbeitet mit einzelnen Spalten in Ordnung, aber ich muss nach mehreren Spalten sortieren:

SELECT * FROM Products 
ORDER BY 
CASE WHEN @SortIndex = 1 THEN Price END ASC, 
CASE WHEN @SortIndex = 2 THEN Price DESC, Title ASC END <-- problem line 

Antwort

39

Sie könnten versuchen, diese

SELECT * FROM Products 
ORDER BY 
CASE WHEN @SortIndex = 1 THEN Price END ASC, 
CASE WHEN @SortIndex = 2 THEN Price END DESC, 
CASE WHEN @SortIndex = 2 THEN Title END ASC 
+0

Für mein besonderes Problem habe ich 'DANN -Preis END ASC,' statt 'DANN Preis END DESC', wie wir die Art zu laden, hatten Spalten aus derselben Spalte in der DB. Um es sauber zu machen, trennten wir jede Spalte durch ein Leerzeichen und verwendeten das negative Zeichen als Ersatz für 'DESC' – th3byrdm4n

1

dies tun ... und verabschieden, um Ihre Leistung . Leider ist die beste Lösung, dynamische SQL zu verwenden.

+0

Pflege um zu erarbeiten? Meine wirkliche Anwendung davon für eine ziemlich komplexe gespeicherte Prozedur, in der dynamisches SQL nicht möglich ist. – BradB

+0

+1. Siehe meine Antwort unter – Sadhir

+5

In meiner Erfahrung (nur 12 Jahre mit MSSQL arbeiten), habe ich mehrere Gelegenheiten gefunden, wo diese Konstrukte schreckliche Pläne produziert haben. Es ist nicht so, dass es per se schlecht ist, nur dass ich es als unzuverlässig empfunden habe. Abfrage, die funktioniert heute toll kann 4 Monate in der Zukunft abgrundtiefen Pläne produzieren) –

2

Versuchen Sie dies als eine geringere Auswirkung auf die Serverleistung - vorausgesetzt, Sie haben nur 2 Werte bei @SortIndex (1 und 2). Wenn nicht, erweitern Sie Ihre If mit mehr Bedingungen.

If @SortIndex = 1 
BEGIN 
    SELECT * FROM Products ORDER BY Price ASC 
END 
ELSE 
BEGIN 
    SELECT * FROM Products ORDER BY Price DESC, TITLE ASC 
END 
+0

Ich rieche Parameter hier :) .. Diese Lösung könnte tatsächlich Parameter Sniffing – Sadhir

+1

Angenommen, dass dies eine gespeicherte Prozedur und @SortIndex ist ein Parameter für die Sproc. Und für Argumente willen sagen wir, es gab eine andere Bedingung - wenn @SortIndex = 3, dann nur nach Tile sortieren, und sagen wir, es gab einen Index für den Preis und einen für den Titel. Wenn Sie jetzt zum ersten Mal den Sproc mit @SortIndex = 1 aufrufen, erstellt SQL Server einen Exec-Plan, der den Preisindex verwendet. Wenn Sie den Sproc erneut aufrufen, unabhängig vom Wert für @SortTitle, verwendet der SQL Server denselben Plan wie zuvor. Also, wenn @SortTitle = 3, wird es immer noch den Index auf Preis verwenden, obwohl es hier unbrauchbar ist – Sadhir

+0

Ich habe nie angenommen, dass dies eine gespeicherte Prozedur ist. Wenn es sich um eine einfache SQL-Anweisung mit Variablen deklariert und Wertzuordnung zu Variablen handelt, sollte die Abfrage einen guten Ausführungsplan haben, abhängig davon, welches "IF" -Feld es gehen soll.Der Benutzer hat nicht gesagt, dass es sich um eine gespeicherte Prozedur mit einem Parameter handelt. und wenn es wirklich ist - Parameter-Sniffing kann unter Berücksichtigung der Anzahl der für jede Kombination abgerufenen Zeilen aktiviert oder deaktiviert werden. – yrushka

6

@Brad. Pavel vorschlug, die folgenden (glaube ich),

DECLARE @query VARCHAR(MAX) 

SET @query = 'SELECT * FROM Products 
       ORDER BY 
       ' 

IF (@SortIndex = 1) 
    SET @query [email protected] + ' Price ASC ' 
ELSE IF (@SortIndex = 2) 
    SET @query [email protected] + ' Price DESC, Title ASC ' 

sp_executesql @query 

Warum denken Sie, dass die dynamische SQL nicht für komplexe gespeicherte Prozeduren geeignet ist? Dies sind genau die Stellen, an denen Sie dynamisches SQL verwenden sollten, da dies die Komplexität reduzieren und Probleme wie das Parameter-Sniffing lösen kann. Ich stimme zu, dass dynamische SQL hat seine Schattenseiten, aber ich würde empfehlen, dass Sie es zumindest versuchen, wenn es für Sie funktioniert.

+0

Danke für den Vorschlag. Ich werde diese Methode ausprobieren. – BradB

+1

Je komplexer die Abfrage (insbesondere die gefürchteten Listenprozeduren) mit mehreren Eingaben, mehreren Sortieroptionen usw. ist, desto zuverlässiger sind die Ergebnisse, die Sie aus dynamischem SQL erhalten. –

+0

Mein einziger Vorschlag hier ist, Ihre dynamischen Abfragen vor Injektionsangriffen zu schützen. – Keith

1

du umschreiben könnte:

ORDER BY 
CASE WHEN @SortIndex = 1 THEN Price END ASC, 
CASE WHEN @SortIndex = 2 THEN Price DESC, Title ASC END 

als

CASE WHEN @SortIndex = 1 THEN Price END ASC, 
CASE WHEN @SortIndex = 2 THEN Price DESC END, 
Title ASC 

Dies fügt die zusätzliche Sortierspalte auf alle Fälle aber in meiner Situation, das ist, was ich wollte.

Sie können dies auch (nur zum Beispiel) tun:

CASE WHEN @SortIndex = 1 THEN Price END ASC,Title ASC, 
CASE WHEN @SortIndex = 2 THEN Price DESC END 
+0

Dieses Szenario funktionierte gut für das, was ich brauchte. Vielen Dank! – tmorell

+0

Ich habe yrushkas Antwort gewählt. Es ist eine gültige Option hier. Aber in meinem Fall, wo ich das oben erwähnte verwendete, hatte der Benutzer 12 mögliche columbs, um zu sortieren, also schien dieser Stil/Option praktischer. –

+0

Und meine obigen Kommentar würde mehr Sinn machen, wenn ich fügte hinzu, dass die Abfrage selbst war ziemlich beteiligt. Es wäre in diesem Fall unpraktisch gewesen, die ganze Anweisung 12 Mal mit unterschiedlicher Reihenfolge durch Klauseln zu wiederholen. –