Reputation: 494
I have written a stored procedure which returns sorted(ascending or descending) data based on the column which user has selected. To achieve that I used Case statements in my order by clause like following code snippet
ORDER BY
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END DESC
Issue in this code is that it does not sort data properly if any column with integer data type is selected. It considers integer value as varchar and sorts the data accordingly
For e.g. It returns
10
1
2
3
If requestId is selected in ASCENDING order.
I tried using couple of solutions for the problem like use ABS() to convert varchar value to integer etc but they are not helping me solve the issue.
I would appreciate if you guys help me to solve the issue
Upvotes: 2
Views: 96
Reputation: 350280
Indeed, when you combine different data types like that, the expression gets converted to varchar
, even when p_filter_type and p_filter_column indicate that only a numeric column should determine the outcome of that expression.
One way to solve this, is to create a separate order by
expression for each of the possibilities, producing null
for all of them except for the one that is relevant. That way each of the expressions can stick to its own data type:
ORDER BY
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'projectCode'
THEN projectcode END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'visaType'
THEN visaType END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'approveRejectStatus'
THEN status END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'createdDate'
THEN createdDate END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'employeeID'
THEN employeeID END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'requestId'
THEN requestId END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'country'
THEN country END ASC,
CASE WHEN p_filter_type = 'ASC' AND p_filter_column = 'serviceDesk'
THEN serviceDesk END ASC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'projectCode'
THEN projectcode END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'visaType'
THEN visaType END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'approveRejectStatus'
THEN status END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'createdDate'
THEN createdDate END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'employeeID'
THEN employeeID END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'requestId'
THEN requestId END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'country'
THEN country END DESC,
CASE WHEN p_filter_type = 'DESC' AND p_filter_column = 'serviceDesk'
THEN serviceDesk END DESC
For instance, when p_filter_type = 'DESC'
and p_filter_column = 'employeeID'
then the above ORDER BY
clause really resolves to the following:
ORDER BY
NULL ASC,
NULL ASC,
NULL ASC,
NULL ASC,
NULL ASC,
NULL ASC,
NULL ASC,
NULL ASC,
NULL DESC,
NULL DESC,
NULL DESC,
NULL DESC,
employeeID DESC,
NULL DESC,
NULL DESC,
NULL DESC
which should order the same way as if you had just written:
ORDER BY
employeeID DESC
You could of course go for some in-between solution, where you group the fields that have the same data type together in one expression, so that no data type conversion will take place. That way you will have maybe 6 expressions in your order by
clause: one for varchar
fields, one for int
fields, one for date
fields, and then each of those repeated for the descending case:
ORDER BY
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column -- all varchar fields
WHEN 'projectCode' THEN projectCode
WHEN 'visaType' THEN visaType
WHEN 'approveRejectStatus' THEN status
WHEN 'country' THEN country
WHEN 'serviceDesk' THEN serviceDesk
END
END ASC,
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column -- all int fields
WHEN 'employeeID' THEN employeeId
WHEN 'requestId' THEN requestId
END
END ASC,
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column -- all date fields
WHEN 'createdDate' THEN createdDate
END
END ASC,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column -- all varchar fields
WHEN 'projectCode' THEN projectCode
WHEN 'visaType' THEN visaType
WHEN 'approveRejectStatus' THEN status
WHEN 'country' THEN country
WHEN 'serviceDesk' THEN serviceDesk
END
END DESC,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column -- all int fields
WHEN 'employeeID' THEN employeeId
WHEN 'requestId' THEN requestId
END
END DESC,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column -- all date fields
WHEN 'createdDate' THEN createdDate
END
END DESC
Upvotes: 2
Reputation: 31802
You can generate a prepared statement using the CONCAT function:
SET @sql = CONCAT('select * from some_table order by ', p_filter_column, ' ', p_filter_type);
PREPARE stmt FROM @sql;
EXECUTE stmt;
As a bonus, the prepared statement can use indexes (if defined).
However you might need to validate the input first.
Another workaround could be to use LPAD for numeric fields and fill the number with zeros:
WHEN 'employeeID' THEN LPAD(employeeId, 20, '0') -- int field
But it will only work for UNSIGNED types.
Upvotes: 0
Reputation: 15140
Since CASE
returns an atomic value, the returned datatype can only be single. You can't have a different returned datatype on a different execution. In this case, everything gets implicitly converted to a varchar. In order to get the order you want, you have to make sure the integers will order according to string ordering rules. Like so:
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN RIGHT(CONCAT('00', employeeId), 2) -- int field
WHEN 'requestId' THEN RIGHT(CONCAT('00', requestId), 2) -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END
For the ordering, it will alter the values to 01, 02, 03, 10
, which will sort the way you want them to.
I'm not sure if your dates will be ordered properly though, that depends on your local settings.
Upvotes: 0
Reputation: 7937
ORDER BY
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN CAST(projectCode AS INT) -- varchar field
WHEN 'visaType' THEN CAST(visaType AS INT) -- varchar field
WHEN 'approveRejectStatus' THEN CAST(status AS INT) -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN CAST(country AS INT) -- varchar field
WHEN 'serviceDesk' THEN CAST(serviceDesk AS INT) -- varchar field
END
END,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN CAST(projectCode AS INT) -- varchar field
WHEN 'visaType' THEN CAST(visaType AS INT) -- varchar field
WHEN 'approveRejectStatus' THEN CAST(status AS INT) -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN CAST(country AS INT) -- varchar field
WHEN 'serviceDesk' THEN CAST(serviceDesk AS INT) -- varchar field
END
END DESC
I think as your column with varchar type contains digits and you want to sort this field according to number, you need to cast that VARCHAR field to INT during ordering as i had tried in above code. Hope this will help you.
Upvotes: 0
Reputation: 12378
If you just want to convert varchar value to integer, assuming these values are digits, mysql can do it simply, let that column plus 0, like this:
ORDER BY
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId + 0 -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId + 0 -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END DESC
see a simple demo in sqlfiddle.
Upvotes: 0
Reputation: 2296
How about adding that ASC order to first case ?
ORDER BY
CASE WHEN p_filter_type = 'ASC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END ASC,
CASE WHEN p_filter_type = 'DESC' THEN
CASE p_filter_column
WHEN 'projectCode' THEN projectCode -- varchar field
WHEN 'visaType' THEN visaType -- varchar field
WHEN 'approveRejectStatus' THEN `status` -- varchar field
WHEN 'createdDate' THEN createdDate
WHEN 'employeeID' THEN employeeId -- int field
WHEN 'requestId' THEN requestId -- int field
WHEN 'country' THEN country -- varchar field
WHEN 'serviceDesk' THEN serviceDesk -- varchar field
END
END DESC
does it work? Also, are You sure that date You used as example of wrong sorting is not in Varchar column? It looks like it works as one.
Upvotes: 0