2009-07-18 22 views
19

Piszę SP, który akceptuje jako kolumnę parametrów do sortowania i kierunku.Dynamiczny kierunek zamówienia

Nie chcę używać dynamicznego SQL.

Problem polega na ustawieniu parametru kierunku.

Jest to kod częściowy:

SET @OrderByColumn = 'AddedDate' 
SET @OrderDirection=1; 
. 
. 
. 
ORDER BY 
     CASE WHEN @OrderByColumn='AddedDate' THEN CONVERT(varchar(50),AddedDate)   
      WHEN @OrderByColumn='Visible' THEN CONVERT(varchar(2), Visible)  
      WHEN @OrderByColumn='AddedBy' THEN AddedBy  
      WHEN @OrderByColumn='Title' THEN Title  
     END 
+1

polecam zbieranie Patt konwersji ern dla AddDate, który upewnia się, że ciągi znaków będą sortować według daty. Wierzę, że domyślna konwersja może zmienić się wraz z ustawieniami lokalizacji. Lepiej jednoznacznie zdefiniować i być bezpiecznym. –

+0

Czy dotyczy to niektórych innych typów? Lub po prostu DATE? – markiz

+0

Numery również będą stanowić problem. Na przykład konwersja 10, 1 i 2 na varchar da ci rodzaj "1", "10", "2". Wyobrażam sobie, że konwertujesz Visible i AddDate na varchar, ponieważ CASE musi zwracać wszystkie tego samego typu, prawda? –

Odpowiedz

27

Można mieć dwa niemal identyczne ORDER BY elementów, jednym ASC i jeden DESC i przedłużyć swoje oświadczenie CASE, aby jeden lub drugi z nich zawsze równa pojedynczą wartość:

ORDER BY 
     CASE WHEN @OrderDirection=0 THEN 1 
     ELSE 
      CASE WHEN @OrderByColumn='AddedDate' THEN CONVERT(varchar(50),AddedDate)   
       WHEN @OrderByColumn='Visible' THEN CONVERT(varchar(2), Visible)  
       WHEN @OrderByColumn='AddedBy' THEN AddedBy   
       WHEN @OrderByColumn='Title' THEN Title 
      END 
     END ASC, 
     CASE WHEN @OrderDirection=1 THEN 1 
     ELSE 
      CASE WHEN @OrderByColumn='AddedDate' THEN CONVERT(varchar(50),AddedDate)   
       WHEN @OrderByColumn='Visible' THEN CONVERT(varchar(2), Visible)  
       WHEN @OrderByColumn='AddedBy' THEN AddedBy   
       WHEN @OrderByColumn='Title' THEN Title 
      END 
     END DESC 
+0

czy możesz odpowiedzieć na to samo pytanie, zapytałem Alexa Blacka w komentarzu? – markiz

+0

Po prostu wykonujesz ZAMÓWIENIE PRZEZ Kolumna1 ASC, Kolumna2 DESC (co oznacza kolejność według Kolumny 1 rosnąco, a następnie Kolumnie 2 malejąco), ale zamiast Kolumny1 i Kolumny2 masz wyrażenie dla każdego. –

+0

jeśli AddDate jest kolumną datetime, należy użyć _CONVERT (char (23), AddDate, 121) _ lub nie będzie poprawnie sortować –

3

Oto przykład:

CREATE PROCEDURE GetProducts 
( 
    @OrderBy  VARCHAR(50), 
    @Input2  VARCHAR(30) 
) 
AS 
BEGIN 
    SET NOCOUNT ON 

    SELECT Id, ProductName, Description, Price, Quantity 
    FROM Products 
    WHERE ProductName LIKE @Input2 
    ORDER BY 
     CASE    
      WHEN @OrderBy = 'ProductNameAsc' THEN ProductName 
     END ASC, 
     CASE 
      WHEN @OrderBy = 'ProductNameDesc' THEN ProductName 
     END DESC 

END 

stąd:

http://www.dominicpettifer.co.uk/Blog/21/dynamic-conditional-order-by-clause-in-sql-server-t-sql

rosnący i działania malejący trzeba do być zgrupowane w oddzielne instrukcje CASE , oddzielone przecinkiem. W kod po stronie serwera/script upewnić dołączyć ASC 'lub „opis produktu” na celu przez ciąg, czy można mieć dwa zapisanych parametrów wejściowych procedura nazwy kolumny i porządku przez kierunku jeśli chcesz .

+0

Czy możesz wyjaśnić tę składnię END ASC, (przecinek) CASE? Jeśli dane wejściowe to ProductNameDesc, czy nie wygeneruje: .. ORDER BY ASC, ProductName DESC? "asc", po końcu jest częścią oświadczenia, nieprawdaż? - markiz 0 sekund temu [usuń ten komentarz] – markiz

+0

Nie wiem, po prostu szukałem w Google tego artykułu. Odpowiedź Gary'ego poniżej wydaje się korzystać z tej samej techniki. –

+0

hej markiz, powinieneś * wypróbować * to. Zasadniczo twierdzisz, że nie wierzysz, że to, co sugeruje ten artykuł, zadziała, więc po prostu daj temu szansę, założę się, że będzie dobrze. –

10

Można uprość CASE, używając ROW_NUMBER, który sortuje dane i skutecznie przekształca je w poręczny format liczb całkowitych. Zwłaszcza, że ​​pytanie jest oznaczone SQL Server 2005

ten rozszerza również dość łatwo radzić sobie z różnego rodzaju drugo- i trzeciorzędowych

Użyłem mnożnik ponownie uprościć rzeczywistą select i zmniejszyć ryzyko w ocenie RBAR oRDER bY

DECLARE @multiplier int; 

SELECT @multiplier = CASE @Direction WHEN 1 THEN -1 ELSE 1 END; 

SELECT 
    Columns you actually want 
FROM 
    (
    SELECT 
     Columns you actually want, 
     ROW_NUMBER() OVER (ORDER BY AddedDate) AS AddedDateSort, 
     ROW_NUMBER() OVER (ORDER BY Visible) AS VisibleSort, 
     ROW_NUMBER() OVER (ORDER BY AddedBy) AS AddedBySort, 
     ROW_NUMBER() OVER (ORDER BY Title) AS TitleSort 
    FROM 
     myTable 
    WHERE 
     MyFilters... 
    ) foo 
ORDER BY 
    CASE @OrderByColumn 
     WHEN 'AddedDate' THEN AddedDateSort 
     WHEN 'Visible' THEN VisibleSort  
     WHEN 'AddedBy' THEN AddedBySort 
     WHEN 'Title' THEN TitleSort 
    END * @multiplier; 
+0

Czy zauważyłeś jakieś trafienie wydajności z robieniem wielu 'ROW_NUMBER()' 'Rozumiem, że dane te są różne –

+0

@ Scotty.NET, Martwiłem się o wydajność rzędu numer, po niektórych testach implementacja wiersza numer wygląda trochę lepiej niż wiele przypadków w porządku według klauzuli, z wyjątkiem sytuacji, gdy potrzebujesz fragmentu danych Rzeczywiście, najlepszy wynik, jaki udało mi się znaleźć, dotyczył tylko jednej liczby przypadków - [tutaj jest mały punkt z moimi pytaniami] (https://gist.github.com/thluiz/228c9e496322695e66213815aa80254e). –

1

działa to dobrze dla mnie - (gdzie, uporządkuj, kierunek przesunięcia pobrać)

parameters 

    @orderColumn int , 
    @orderDir varchar(20), 
    @start int , 
    @limit int 


    select * from items 
    WHERE  (items.status = 1) 
    order by 

    CASE WHEN @orderColumn = 0 AND @orderdir = 'desc' THEN items.[category] END DESC,  
    CASE WHEN @orderColumn = 0 AND @orderdir = 'asc' THEN items.[category] END ASC,  
    CASE WHEN @orderColumn = 1 AND @orderdir = 'desc' THEN items.[category] END DESC, 
    CASE WHEN @orderColumn = 1 AND @orderdir = 'asc' THEN items.[category] END ASC, 
    CASE WHEN @orderColumn = 2 AND @orderdir = 'desc' THEN items.[category] END DESC, 
    CASE WHEN @orderColumn = 2 AND @orderdir = 'asc' THEN items.[category] END ASC 

    OFFSET @start ROWS FETCH NEXT @limit ROWS ONLY