ORDER BY with a CASE statement for column with alias - sql

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.

Related

SQL multiple case statement

How can I make this correct? I am getting an error saying:
Incorrect syntax near the keyword 'DESC'.
SELECT *
FROM Companies
Order By
CASE #OrderByField
WHEN 'CompanyName' THEN CompanyName
WHEN 'CreatedDate' THEN CreatedDate
END,
CASE #Direction
WHEN 'DESC' THEN DESC
WHEN 'ASC' THEN ASC
END
Can I not have two case statements? If not, how can i pass in the name of the order by field and direction as parameters?
Thanks!
Another problem surface after the first one is solved...
If I include a field that doesn't have the datatype of string, if throws an error.
For example:
SELECT *
FROM Companies
Order By
CASE #Direction WHEN 'DESC' THEN
CASE #OrderByField
WHEN 'CompanyName' THEN CompanyName
WHEN 'CreatedDate' THEN CreatedDate
WHEN 'Score' THEN Score
END
END DESC,
CASE #Direction WHEN 'ASC' THEN
CASE #OrderByField
WHEN 'CompanyName' THEN CompanyName
WHEN 'CreatedDate' THEN CreatedDate
WHEN 'Score' THEN Score
END
END ASC
#OrderByField is type of nvarchar(50)
assume Score has a datatype of float.
Above throws an error like the one below even if i am not trying to order by the score field. Error converting data type nvarchar to float.
Similarly, including a createddate throws an error: Conversion failed when converting date and/or time from character string.
Will be very appreciated if anyone can help out.
You can't return a keyword from a case statement.
But you can achieve what you want by ordering in two ways, but return a constant expression for the order you don't want:
SELECT *
FROM Companies
Order By
CASE #Direction WHEN 'DESC' THEN
CASE #OrderByField
WHEN 'CompanyName' THEN CompanyName
WHEN 'CreatedDate' THEN CreatedDate
END
END DESC,
CASE #Direction WHEN 'ASC' THEN
CASE #OrderByField
WHEN 'CompanyName' THEN CompanyName
WHEN 'CreatedDate' THEN CreatedDate
END
END ASC
Non-matching cases will return null and so will be ignored for ordering purposes.
As ASC and DESC are keywords, you can't have them as return value from a CASE.
You can make two cases, one for ASC and one for DESC:
SELECT *
FROM Companies
Order By
CASE
WHEN #OrderByField = 'CompanyName' AND #Direction = 'ASC' THEN CompanyName
WHEN #OrderByField = 'CreatedDate' AND #Direction = 'ASC' THEN CreatedDate
END ASC,
CASE
WHEN #OrderByField = 'CompanyName' AND #Direction = 'DESC' THEN CompanyName
WHEN #OrderByField = 'CreatedDate' AND #Direction = 'DESC' THEN CreatedDate
END DESC
Note that all values from a case has to have the same data type, so you might need one pair of cases for string and one pair for dates.

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)

Passing dynamic order by in stored procedure

I am creating below stored procedure.
declare #PageNum as Int
declare #PerPageResult as Int
declare #StartDate as varchar(25)
declare #EndDate as varchar(25)
declare #SortType as Varchar(50)
declare #SortDirection as Varchar(4)
set #PageNum=1
set #PerPageResult=20
set #StartDate='2008-02-08'
set #EndDate='2015-02-08'
set #SortType='RegDate'
set #SortDirection='Desc'
declare #Temp Table(RowNum int, RegDate Date, Registered int, Female int, Male int, [Join] int, Rebill int, TotalPointsEarned int, Expire int)
declare #sort varchar(50)
Insert into #Temp
Select ROW_NUMBER() over (order by #SortType+' '+#SortDirection) As RowNum, * From (
SELECT
CAST(m.registrationdate AS Date) as RegDate,
count(m.id) Registered,
count(CASE WHEN m.gender='F' then 'F' end) As Female,
count(CASE WHEN m.gender='M' then 'M' end) As Male
count(CASE WHEN p.paymenttransactiontype='N' then 'N' end) As [Join],
count(CASE WHEN p.paymenttransactiontype='R' then 'R' end) As Rebill,
count(m.tokensearned) As TotalPointsEarned,
count(CASE WHEN p.paymenttransactiontype='E' then 'E' end) As Expire
from member m
join payment p on m.id=p.id_member
join user_role u on u.member_id=m.id
where u.role_id <> 3
and CAST(m.registrationdate AS Date) > #StartDate and CAST(m.registrationdate AS Date) < #EndDate
GROUP BY CAST(m.registrationdate AS Date)
) as aa
Select * from #Temp Where RowNum>((#PageNum-1)*#PerPageResult) and RowNum<=#PerPageResult * #PageNum
Order by #SortType+' '+#SortDirection
In above when i pass the Order by clause dynamically, its not sorting the data properly but when i write column name explicitly, it works fine. Might be its taking #SortType+' '+#SortDirection as varchar rather than Date
I tried writing Order by case when (#Sort='RegDate' and #SortDirection='Desc') Then RegDate End Desc, but it didn't work
How can i pass order by dynamically here.
Edit:
#Andomar: I tried your provided solution and added one more field for Date type. And it didn't work too.
below is what i did.
create table t1 (id int, name varchar(50), dt date);
insert t1 values
(1, 'Chihiro Ogino','2009-02-08'),
(2, 'Spirit of the Kohaku River','2008-02-08'),
(3, 'Yubaba','2012-02-08');
declare #sortColumn varchar(50) = 'dt'
declare #sortOrder varchar(50) = 'ASC'
select *
from t1
order by
case
when #sortOrder <> 'ASC' then 0
when #sortColumn = 'id' then id
end ASC
, case
when #sortOrder <> 'ASC' then ''
when #sortColumn = 'name' then name
end ASC
, case
when #sortOrder <> 'ASC' then ''
when #sortColumn = 'dt' then name
end ASC
, case
when #sortOrder <> 'DESC' then 0
when #sortColumn = 'id' then id
end DESC
, case
when #sortOrder <> 'DESC' then ''
when #sortColumn = 'name' then name
end DESC
, case
when #sortOrder <> 'DESC' then ''
when #sortColumn = 'dt' then name
end DESC
You can use a complicated order by clause. That requires one case for each sort direction and each data type. With this example dataset:
create table t1 (id int, name varchar(50), created date);
insert t1 values
(1, 'Chihiro Ogino', '2012-01-01'),
(2, 'Spirit of the Kohaku River', '2012-01-03'),
(3, 'Yubaba', '2012-01-02');
You could use an order by clause like:
declare #sortColumn varchar(50) = 'created'
declare #sortOrder varchar(50) = 'DESC'
select *
from t1
order by
case
when #sortOrder <> 'ASC' then 0
when #sortColumn = 'id' then id
end ASC
, case
when #sortOrder <> 'ASC' then ''
when #sortColumn = 'name' then name
end ASC
, case
when #sortOrder <> 'ASC' then cast(null as date)
when #sortColumn = 'created' then created
end ASC
, case
when #sortOrder <> 'DESC' then 0
when #sortColumn = 'id' then id
end DESC
, case
when #sortOrder <> 'DESC' then ''
when #sortColumn = 'name' then name
end DESC
, case
when #sortOrder <> 'DESC' then cast(null as date)
when #sortColumn = 'created' then created
end DESC
Working example at SQL Fiddle.
Another option is to create the query dynamically, and run it with exec. For example:
declare #sql nvarchar(max)
set #sql = 'select * from YourTable order by ' + #sortColumn + ' ' + #sortDir
exec (#sql)
#Andomar's answer help solve a similar issue. I needed to sort on any number of 23 different columns, in any order. I ended up with the following:
create table sorting(ID int, columnName varchar(50), sort varchar(10), position int)
insert into sorting
values(1,'column1','DESC',1),
(1,'column2','ASC',2),
...
(1,'columnN','DESC',N)
Adding parameter #sort to the SP to identify the entries in sorting:
ORDER BY ISNULL(STUFF((SELECT ', ' + a.columnName + ' ' + a.sort
FROM sorting a
WHERE a.ID = #sort
ORDER BY a.position ASC
FOR XML PATH('')), 1, 2, ''),NULL)
There are two basic approaches to building dynamically orderable stored procedures:
Pass in the ORDER BY clause as a parameter to the stored procedure. In the stored procedure, build up the SQL statement in a string and then execute this statement using EXEC or sp_ExecuteSql.
-- This Method is used when your Column names are dynamic
-- We need to create a dynamic query and Execute it as shown below.
CREATE PROCEDURE getEmployees ( #OrderByClause varchar(100) ) AS
-- Create a variable #SQLStatement
DECLARE #SQLStatement varchar(255)
-- Enter the dynamic SQL statement into the
-- variable #SQLStatement
SELECT #SQLStatement = 'SELECT EmployeeID, FirstName, LastName, SSN, Salary
FROM Employees ORDER BY '+ #OrderByClause+''
-- Execute the SQL statement
EXEC(#SQLStatement)
Pass in the column to sort by and then use a CASE statement in the ORDER BY clause to order the results according to the input parameter value.
--This method is used when you column name is not dynamic
SELECT EmployeeID, FirstName, LastName, SSN, Salary
FROM Employees
ORDER BY
CASE WHEN #ColumnName='LastName' THEN LastName
WHEN #ColumnName='Salary' THEN CONVERT(varchar(50), Salary)
WHEN #ColumnName='SSN' THEN SSN
END
something like this should work :
ORDER BY
CASE WHEN #SortDirection = 'ASC' THEN #SortType END ASC,
CASE WHEN #SortDirection = 'DESC' THEN #SortType END DESC

Dynamic order by without using dynamic sql?

I have the following stored procedure which can be sorted ascending and descending on TemplateName,CreatedOn and UploadedBy. The following SP when runs doesnot sort records.if i replace 2,3,4 by columnname, i got an error message "Conversion failed when converting the nvarchar value 'Test Template' to data type int.".Please suggest how to achieve sorting.
CREATE PROCEDURE [dbo].[usp_SEL_GetRenderingTemplate]
(
#facilityID INT,
#sortOrder VARCHAR(5),
#sortExpression VARCHAR(100),
#errorCode INT OUTPUT
)
AS
BEGIN
SET NOCOUNT ON ;
BEGIN TRY
SET #sortOrder = CASE #sortOrder
WHEN 'Ascending' THEN 'ASC'
WHEN 'Descending' THEN 'DESC'
ELSE 'ASC'
END
SELECT TemplateID,
TemplateName,
CreatedOn,
( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
FROM Templates
INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
WHERE facilityid = #facilityID
ORDER BY CASE WHEN #sortExpression = 'TemplateName'
AND #sortOrder = 'ASC' THEN 2
WHEN #sortExpression = 'CreatedOn'
AND #sortOrder = 'ASC' THEN 3
WHEN #sortExpression = 'UploadedBy'
AND #sortOrder = 'ASC' THEN 4
END ASC,
CASE WHEN #sortExpression = 'TemplateName'
AND #sortOrder = 'DESC' THEN 2
WHEN #sortExpression = 'CreatedOn'
AND #sortOrder = 'DESC' THEN 3
WHEN #sortExpression = 'UploadedBy'
AND #sortOrder = 'DESC' THEN 4
END DESC
SET #errorCode = 0
END TRY
BEGIN CATCH
SET #errorCode = -1
DECLARE #errorMsg AS VARCHAR(MAX)
DECLARE #utcDate AS DATETIME
SET #errorMsg = CAST(ERROR_MESSAGE() AS VARCHAR(MAX))
SET #utcDate = CAST(GETUTCDATE() AS DATETIME)
EXEC usp_INS_LogException 'usp_SEL_GetFacilityWorkTypeList',
#errorMsg, #utcDate
END CATCH
END
The dynamic ordering has to be of the same datatype. Here is an example of something that I use to case order three different datatypes - integer, date (ascending and descending), and string. This may not work for your situation, but at least you can see some techniques for casting to a common datatype.
...
ORDER BY
Case Parent.RankTypeID
When 0 Then dbo.Documents.Rank
When 1 Then Convert(int, dbo.Documents.DateStart, 112)
When 2 Then (1 - Convert(int, dbo.Documents.DateStart, 112))
When 3 Then Cast(dbo.Documents.Title as sql_variant)
End
Note:
112 is a date format of YYYYMMDD - useful for ordering.
We perform a similar sort of dynamic ordering in one of our products.
The only real different to your code is firstly, we don't use the live join.
We create a temporary table, so we can perform paging, and then apply the order.
We also use ints for ordering, cuts down on the overhead of string comparison.
It does make your SQL a bit longer, but this approach is definitely faster for the Query Optimiser. Also, and more importantly, I don't think you can mix types in Switch blocks, for ordering, so to follow your original code, you'd have to CONVERT all your data to the same time, which defeats the object :(
So you'd have
DECLARE #temp TABLE(ID int identity(1,1), TemplateID int, TemplateName nvarchar(100), CreatedOn datetime, UploadedBy nvarchar(100))
INSERT INTO #temp(TemplateID, TemplateName, CreatedOn, UploadedBy)
SELECT TemplateID,
TemplateName,
CreatedOn,
( [user].LastName + ' ' + [user].FirstName ) AS UploadedBy
FROM Templates
INNER JOIN [user] ON [user].UserID = Templates.CreatedBy
WHERE facilityid = #facilityID
Then:
IF #SortOrder = 1 --'ASC'
BEGIN
IF #sort = 2
Select *
From #Temp
Order by TemplateName ASC
ELSE IF #sort = 3
Select *
From #Temp
Order By CreatedBy ASC
-- and so on...
END
ELSE -- descending
BEGIN
-- Ad. Inf.
END
Delete
From #Temp
WHERE ID < #pageStart or ID > #pageStart + #pageSize
The following SP when runs doesnot
sort records.if i replace 2,3,4 by
columnname,
You shouldn't replace 2, 3, 4 in the ORDER BY clause by the column name. When you call the procedure just pass the column you want to sort by as the 3rd parameter.
EXEC [dbo].[usp_SEL_GetRenderingTemplate] 1,'Ascending','CreatedOn',#vErrorCode
The CASE will then evaluate the query to something like
...ORDER BY 3 DESC
which sorts the query by the 3rd column in the SELECT clause (i.e. CreatedOn)
The problem is that in CASE clauses the return values must have the same data type.
You can work around this problem by using multiple CASE statements http://www.extremeexperts.com/sql/articles/CASEinORDER.aspx
Or casting everything to the same data type.