Como você ordena por um parâmetro?

16

Apenas imaginando se eu poderia solicitar algum feedback sobre um procedimento armazenado que estou executando e se há uma maneira mais eficiente de lidar com o cenário (tenho certeza de que haverá!).

Basicamente, tenho um único SP que chamo para retornar uma lista de registros (trabalhos) que podem ter um ou mais status e uma ordem de classificação (estou usando o RowNum para paginação). No momento estou usando WITH RECOMPILE porque as variações nos status podem mudar o tempo todo (dependendo do usuário, etc.). Há também algumas filtragem em andamento.

Estou usando uma instrução IF para executar essencialmente o mesmo bit de código, com a única alteração sendo a ordem de classificação.

Acho que minhas perguntas são: Existe uma maneira melhor de fazer isso (talvez SP diferentes para status diferentes)? Estou complicando demais as coisas devido à falta de conhecimento (provavelmente) O SP está realmente bom, mas requer pequenos ajustes para reduzir o número de linhas?

Colei uma parte do SP abaixo - a única diferença no código completo são as instruções IF adicionais para as diferentes ordens de classificação ...

Eu apreciaria qualquer feedback.

Desde já, obrigado!

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 SE para outra classificação de coluna

VaticNZ
fonte

Respostas:

16

A classificação pode ser resolvida com uma expressão CASE, algo como:

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

Você pode reconsiderar as condições de operação OR, pois elas provavelmente geram planos ruins. Um dos melhores artigos que li sobre isso (e as abordagens alternativas) é Condições dinâmicas de pesquisa no T-SQL

Editar: olhando novamente para sua lista de parâmetros, os filtros principais parecem ser @CustomerId e @UserId. Eu sugeriria a criação de dois procs, spJobs_SelectByCustomerId e spJobs_SelectByUserId, que filtram por seus respectivos parâmetros para eliminar as condições '@Param = 0 ou Column = @Param'. Eu acho que o próximo parâmetro importante é @ShowCompleted (presumindo que uma vez que um trabalho seja 'concluído', ele não seja exibido, a menos que @ ShowCompleted = 1), que eu consideraria incluir nos índices CustomerId e UserId.

Edit2: Engraçado como essas perguntas às vezes surgem no fundo da sua mente! :) Na indexação @ShowCompleted, esta é uma das ocasiões em que Usar uma coluna BIT de baixa seletividade primeiro pode ser a melhor estratégia . Índices filtrados também devem ser considerados.

Mark Storey-Smith
fonte
Wooosh! Tudo sobre minha cabeça, mas não tenho medo de ler e aprender! Obrigado, Mark, por reservar um tempo para responder. É realmente engraçado como o subconsciente continua a trabalhar com essas coisas. Acho cerveja e ajuda a nicotina também :)
VaticNZ
Se algo precisar de esclarecimentos, fique à vontade para estender sua pergunta ou iniciar uma nova postagem.
Mark-Storey-Smith
11
Obrigado Mark. Eu tenho implementado algumas das suas sugestões e tudo é bom, exceto um problema estranho ... Eu postei em outro segmento: dba.stackexchange.com/questions/4162/...
VaticNZ
Meu mal, não explicou que você tem que lidar com tipos diferentes em expressões de caso separadas. Adicione uma resposta à sua nova pergunta.
Mark-Storey-Smith
Essa solução ( CASEbaseada em) também não gerará um plano de execução ruim? Isso não CASEserá avaliado para cada linha?
Andrei Rînea 04/04