A developer wanted to keep track of some birthdays with the following table design and data.
To sort the data by name and by date, the developer wrote a single stored procedure to handle both cases.
The developer tested the proc by passing a value of 1, and was pleased to see a resultset ordered by birth date.
What can go wrong? (Hint: Try passing a 2).
How could the developer fix the problem by changing only 1 line?
Comments
The problem is that [Birthdate] and [Name] are different types.
I'd fix it by altering the ORDER BY clause to:
CASE @Ordering
WHEN 1 THEN [Birthdate]
END,
[Name]
This way, [Birthdate] just gets skipped (well, it uses null) unless @Ordering is 1.
You could do it by converting birthdate to a string using format 112 - but it's horrible to sort on a date that's converted to a string.
Rob
Returns the highest precedence type from the set of types in result_expressions and the optional else_result_expression.
Correct:
CREATE PROC GetBirthdays
@Ordering int
AS
BEGIN
SELECT [Name], [BirthDate] FROM [Birthdays]
ORDER BY
CASE @Ordering
WHEN 1 THEN CAST([Birthdate] AS varchar(50))
WHEN 2 THEN [Name]
END
END
I have solved the issue and also providing it here.
// Procedure
CREATE PROC GetBirthdays
@Ordering int
AS
BEGIN
SELECT [Name], [BirthDate] FROM [Birthdays]
ORDER BY
CASE @Ordering
WHEN 1 THEN CAST(Birthdate as nvarchar(50))
WHEN 2 THEN Name
END
END
// Execution
Exec GetBirthdays 2
WHEN 1 THEN [Birthdate]
to
WHEN 1 THEN cast([Birthdate] as varchar(10))
but that seems like a bit of a hack, I'll think a bit more & see if there's a better solution
SQl reports
Server: Msg 241, Level 16, State 1, Procedure GetBirthdays, Line 6
Syntax error converting datetime from character string.
because it expects the types returned by the case to be the same as the 1st option.
"Returns the highest precedence type from the set of types in result_expressions and the optional else_result_expression."
WHEN 1 THEN cast([Birthdate] as varchar(50))
That was pretty tough. I had to look it up on google:
www.4guysfromrolla.com/webtech/010704-1.shtml
:)
CASE WHEN @Ordering = 1 THEN [Birthdate] END,
CASE WHEN @Ordering = 2 THEN [Name] END
instead of
CASE @Ordering
WHEN 1 THEN [Birthdate]
WHEN 2 THEN [Name]
END
WHEN 1 THEN [Birthdate]
ELSE [NAME] ??
so i think the solution is to cast the datetime to varchar...
alter PROC GetBirthdays
@Ordering int
AS
BEGIN
SELECT [Name], [BirthDate] FROM [Birthdays]
ORDER BY
CASE @Ordering
WHEN 1 THEN cast([Birthdate] as varchar)
WHEN 2 THEN [Name]
END
END
CASE @Ordering
WHEN 1 THEN cast([Birthdate] as varchar(20))
WHEN 2 THEN [Name]
END
mixing datatypes in dynamic order by doesn't make SQL very happy. : )
CONVERT would also work in this instance:
when 1 then convert(varchar(20), [birthdate])
CREATE PROC GetBirthdays
@Ordering int
AS
BEGIN
SELECT [Name],
[BirthDate]
FROM [Birthdays]
ORDER BY
CASE @Ordering
WHEN 1 THEN CONVERT(char(30),[BirthDate],121)
WHEN 2 THEN [Name]
END
END
Another solution I've seen is to write the CASE statement as:
CASE @Ordering
WHEN 1 THEN CAST([Birthdate] as sql_variant)
WHEN 2 THEN [Name]
END
Thoughts?
CREATE PROC GetBirthdays
@Ordering int
AS
BEGIN
SELECT Name, BirthDate
FROM (
SELECT [Name], [BirthDate],
CASE @Ordering
WHEN 1 THEN [Birthdate]
WHEN 2 THEN [Name]
END As SortField
FROM [Birthdays] ) data
ORDER BY data.SortField
END
CASE @Ordering
WHEN 1 THEN [Name]
WHEN 2 THEN convert(varchar(20), [birthdate], 21)
END