Random sorting with ORDER BY with CASE clause - sql

I am testing ORDER BY clause with CASE, and came across this problem.
My test select statement:
SELECT to_date as "One", field1 as "Two"
FROM(
SELECT to_date('yyyy-mm-dd', '2017-10-10'), '333' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '111' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '222' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), '' union all
SELECT to_date('yyyy-mm-dd', '2017-09-09'), ''
)
ORDER BY One DESC,
CASE when Two = '' then 1
else 0 end DESC
And it's result may vary in a way, that sorting by second column is random:
How should I modify CASE clause to avoid it?

In Oracle, an empty string '' is the identical to NULL so your query is:
ORDER BY
One DESC,
CASE when Two = NULL then 1 else 0 end DESC
When comparing values, the are possible states are:
Equality Result
------------------------ ------
value = value TRUE
value = other_value FALSE
value = NULL NULL
NULL = NULL NULL
Your CASE expression will only evaluate to 1 when the equality evaluates to TRUE and this will never be the result when at least one side of the equality is NULL.
What you want is to use IS NULL rather than = '':
ORDER BY
One DESC,
CASE WHEN Two IS NULL THEN 1 ELSE 0 END DESC,
Two DESC;
Which you can simplify to:
ORDER BY
One DESC,
Two DESC NULLS FIRST;
The default for DESC ordering is NULLS FIRST so you could further simplify it to:
ORDER BY
One DESC,
Two DESC;
However, I would not take it this far as you are better explicitly stating that you are expecting NULL values to be ordered before non-NULL so future developers know that that is your intended ordering (rather than just an unintentional side-effect of your query).

Add the column two as third order condition
ORDER BY One DESC,
CASE when Two = '' then 1 else 0 end DESC,
Two DESC
The second order condition only puts empty entries first and not more.

Related

Use rownum to always get non null value

select case when (CUST.ADDRESS_TYPE='OFFICE') then
(Select MOBILE
FROM cust_table CUST
where CID = Deal.CID
and ADDRESS_TYPE = 'CURRES'
and rownum = 1)
else
CUST.MOBILE
end as MOBILE
FROM cust_table CUST
RIGHT OUTER JOIN (SELECT CID CID
, WNAME
, APPLICANT_TYPE
FROM deal_table ) DEAL
ON DEAL.CID = CUST.CID
AND APPLICANT_TYPE = 'P'
and mailing_add = 'true'
WHERE WNAME='22135'
and rownum = 1
#MOBILE#
NULL
647432923
OR
#MOBILE#
74238423
NULL
This query returns a column named 'MOBILE ' with two rows, one of the entries being always null when I dont use rownum = 1 at the end, but if I put rownum = 1 towards the end then in some cases it returns null value and in some cases non null value. How can I use rownum so that the query always returns non null value.
Do not use rownum for this. rownum will give you the number of the row after your query has been run. Use a properly defined ORDER BY clause to get your NULLS at the end and only fetch first row.
<your query>
WHERE wname='22135'
ORDER BY mobile NULLS LAST
FETCH FIRST 1 ROWS ONLY

Order by a specific value first then show all rest in order

The below query returns list of areas i want to show the null value which is '--- All ----' then the rest of the values.
So out out will be
AreaID AreaName
Null '--- All ----'
1 area1
2 area2
etc..
Query
SELECT
dbo.Areas.AreaID,
dbo.Areas.AreaName
FROM dbo.Areas
UNION
SELECT
NULL,
'--- All ----'
ORDER BY dbo.Areas.AreaName
Try this:
SELECT AreaID,
AreaName
FROM (
SELECT dbo.Areas.AreaID,
dbo.Areas.AreaName
FROM dbo.Areas
UNION
SELECT NULL,
'--- All ----'
) AS t
ORDER BY CASE
WHEN AreaID IS NULL THEN 1
ELSE 2
END,
AreaName
Note: Using a nested derived table is not required as it would have sufficed to use ORDER BY like you did in your query. I think though it adds to the readability of the query.
One option is to use a CASE expression to place your NULL record first in the result set. You can also add a second ordering condition to sort on the area name after this first condition has been applied.
SELECT
dbo.Areas.AreaID,
dbo.Areas.AreaName
FROM dbo.Areas
UNION
SELECT
NULL,
'--- All ----'
ORDER BY CASE WHEN dbo.Areas.AreaID IS NULL THEN 0 ELSE 1 END,
dbo.Areas.AreaName

Why does this SQL order null values last?

This will apparently put null values for myDate at the bottom of the result set. What's the logic behind how this is being executed?
SELECT * FROM myTable
WHERE ...
ORDER BY CASE WHEN myDate IS NULL THEN 1 ELSE 0 END, myDate;
This is your order by:
ORDER BY (CASE WHEN myDate IS NULL THEN 1 ELSE 0 END),
myDate
The first expression for the order by says "Give the NULL values a value of 1 (for the sort) and non-NULL values a value of 0". Well, you are sorting in ascending order, so the NULL values go last.
If you want them first, use desc:
ORDER BY (CASE WHEN myDate IS NULL THEN 1 ELSE 0 END) DESC,
myDate
ORDER BY CASE WHEN myDate IS NULL THEN 1 ELSE 0 END, myDate;
When myDate is null the expression will return 1. Otherwise it will return 0. 1 is greater than 0, so when ordering by the result of that expression in ascending order (the default), null values are moved to the end.

Adding second CASE WHEN column changes query result completely?

I have the following queries:
1
SELECT TOP 1
CASE WHEN latency=-1
THEN 'Down'
ELSE 'Up'
END AS status
FROM
#pings_temp
ORDER BY datetime DESC;
2
SELECT TOP 1
CASE WHEN latency=-1
THEN 'Down'
ELSE 'Up'
END AS status,
CASE WHEN latency=-1
THEN
(
SELECT TOP 1
datetime
FROM
#downtimes_temp
ORDER BY
datetime DESC
)
ELSE NULL
END AS datetime
FROM
#pings_temp
ORDER BY datetime DESC;
The first should yield just 'Up' and the second should yield 'Up' in the first column and NULL in the second.
However, this doesn't happen. The first query performs exactly how it should, but adding the second column to the SELECT makes the query go berserk, causing it to read out 'Down' and the date from a seemingly random column.
Here's an image of the two results from the same T-SQL batch.
The error in the second case is probably caused by your ORDER BY clause, since the engine cannot evaluate the order of a NULL value.

Ordering Select clause result in specific way

I need help with writing a select clause query.
For example, lets say I have a query like that:
select value from some_table order by value asc;
as a result I get this:
1
2
3
4
5
6
7
8
9
10
but a special query I want to write, is the one which still will give me sorted values, but will put 5 after 8.
this means I need one value to be out of regular order.
it can be described in other way. lets say I have two groups of numbers (example):
A={ a | 1<=a<=118, a!=78 } B={ b | b>118 }
I have a group C=A U B U {78}
and I need all these values sorted like "A,78,B"
Assuming value is integer, you could do this:
SELECT *
FROM tbl
ORDER BY
CASE
WHEN value = 5 THEN 8.5
ELSE value
END
Or to expand upon DCP's answer...
SELECT *
FROM tbl
ORDER BY
CASE
WHEN (Condition for first grouping) THEN 1
WHEN (Condition for second grouping) THEN 2
WHEN (Condition for third grouping) THEN 3
ELSE 4
END
You can use multiple conditions in your order by:
ORDER BY (value BETWEEN 1 AND 118) AND value != 78 DESC,
value > 118 DESC,
value
This will ensure that values which match the first predicate come first, then values matching the second predicate, and finally values matching none of the predicates. If there is a tie (two numbers matching the same predicate) then these numbers are sorted in ascending order.
Note that I haven't tested this in Oracle. It might be necessary to wrap the predicate in a CASE expression (CASE WHEN predicate THEN 1 ELSE 0 END) to get the sorting to work in Oracle.
ORDER BY
(CASE WHEN ((value BETWEEN 1 AND 118) AND value <> 78) THEN 1 ELSE 0 END) DESC,
(CASE WHEN (value > 118) THEN 1 ELSE 0 END) DESC,
value
Order by some CASE-expression to remap your values.