Jak zamawiasz według parametru?

16

Zastanawiam się, czy mógłbym poprosić o informacje zwrotne na temat procedury przechowywanej, którą uruchamiam i czy istnieje bardziej skuteczny sposób obsługi scenariusza (jestem pewien, że tak będzie!).

Zasadniczo mam pojedynczy SP, który wywołuję, aby zwrócić listę rekordów (zadań), które mogą mieć jeden lub więcej stanów i porządek sortowania (używam RowNum do stronicowania). W tej chwili używam Z RECOMPILE, ponieważ zmiany statusów mogą się zmieniać cały czas (w zależności od użytkownika itp.). Trwa również filtrowanie.

Używam instrukcji JEŻELI, aby zasadniczo uruchomić ten sam kawałek kodu, a jedyną zmianą jest kolejność sortowania.

Wydaje mi się, że moje pytania brzmią: czy istnieje lepszy sposób na zrobienie tego (może różne SP dla różnych statusów)? Czy nadmiernie komplikuję rzeczy z powodu braku wiedzy (całkiem prawdopodobne) Czy SP jest w porządku, ale wymaga drobnych poprawek w celu zmniejszenia liczby linii?

Wkleiłem poniżej część SP - jedyną różnicą w stosunku do pełnego kodu są dodatkowe instrukcje IF dla różnych porządków sortowania ...

Byłbym wdzięczny za wszelkie opinie.

Z góry dziękuję!

PROCEDURE [dbo].[sp_Jobs] 

@PageNumber int, 
@PageSize int, 
@FilterExpression varchar(500), 
@OrderBy varchar(50), 
@CustomerID int, 
@ShowNotSet bit, 
@ShowPlaced bit, 
@ShowProofed bit, 
@ShowReProofed bit, 
@ShowApproved bit, 
@ShowOnTime bit, 
@ShowLate bit, 
@ShowProblem bit, 
@ShowCompleted bit, 
@ShowDispatched bit, 
@ShowUnapproved bit, 
@ShowClosed bit, 
@ShowReturned bit, 
@UserID int

WITH RECOMPILE 

AS

--JobNumber DESC 
if @OrderBy='JobNumberDESC' 
BEGIN 

WITH Keys AS (SELECT TOP (@PageNumber * @PageSize) ROW_NUMBER() OVER (ORDER BY JobNumber DESC) as rn,P1.jobNumber,P1.CustID,P1.DateIn,P1.DateDue,P1.DateOut,p1.client,p1.MasterJobStatusID,p1.MasterJobStatusTimestamp,p1.OwnerID 

FROM 
vw_Jobs_List P1 WITH (NOLOCK) 

WHERE 
(@CustomerID = 0 OR CustID = @CustomerID) 
AND (@UserID = 0 OR OwnerID = @UserID) 
AND ((@ShowNotSet = 1 AND MasterJobStatusID=1) OR (@ShowPlaced = 1 AND MasterJobStatusID=2) OR (@ShowProofed = 1 AND MasterJobStatusID=3) OR (@ShowReProofed = 1 AND MasterJobStatusID=4) OR (@ShowApproved = 1 AND MasterJobStatusID=5) OR (@ShowOnTime = 1 AND MasterJobStatusID=6) OR (@ShowLate = 1 AND MasterJobStatusID=7) OR (@ShowProblem = 1 AND MasterJobStatusID=8) OR (@ShowCompleted = 1 AND MasterJobStatusID=9) OR (@ShowDispatched = 1 AND MasterJobStatusID=10) OR (@ShowUnapproved = 1 AND MasterJobStatusID=11) OR (@ShowClosed = 1 AND MasterJobStatusID=12) OR (@ShowReturned = 1 AND MasterJobStatusID=13)) AND (Search LIKE '%'+@FilterExpression+'%')

ORDER BY 
P1.JobNumber DESC ),SelectedKeys AS (
SELECT TOP (@PageSize)SK.rn,SK.JobNumber,SK.CustID,SK.DateIn,SK.DateDue,SK.DateOut 

FROM 
Keys SK 

WHERE 
SK.rn > ((@PageNumber-1) * @PageSize) 

ORDER BY 
SK.JobNumber DESC) 

SELECT SK.rn,J.JobNumber,J.OwnerID,J.Description,J.Client,SK.CustID,OrderNumber, CAST(DateAdd(d, -2, CAST(isnull(SK.DateIn,0) AS DateTime)) AS nvarchar) AS DateIn, CAST(DateAdd(d, -2, CAST(isnull(SK.DateDue,0) AS DateTime)) AS nvarchar) AS DateDue,CAST(DateAdd(d, -2, CAST(isnull(SK.DateOut,0) AS DateTime)) AS nvarchar) AS DateOut, Del_Method,Ticket#, InvoiceEmailed, InvoicePrinted, InvoiceExported, InvoiceComplete, JobStatus,j.MasterJobStatusID,j.MasterJobStatusTimestamp,js.MasterJobStatus 

FROM SelectedKeys SK JOIN vw_Jobs_List J WITH (NOLOCK) ON j.JobNumber=SK.JobNumber JOIN tbl_SYSTEM_MasterJobStatus js WITH (NOLOCK) ON j.MasterJobStatusID=js.MasterJobStatusID 

ORDER BY 
SK.JobNumber DESC 
END

--ELSE IF dla innych sortowań kolumn

VaticNZ
źródło

Odpowiedzi:

16

Sortowanie można rozwiązać za pomocą wyrażenia CASE, podobnie jak:

ORDER BY
    CASE WHEN @SortDirection = 'A' THEN
        CASE 
           WHEN @SortBy = 'JobNumber' THEN JobNumber
           WHEN @SortBy = 'JobId' THEN JobId 
        END
    END ASC
    , CASE WHEN @SortDirection = 'D' THEN
        CASE 
           WHEN @SortBy = 'JobNumber' THEN JobNumber
           WHEN @SortBy = 'JobId' THEN JobId 
        END
    END DESC

Możesz ponownie rozważyć OR, gdzie warunki mogą generować słabe plany. Jednym z najlepszych artykułów, które przeczytałem na ten temat (i alternatywne podejścia) jest Warunki dynamicznego wyszukiwania w T-SQL

Edycja: ponownie patrząc na listę parametrów, podstawowe filtry wyglądają jak @CustomerId i @UserId. Sugerowałbym utworzenie dwóch procs, spJobs_SelectByCustomerId i spJobs_SelectByUserId, które filtrują według ich odpowiednich parametrów, aby wyeliminować warunki „@Param = 0 lub Kolumna = @Param”. Wydaje mi się, że następnym ważnym parametrem jest @ShowCompleted (zakładając, że kiedy zadanie zostanie wykonane), nie jest pokazywane, chyba że @ ShowCompleted = 1), co rozważałbym uwzględnienie w indeksach CustomerId i UserId.

Edycja2: Zabawne, że te pytania czasami tykają w twoich myślach! :) Po zaindeksowaniu @ShowCompleted jest to jedna z okazji, w których użycie kolumny BIT o niskiej selektywności może być najlepszą strategią . Należy również wziąć pod uwagę przefiltrowane indeksy .

Mark Storey-Smith
źródło
Wooosh! Wszystko prosto nad moją głową, ale nie boję się czytać i uczyć! Dzięki Mark za poświęcenie czasu na odpowiedź. To naprawdę zabawne, jak podświadomość nadal działa przez te rzeczy. Pomagają mi też piwo i nikotyna :)
VaticNZ
Jeśli coś wymaga wyjaśnienia, możesz rozszerzyć swoje pytanie lub rozpocząć nowy post.
Mark Storey-Smith
1
Dzięki Mark. Wdrożyłem
VaticNZ
Mój zło, nie wyjaśniłem, że masz do czynienia z różnymi typami w osobnych wyrażeniach. Dodałem odpowiedź na twoje nowe pytanie.
Mark Storey-Smith
czy to rozwiązanie (na podstawie CASE) nie wygeneruje również złego planu wykonania? Czy nie będzie to CASEoceniane dla każdego wiersza?
Andrei Rînea