¿Cómo se ORDENA POR un parámetro?

16

Me pregunto si podría solicitar algún comentario sobre un procedimiento almacenado que estoy ejecutando y si hay una forma más eficiente de manejar el escenario (¡estoy bastante seguro de que lo habrá!).

Básicamente, tengo un único SP al que llamo para devolver una lista de registros (trabajos) que pueden tener uno o más estados y un orden de clasificación (estoy usando RowNum para paginación). En este momento estoy usando WITH RECOMPILE porque las variaciones en los estados pueden cambiar todo el tiempo (dependiendo del usuario, etc.). También hay algo de filtrado en curso.

Estoy usando una instrucción IF para ejecutar esencialmente el mismo bit de código con el único cambio que es el orden de clasificación.

Supongo que mis preguntas son: ¿Hay una mejor manera de hacerlo (tal vez diferentes SP para diferentes estados)? ¿Estoy complicando demasiado las cosas debido a la falta de conocimiento (muy probable) ¿El SP está realmente bien, pero requiere ajustes menores para reducir la cantidad de líneas?

He pegado una parte del SP a continuación: la única diferencia con el código completo son las declaraciones IF adicionales para los diferentes órdenes de clasificación ...

Agradecería cualquier comentario.

¡Gracias por adelantado!

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 para otra clasificación de columnas

VaticNZ
fuente

Respuestas:

16

La ordenación se puede resolver con una expresión CASE, algo similar a:

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

Es posible que desee reconsiderar las condiciones ORd donde es probable que generen malos planes. Uno de los mejores artículos que he leído sobre esto (y los enfoques alternativos) son las Condiciones de búsqueda dinámica en T-SQL

Editar: mirando nuevamente su lista de parámetros, los filtros principales parecen ser @CustomerId y @UserId. Sugeriría crear dos procs, spJobs_SelectByCustomerId y spJobs_SelectByUserId, que filtran por sus respectivos parámetros para eliminar las condiciones '@Param = 0 o Column = @Param'. Supongo que el siguiente parámetro importante es @ShowCompleted (suponiendo que una vez que un trabajo está 'hecho', no se muestra a menos que @ ShowCompleted = 1), lo que consideraría incluir en los índices de CustomerId y UserId.

Edit2: ¡Es curioso cómo estas preguntas a veces responden en el fondo de tu mente! :) Al indexar @ShowCompleted, esta es una de las ocasiones en que el uso de una columna BIT de baja selectividad puede ser la mejor estrategia . Los índices filtrados también deben considerarse.

Mark Storey-Smith
fuente
Wooosh! Todo sobre mi cabeza, ¡pero no tengo miedo de leer y aprender! Gracias Mark por tomarte el tiempo de responder. Es realmente divertido cómo el subconsciente continúa trabajando a través de estas cosas. También encuentro ayuda con la cerveza y la nicotina :)
VaticNZ
Si algo necesita aclaración, siéntase libre de extender su pregunta o comenzar una nueva publicación.
Mark Storey-Smith
1
Gracias Mark He implementado algunas de sus sugerencias y todo está bien, excepto un problema extraño ... He publicado en otro hilo: dba.stackexchange.com/questions/4162/…
VaticNZ
Mi mal, no explicó que tienes que lidiar con diferentes tipos en expresiones de casos separadas. He agregado una respuesta a su nueva pregunta.
Mark Storey-Smith
¿esta solución ( CASEbasada en) no generará un plan de ejecución deficiente también? ¿No CASEse evaluará esto para cada fila?
Andrei Rînea