SQL Server : dynamic order by sort by asc - sql

I have a SQL query that I'm running with a conditional order by statement.
I'm trying to order by desc with nulls last, however my query returns the results not in asc order.
Here's an example:
DECLARE #Sort varchar(300);
SET #Sort = 'UngradedDescription asc'
SELECT
[UngradedDescription]
FROM
[dbo].[CardListing_Staging]
ORDER BY
CASE
WHEN #Sort = 'UngradedDescription asc'
THEN ((CASE WHEN UngradedDescription IS NULL THEN 1 ELSE 0 END))
END
These are the results I get:
How can I get them in asc order 02... 04... 06... nulls?

After some testing I found out it's multiple case statements
declare #Sort varchar(300);
set #Sort = 'UngradedDescription asc'
SELECT
[UngradedDescription]
FROM [dbo].[CardListing_Staging]
order by
CASE
WHEN #Sort = 'UngradedDescription asc' then ((CASE WHEN UngradedDescription IS NULL THEN 1 ELSE 0 END))
END,
CASE
WHEN #Sort = 'UngradedDescription asc' then UngradedDescription
END

Related

CASE WHEN in Order By taking too much time

I have very weird behavior. If my query have,
,ROW_NUMBER() OVER (ORDER BY CDF.Id) AS [ROW_Number]
Then it will take 1 to 2 seconds. If I have,
,ROW_NUMBER() OVER (ORDER BY CASE '' WHEN '' THEN CDF.Id END) AS [ROW_Number]
Then it will take 1 to 2 seconds. But If I have a variable with empty value,
DECLARE #SortExpression varchar(50)=''
,ROW_NUMBER() OVER (ORDER BY CASE #SortExpression WHEN '' THEN CDF.Id END) AS [ROW_Number]
Then it will take 12 to 16 seconds. In my real query I have some CASE statements CASE WHEN statements in ORDER BY clause. Here is my real query,
,ROW_NUMBER() OVER (
ORDER BY
CASE WHEN #SortExpression = 'MerchantName' THEN M.Name END ASC,
CASE WHEN #SortExpression = '-MerchantName' THEN M.Name END DESC,
CASE WHEN #SortExpression = 'Id' THEN CD.Id END ASC,
CASE WHEN #SortExpression = '-Id' THEN CD.Id END DESC,
CASE WHEN #SortExpression = 'MerchantProductId' THEN CD.MerchantProductId END ASC,
CASE WHEN #SortExpression = '-MerchantProductId' THEN CD.MerchantProductId END DESC,
CASE WHEN #SortExpression = 'Sku' THEN CD.Sku END ASC,
CASE WHEN #SortExpression = '-Sku' THEN CD.Sku END DESC,
CASE WHEN #SortExpression = 'ModelNumber' THEN CD.ModelNumber END ASC,
CASE WHEN #SortExpression = '-ModelNumber' THEN CD.ModelNumber END DESC,
CASE WHEN #SortExpression = 'Offer' THEN CD.Offer END ASC,
CASE WHEN #SortExpression = '-Offer' THEN CD.Offer END DESC,
CASE WHEN #SortExpression = 'Price' THEN CD.Price END ASC,
CASE WHEN #SortExpression = '-Price' THEN CD.Price END DESC,
CASE WHEN #SortExpression = 'NewPrice' THEN CD.NewPrice END ASC,
CASE WHEN #SortExpression = '-NewPrice' THEN CD.NewPrice END DESC,
CASE WHEN #SortExpression = 'InventoryControlType' THEN CD.InventoryControlType END ASC,
CASE WHEN #SortExpression = '-InventoryControlType' THEN CD.InventoryControlType END DESC,
CASE WHEN #SortExpression = 'Inventory' THEN CD.Inventory END ASC,
CASE WHEN #SortExpression = '-Inventory' THEN CD.Inventory END DESC,
CASE WHEN #SortExpression = 'Featured' THEN CD.Featured END ASC,
CASE WHEN #SortExpression = '-Featured' THEN CD.Featured END DESC,
CASE WHEN #SortExpression = 'Visible' THEN CD.Visible END ASC,
CASE WHEN #SortExpression = '-Visible' THEN CD.Visible END DESC,
CASE WHEN #SortExpression = 'Field1' THEN CD.Field1 END ASC,
CASE WHEN #SortExpression = '-Field1' THEN CD.Field1 END DESC,
CASE WHEN #SortExpression = 'Field2' THEN CD.Field2 END ASC,
CASE WHEN #SortExpression = '-Field2' THEN CD.Field2 END DESC,
CASE WHEN #SortExpression = 'Field3' THEN CD.Field3 END ASC,
CASE WHEN #SortExpression = '-Field3' THEN CD.Field3 END DESC,
CASE WHEN #SortExpression = 'Field4' THEN CD.Field4 END ASC,
CASE WHEN #SortExpression = '-Field4' THEN CD.Field4 END DESC,
CASE WHEN #SortExpression = 'OutletCode' THEN CD.OutletCode END ASC,
CASE WHEN #SortExpression = '-OutletCode' THEN CD.OutletCode END DESC,
CASE WHEN #SortExpression = 'Stock' THEN CD.Stock END ASC,
CASE WHEN #SortExpression = '-Stock' THEN CD.Stock END DESC,
CASE WHEN #SortExpression = 'Order' THEN CD.[Order] END ASC,
CASE WHEN #SortExpression = '-Order' THEN CD.[Order] END DESC,
CASE WHEN #SortExpression = 'ErrorDescription' THEN CD.[ErrorDescription] END ASC,
CASE WHEN #SortExpression = '-ErrorDescription' THEN CD.[ErrorDescription] END DESC,
CASE WHEN #SortExpression = 'CreationDateUtc' THEN CD.[CreationDateUtc] END ASC,
CASE WHEN #SortExpression = '-CreationDateUtc' THEN CD.[CreationDateUtc] END DESC,
CDF.Id, CD.[Order]
) AS [ROW_Number]
Just to address the first part, CASE '' WHEN '' THEN CDF.Id END will be optimised away at compile to just CDF.Id, so your first two queries are equivalent.
At compile time the optimiser knows you want to sort by CDF.Id so can generate a plan that utilizes an index on this.
Short Answer
Just add the OPTION (RECOMPILE) query hint, but this will only help if the column you are sorting on is indexed.
The full answer
The problem with your latter example is that the optimiser will create a plan based on an unknown value for #SortExpression, therefore cannot plan to use an appropriate index, since the sort column is unknown.
I created a simple test DDL:
IF OBJECT_ID(N'dbo.T', 'U') IS NOT NULL DROP TABLE dbo.T;
CREATE TABLE dbo.T (A INT, B INT, C INT);
INSERT dbo.T (A, B, C)
SELECT TOP 100000
A = ABS(CHECKSUM(NEWID())) % 1000,
B = ABS(CHECKSUM(NEWID())) % 1000,
C = ABS(CHECKSUM(NEWID())) % 1000
FROM sys.all_objects AS a
CROSS JOIN sys.all_objects AS b;
CREATE INDEX IX_T_A ON dbo.T (A);
CREATE INDEX IX_T_B ON dbo.T (B);
CREATE INDEX IX_T_C ON dbo.T (C);
As a control, I ran:
SELECT TOP 100 A, B, C,
D = ROW_NUMBER() OVER (ORDER BY A)
FROM dbo.T
ORDER BY D;
This gives a plan as such:
The key here is that the optimiser knows that rather than sort the whole table, it only needs the first 100 rows from the index IX_T_A, which is very cheap compared to sorting the table, since the index is already sorted.
This is our optimal plan for a sort on an indexed column. So the aim of the game is to get to this plan while using a variable to define the sort. Just to explain further, I have used TOP because it is representative of what (I assume) you are trying to achieve which is to filter for a certain set of records for paging:
SELECT *
FROM ( SELECT A, B, C,
D = ROW_NUMBER() OVER (ORDER BY A)
FROM dbo.T
) T
WHERE D BETWEEN 150 AND 250;
This gives exactly the same plan, it just means internally the index seek starts further into the index. For the rest of the tests I will continue with TOP as it is shorter.
As explained above, if I run this with the variable, the query plan cannot use the index scan on IX_T_A since it does not know for sure that A will be the sort column, so it just uses a plain old table scan, and has to sort the entire table, rather than being able to just sequentially read from a nonclustered index:
DECLARE #Sort VARCHAR(10) = 'A';
SELECT TOP 100 A, B, C,
D = ROW_NUMBER() OVER (ORDER BY
CASE WHEN #Sort = 'A' THEN A END ASC,
CASE WHEN #Sort = '-A' THEN A END DESC,
CASE WHEN #Sort = 'B' THEN B END ASC,
CASE WHEN #Sort = '-B' THEN B END DESC,
CASE WHEN #Sort = 'C' THEN C END ASC,
CASE WHEN #Sort = '-C' THEN C END DESC)
FROM dbo.T
ORDER BY D;
Query Plan:
The only way around this that I can see is to force recompilation at run-time, so that the redundant sorts can be optimised away, and the correct index used:
DECLARE #Sort VARCHAR(10) = 'A';
SELECT TOP 100 A, B, C,
D = ROW_NUMBER() OVER (ORDER BY
CASE WHEN #Sort = 'A' THEN A END ASC,
CASE WHEN #Sort = '-A' THEN A END DESC,
CASE WHEN #Sort = 'B' THEN B END ASC,
CASE WHEN #Sort = '-B' THEN B END DESC,
CASE WHEN #Sort = 'C' THEN C END ASC,
CASE WHEN #Sort = '-C' THEN C END DESC)
FROM dbo.T
ORDER BY D
OPTION (RECOMPILE);
Query Plan:
As you can see, this has reverted to the plan when the sort column was hard coded. You will have an additional cost at compile time, but this should be less than the additional 10s+ you are seeing in run time.
If you sort on a column without an index then it does not matter whether you recompile or not, it will use the same plan.
The only option that I could think of would require indexes on all the columns. I'm not sure if this is really feasible, but you had all such indexes, then the following might perform well:
(case when #SortExpression = 'MerchantName'
then row_number() over (order by MerchantName)
when #SortExpression = '-MerchantName'
then row_number() over (order by MerchantName desc)
. . .
end)
SQL Server is smart enough to use indexes for row_number(), when possible. And, I'm pretty sure index usage is at the root of the performance difference. It should be smart enough to use indexes even when row_number() is an expression in a case statement.
I will strongly recommend to use dynamic query here. For example:
declare #mainStatement varchar(1000) = 'select * from sometable'
declare #orderingStatement varchar(1000) = ' order by '
if #SortExpression = 'MerchantName'
set #orderingStatement = 'M.Name ASC'
else if #SortExpression = '-MerchantName'
set #orderingStatement = 'M.Name DESC'
else if
......
set #mainStatement = #mainStatement + #orderingStatement
exec(#mainStatement)
This way you will end up with query such us:
select * from sometable order by M.Name ASC
or
select * from sometable order by M.Name DESC
You will do your best and optimize query as much as possible. The rest is the job of DBA. He will add some missing indexes on table`s columns and voilĂ .

Optimizing this 40+ second select query on MSSQL 2012?

I'm no DBA and I'm out of ideas on optimizing this query. It's taking roughly 40+ seconds to run. Any glaring newbie mistakes where I could optimize?
USE [deskcal2014]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROC [dbo].[proc_AdminRegisteredCards]
(
#Take AS INT,
#Skip AS INT,
#FilterColumn AS NVARCHAR(max),
#FilterOrder AS NVARCHAR(max))
AS
BEGIN
SELECT TOP(#Take)
ISNULL( ROW_NUMBER() OVER(ORDER BY ta.CreatedOn, ta.ItemId), -1000) AS AdminRegisteredCardsId,
ta.ItemId,
ta.CardNumber,
ta.FirstName,
ta.LastName,
ta.Birthday,
ta.PostalCode,
ta.[Description],
ta.CardActivated,
ta.ContactInfo,
ta.PhoneNumber,
ta.ReceiveCalendarReminders,
ta.ReceiveGeneralMails,
ta.ReceivePrefStoreMails,
ta.CardStatus,
ta.SamoaCardId,
ta.CalendarUserId,
ta.LiveOpsRegistrantId,
ta.UseType,
ta.CreatedOn,
ta.ModifiedBy,
ta.ModifiedOn from (
SELECT CalendarUser.CalendarUserId as ItemId,
SamoaCard.CardNumber,
SamoaCard.FirstName,
SamoaCard.LastName,
CalendarUser.Birthday,
CalendarUser.PostalCode,
RegisterSourceType.[Description],
CalendarUserCard.CardActivated,
CalendarUser.EmailAddress as ContactInfo,
CalendarUser.PhoneNumber,
CalendarUser.ReceiveCalendarReminders,
CalendarUser.ReceiveGeneralMails,
CalendarUser.ReceivePrefStoreMails,
CASE WHEN CalendarUserCard.CardDeactivated IS NOT NULL THEN 'Deactivated' ELSE 'Activated' END AS CardStatus,
SamoaCard.SamoaCardId,
CalendarUser.CalendarUserId,
null as LiveOpsRegistrantId,
SamoaCard.CreatedOn,
'C' as UseType,
CalendarUser.ModifiedBy,
CalendarUser.ModifiedOn
FROM (
(dbo.CalendarUser CalendarUser
INNER JOIN dbo.RegisterSourceType RegisterSourceType ON (CalendarUser.RegisterType = RegisterSourceType.RegisterType))
INNER JOIN dbo.CalendarUserCard CalendarUserCard ON (CalendarUserCard.CalendarUserId = CalendarUser.CalendarUserId)
)
INNER JOIN dbo.SamoaCard SamoaCard ON (CalendarUserCard.SamoaCardId = SamoaCard.SamoaCardId)
ORDER BY
case when #FilterColumn = 'FirstName' and #FilterOrder = 'ASC'
then CalendarUser.Firstname end asc,
case when #FilterColumn = 'FirstName' and #FilterOrder = 'DESC'
then CalendarUser.Firstname end desc,
case when #FilterColumn = 'LastName' and #FilterOrder = 'ASC'
then CalendarUser.Lastname end asc,
case when #FilterColumn = 'LastName' and #FilterOrder = 'DESC'
then CalendarUser.Lastname end desc,
case when #FilterColumn = 'CardNumber' and #FilterOrder = 'ASC'
then CalendarUser.CardNumber end asc,
case when #FilterColumn = 'CardNumber' and #FilterOrder = 'DESC'
then CalendarUser.CardNumber end desc,
case when #FilterColumn = 'Birthday' and #FilterOrder = 'ASC'
then CalendarUser.Birthday end asc,
case when #FilterColumn = 'Birthday' and #FilterOrder = 'DESC'
then CalendarUser.Birthday end desc,
case when #FilterColumn = 'Description' and #FilterOrder = 'ASC'
then RegisterSourceType.[Description] end asc,
case when #FilterColumn = 'Description' and #FilterOrder = 'DESC'
then RegisterSourceType.[Description] end desc,
case when #FilterColumn = 'ContactInfo' and #FilterOrder = 'ASC'
then CalendarUser.EmailAddress end asc,
case when #FilterColumn = 'ContactInfo' and #FilterOrder = 'DESC'
then CalendarUser.EmailAddress end desc,
case when #FilterColumn = 'CardActivated' and #FilterOrder = 'ASC'
then CalendarUserCard.CardActivated end asc,
case when #FilterColumn = 'CardActivated' and #FilterOrder = 'DESC'
then CalendarUserCard.CardActivated end desc,
case when #FilterColumn = 'PostalCode' and #FilterOrder = 'ASC'
then CalendarUser.PostalCode end asc,
case when #FilterColumn = 'PostalCode' and #FilterOrder = 'DESC'
then CalendarUser.PostalCode end desc
OFFSET #Skip ROWS -- skip N rows
FETCH NEXT #Take ROWS ONLY
union all
SELECT TOP(10)
LiveOpsRegistrant.LiveOpsRegistrantId as ItemId,
LiveOpsRegistrant.CardNumber,
'Registered' as FirstName,
'Card' as LastName,
LiveOpsRegistrant.Birthday,
null as PostalCode,
'LiveOps' as Description,
LiveOpsRegistrant.CreatedOn as CardActivated,
LiveOpsRegistrant.PhoneNumber as ContactInfo,
LiveOpsRegistrant.PhoneNumber,
CONVERT(bit,0) as ReceiveCalendarReminders,
CONVERT(bit,0) as ReceiveGeneralMails,
CONVERT(bit,0) as ReceivePrefStoreMails,
'Activated' AS CardStatus,
SamoaCard.SamoaCardId,
null as CalendarUserId,
LiveOpsRegistrant.LiveOpsRegistrantId,
SamoaCard.CreatedOn,
'L' as UseType,
SamoaCard.ModifiedBy,
SamoaCard.ModifiedOn
FROM dbo.LiveOpsRegistrant LiveOpsRegistrant
INNER JOIN dbo.SamoaCard SamoaCard ON (LiveOpsRegistrant.CardNumber = SamoaCard.CardNumber)) ta
END
GO
Echoing some of the comments already given: Having a bunch of logic in an ORDER BY clause usually doesn't work well. ROW_NUMBER() can be nasty when used in a query with many joins and other complexities as in your case.
Temp tables are probably your first best option here. Reading your code, I think the first one is for CalendarUser.CalendarUserId, and you'll want to populate it with a bunch of nested if ... else if statements:
if #FilterColumn = 'FirstName' and #FilterOrder = 'ASC'
begin
insert into #CalendarUser
select top(#Take) CalendarUserId
order by Firstname asc
offset #Skip rows
fetch next #Take rows only
end
else
begin
if .....
Populate a second temp table #DataOut with all the fields you want to output, using an inner join on #CalendarUser to filter the result set. Exclude the field you're calculating with ROW_NUMBER(). Leave the UNION ALL out of this query, append the data from that into #DataOut table as a separate step.
The final output query will be
select
ISNULL( ROW_NUMBER() OVER(ORDER BY CreatedOn, ItemId), -1000)
AS AdminRegisteredCardsId,
#DataOut.*
from #DataOut
Not pleasant to write, feels brute force, but I'm fairly sure you'll see a dramatic performance improvement.
I've been trying to optimize a similar query and came to the conclusion that ordering and filtering across joins has a big impact on performance - my advice would be to denormalize everything that you sort by or filter on using an indexed view and seeing what impact this has on the performance.

T-SQL Conditional Order By

I am trying to write a stored procedure that returns a list of object with the sort order and sort direction selected by the user and passed in as sql parameters.
Lets say I have a table of products with the following columns: product_id(int), name(varchar), value(int), created_date(datetime)
and parameters #sortDir and #sortOrder
I want to do something like
select *
from Product
if (#sortOrder = 'name' and #sortDir = 'asc')
then order by name asc
if (#sortOrder = 'created_date' and #sortDir = 'asc')
then order by created_date asc
if (#sortOrder = 'name' and #sortDir = 'desc')
then order by name desc
if (#sortOrder = 'created_date' and #sortDir = 'desc')
then order by created_date desc
I tried do it with case statements but was having problems since the data types were different. Anyone got any suggestions?
CASE is an expression that returns a value. It is not for control-of-flow, like IF. And you can't use IF within a query.
Unfortunately, there are some limitations with CASE expressions that make it cumbersome to do what you want. For example, all of the branches in a CASE expression must return the same type, or be implicitly convertible to the same type. I wouldn't try that with strings and dates. You also can't use CASE to specify sort direction.
SELECT column_list_please
FROM dbo.Product -- dbo prefix please
ORDER BY
CASE WHEN #sortDir = 'asc' AND #sortOrder = 'name' THEN name END,
CASE WHEN #sortDir = 'asc' AND #sortOrder = 'created_date' THEN created_date END,
CASE WHEN #sortDir = 'desc' AND #sortOrder = 'name' THEN name END DESC,
CASE WHEN #sortDir = 'desc' AND #sortOrder = 'created_date' THEN created_date END DESC;
An arguably easier solution (especially if this gets more complex) is to use dynamic SQL. To thwart SQL injection you can test the values:
IF #sortDir NOT IN ('asc', 'desc')
OR #sortOrder NOT IN ('name', 'created_date')
BEGIN
RAISERROR('Invalid params', 11, 1);
RETURN;
END
DECLARE #sql NVARCHAR(MAX) = N'SELECT column_list_please
FROM dbo.Product ORDER BY ' + #sortOrder + ' ' + #sortDir;
EXEC sp_executesql #sql;
Another plus for dynamic SQL, in spite of all the fear-mongering that is spread about it: you can get the best plan for each sort variation, instead of one single plan that will optimize to whatever sort variation you happened to use first. It also performed best universally in a recent performance comparison I ran:
http://sqlperformance.com/conditional-order-by
You need a case statement, although I would use multiple case statements:
order by (case when #sortOrder = 'name' and #sortDir = 'asc' then name end) asc,
(case when #sortOrder = 'name' and #sortDir = 'desc' then name end) desc,
(case when #sortOrder = 'created_date' and #sortDir = 'asc' then created_date end) asc,
(case when #sortOrder = 'created_date' and #sortDir = 'desc' then created_date end) desc
Having four different clauses eliminates the problem of converting between types.
There are multiple ways of doing this. One way would be:
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER ( ORDER BY
CASE WHEN #sortOrder = 'name' and #sortDir = 'asc' then name
END ASC,
CASE WHEN #sortOrder = 'name' and #sortDir = 'desc' THEN name
END DESC,
CASE WHEN i(#sortOrder = 'created_date' and #sortDir = 'asc' THEN created_date
END ASC,
CASE WHEN i(#sortOrder = 'created_date' and #sortDir = 'desc' THEN created_date
END ASC) RowNum
*
)
order by
RowNum
You can also do it using dynamic sql.
declare #str varchar(max)
set #str = 'select * from Product order by ' + #sortOrder + ' ' + #sortDir
exec(#str)

ORDER BY with a CASE statement for column with alias

I need a stored procedure which will allow me to return sorted results based on two input parameters: #sortColumnName and #sortDirection. I wrote the following stored procedure, but when I run it, I am getting this error: "Invalid column name 'LastPayCheckDate'."
SELECT Name, SUM(Pay), MAX(PayCheckDate) as LastPayCheckDate
FROM Employee
GROUP BY Name
ORDER BY
CASE WHEN #sortColumnName = 'LastPayCheckDate' AND #sortDirection = 'ASC'
THEN [LastPayCheckDate] END ASC,
CASE WHEN #sortColumnName = 'LastPayCheckDate' AND #sortDirection = 'DESC'
THEN [LastPayCheckDate] END DESC
What is going on? I suppose that t-sql runs the case statement before the select... Am I right? How can I work around this issue?
Thanks for the help!
Try this
ORDER BY
CASE WHEN #sortColumnName = 'LastPayCheckDate' AND #sortDirection = 'ASC'
THEN MAX(PayCheckDate) END ASC,
CASE WHEN #sortColumnName = 'LastPayCheckDate' AND #sortDirection = 'DESC'
THEN MAX(PayCheckDate) END DESC
Example
create table Test (id int, somevalue int)
insert Test values(1,1)
insert Test values(2,1)
insert Test values(3,2)
insert Test values(3,2)
insert Test values(4,2)
run this in 1 shot
declare #sortDirection char(4)
select #sortDirection = 'DESC'
select somevalue, COUNT(*)
from Test
group by somevalue
order by case when #sortDirection = 'ASC'
then COUNT(*) end asc,
case when #sortDirection = 'DESC'
then COUNT(*) end desc
select #sortDirection = 'ASC'
select somevalue, COUNT(*)
from Test
group by somevalue
order by case when #sortDirection = 'ASC'
then COUNT(*) end asc,
case when #sortDirection = 'DESC'
then COUNT(*) end desc
You need to either use the function again or use a subquery if you want to be able to refer to the column alias.
Also, I think that you need to make sure that all of your columns in the case statement get converted to the same data type.

Can I store SQL Server sort order in a variable?

I have the following SQL within a stored procedure. Is there a way to remove the IF statement and pass the 'ASC'/'DESC' option as a variable?
I know I could do the query a number of different ways, or return a table and sort it externally etc. I would just like to know if I can avoid duplicating the CASE statement.
IF #sortOrder = 'Desc'
BEGIN
SELECT * FROM #t_results
ORDER BY
CASE WHEN #OrderBy = 'surname' THEN surname END DESC,
CASE WHEN #OrderBy = 'forename' THEN forename END DESC,
CASE WHEN #OrderBy = 'fullName' THEN fullName END DESC,
CASE WHEN #OrderBy = 'userId' THEN userId END DESC,
CASE WHEN #OrderBy = 'MobileNumber' THEN MSISDN END DESC,
CASE WHEN #OrderBy = 'DeviceStatus' THEN DeviceStatus END DESC,
CASE WHEN #OrderBy = 'LastPosition' THEN LastPosition END DESC,
CASE WHEN #OrderBy = 'LastAlert' THEN LastAlert END DESC,
CASE WHEN #OrderBy = 'LastCommunication' THEN LastCommunication END DESC,
CASE WHEN #OrderBy = 'LastPreAlert' THEN LastPreAlert END DESC
END
ELSE
BEGIN
SELECT * FROM #t_results
ORDER BY
CASE WHEN #OrderBy = 'surname' THEN surname END DESC,
CASE WHEN #OrderBy = 'forename' THEN forename END DESC,
CASE WHEN #OrderBy = 'fullName' THEN fullName END DESC,
CASE WHEN #OrderBy = 'userId' THEN userId END DESC,
CASE WHEN #OrderBy = 'MobileNumber' THEN MSISDN END DESC,
CASE WHEN #OrderBy = 'DeviceStatus' THEN DeviceStatus END DESC,
CASE WHEN #OrderBy = 'LastPosition' THEN LastPosition END DESC,
CASE WHEN #OrderBy = 'LastAlert' THEN LastAlert END DESC,
CASE WHEN #OrderBy = 'LastCommunication' THEN LastCommunication END DESC,
CASE WHEN #OrderBy = 'LastPreAlert' THEN LastPreAlert END DESC
END
END
Pass in #OrderBy int, where positive is ASC, negative is DESC, and the actual number is the column to sort by:
SELECT
dt.yourColumn1
,dt.yourColumn2
,dt.yourColumn3
,CASE
WHEN #OrderBy>0 THEN dt.SortBy
ELSE NULL
END AS SortByAsc
,CASE
WHEN #OrderBy<0 THEN dt.SortBy
ELSE NULL
END AS SortByDesc
FROM (SELECT
yourColumn1
,yourColumn2
,yourColumn3
,CASE
WHEN ABS(#OrderBy) = 1 THEN surname
WHEN ABS(#OrderBy) = 2 THEN forename
WHEN ABS(#OrderBy) = 3 THEN fullName
WHEN ABS(#OrderBy) = 4 THEN CONVERT(varchar(10),userId)
WHEN ABS(#OrderBy) = 5 THEN CONVERT(varchar(10),MobileNumber
WHEN ABS(#OrderBy) = 6 THEN DeviceStatus
WHEN ABS(#OrderBy) = 7 THEN LastPosition
WHEN ABS(#OrderBy) = 8 THEN CONVERT(varchar(23),LastAlert,121)
WHEN ABS(#OrderBy) = 9 THEN CONVERT(varchar(23),LastCommunication,121)
WHEN ABS(#OrderBy) =10 THEN CONVERT(varchar(23),LastPreAlert,121)
ELSE NULL
END AS SortBy
FROM YourTablesHere
WHERE X=Y
) dt
ORDER BY SortByAsc ASC, SortByDesc DESC
Just make sure you build a string that sort properly. Notice I used 'YYYY-MM-DD hh:mm:ss.mmm' for the dates and put the numbers into strings. We usually put multiple columns together, so if you sort by surname, forename is used too, etc. Watch out, if you do combine multiple columns you'll need to pad with zeros or spaces.
If you don't want the SortByAsc and SortByDesc columns to be in the result set, wrap the entire thing in a derived table.
You can do it without dynamic SQL...
SELECT
*
FROM
My_Table
WHERE
Whatever = #something
ORDER BY
CASE #sort_order
WHEN 'ASC' THEN
CASE #order_by
WHEN 'surname' THEN surname
WHEN 'forename' THEN forename
WHEN 'fullname' THEN fullname
ELSE surname
END
ELSE '1'
END ASC,
CASE #sort_order
WHEN 'DESC' THEN
CASE #order_by
WHEN 'surname' THEN surname
WHEN 'forename' THEN forename
WHEN 'fullname' THEN fullname
ELSE surname
END
ELSE '1'
END DESC
Yes, but you have to use dynamic queries.
Take a look here.
How about this:
CASE WHEN #order_by = #order_by THEN #order_by END
Then there is no reason to have multiple cases.