SQL multiple case statement - sql

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.

Related

Order converting to int

I have this order by clause in my stored procedure
ORDER BY
CASE WHEN #sortDir = 2 THEN
CASE
WHEN #sortCol = 'ProductNumber' THEN ProductId
WHEN #sortCol = 'ProductName' THEN CAST(V.[Name] AS NVARCHAR)
...
END
END DESC
I wanted to sort via the column given the #sortCol
It's giving me this error
Conversion failed when converting the nvarchar value 'Product 28' to
data type int.
Why?
How do I fix this?
The "problem" is that case is an expression that returns a single type. And ints beat out strings.
Instead of worrying about types, use separate expressions:
ORDER BY (CASE WHEN #sortDir = 2 AND #sortCol = 'ProductNumber' THEN ProductId END) DESC,
(CASE WHEN #sortDir = 2 AND #sortCol = 'ProductName' THEN v.Name END) DESC,
. . .
This is a bit more verbose, but it avoids all type conversions.

SQL Case Order By specific order and Direction

I have a table that I need sorted on fields By #SortBy and #SortDirection,
for ID,PriorityID,stateType (column type is int) result is OK, but for Title (nvarchar) Query Result is:
Conversion failed when converting the nvarchar value 'Title Column value' to data type int.
Query:
CASE
WHEN #SortDirection = 'ASC' THEN
CASE #SortBy
WHEN 'ID' THEN ID --int
WHEN 'Title' THEN Title --nvarchar
WHEN 'PriorityID' THEN [Priority] --int
WHEN 'stateType' THEN [state] --int
end
END ASC
,case WHEN #SortDirection = 'DESC' THEN
CASE #SortBy
WHEN 'ID' THEN ID
WHEN 'Title' THEN Title
WHEN 'Priority' THEN [Priority]
WHEN 'state' THEN [state]
END
END DESC
The types are different, and that is a problem for the case expressions. The simplest method is a different case for each possibility:
ORDER BY (CASE WHEN #SortDirection = 'ASC' AND #SortBy = 'ID' THEN ID END) ASC,
(CASE WHEN #SortDirection = 'ASC' AND #SortBy = 'Title' THEN Title END) ASC,
. . .

Arithmetic overflow error When sorting by [TimeStamp] (column name)

I'm getting the error:
Arithmetic overflow error converting expression to data type smalldatetime
Please note that I've already searched this topic, but my bug is unique because it is caused by the ORDER BY clause. the problematic line(s) is
WHEN 'TimeStamp' THEN [MyTable].[TimeStamp].
when I comment those TimeStamp, the query executed successfully, but I still need a solution for sorting my TimeStamp column (renaming the column is not possible).
This is my query:
SELECT
Id,
[TimeStamp],
StatusCode
FROM
(
SELECT
TOP (#EndRecord) ROW_NUMBER() OVER
(
ORDER BY
CASE #SortDirection
WHEN 'Desc' THEN
CASE #SortColumn
WHEN 'Id' THEN [MyTable].[Id]
WHEN 'TimeStamp' THEN [MyTable].[TimeStamp]
WHEN 'StatusCode' THEN [MyTable].[StatusCode]
ELSE [MyTable].[TimeStamp]
END
END DESC,
CASE #SortDirection
WHEN 'Asc' THEN
CASE #SortColumn
WHEN 'Id' THEN [MyTable].[Id]
WHEN 'TimeStamp' THEN [MyTable].[TimeStamp]
WHEN 'StatusCode' THEN [MyTable].[StatusCode]
ELSE [MyTable].[TimeStamp]
END
END ASC
) AS 'Row',
[MyTable].[Id] AS 'Id',
[MyTable].[TimeStamp] AS 'TimeStamp',
[MyTable].[StatusCode] AS 'StatusCode'
FROM
[MyTable]
WHERE
(#Filter_Id is null or [MyTable].[Id] = #Filter_Id) AND
(#Filter_StatusCode is null or [MyTable].[StatusCode] = #Filter_StatusCode) AND
(#Filter_FromTimeStamp IS NULL OR [MyTable].[TimeStamp] >= #Filter_FromTimeStamp) AND
(#Filter_TillTimeStamp IS NULL OR [MyTable].[TimeStamp] <= #Filter_TillTimeStamp)
) AS t1
WHERE
Row >=#StartRecord AND
Row <= #EndRecord
A CASE expression has to return a value of a particular type. All THEN clauses have to agree as to the type or allow the data type precedence rules to determine the overall type and apply conversions.
Since you probably don't want to e.g. convert everything to strings and perform ordering operations based on the textual sorting rules, you need to split out your CASE expressions so that you're not forcing any conversions between types.
E.g.
ORDER BY
CASE WHEN #SortDirection='Desc' and #SortColumn = 'Id'
THEN [MyTable].[Id]
END DESC,
CASE WHEN #SortDirection='Desc' and #SortColumn = 'TimeStamp'
THEN [MyTable].[TimeStamp]
END DESC,
CASE WHEN #SortDirection='Desc' and #SortColumn='StatusCode'
THEN [MyTable].[StatusCode]
END DESC,
CASE WHEN #SortDirection='Desc' and #SortColumn not in ('Id','TimeStamp','StatusCode')
THEN [MyTable].[TimeStamp]
END DESC,
And similarly for your ascending sorts.

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.