I have a query where each row consists of 3 columns:
I want to sort the rows based on number of NOT NULL
(ie present) values exactly as follows:
Here is sample data (insert statements are sorted in the order i expect):
/*
CREATE TABLE #TEMP (
Type VARCHAR(100),
Name VARCHAR(100),
Distance VARCHAR(100),
Proximity VARCHAR(100)
);
*/
INSERT INTO #TEMP VALUES ('AIRPORT', 'KBLI', '21mi', 'City')
INSERT INTO #TEMP VALUES ('AIRPORT', 'KBLI', '21mi', NULL )
INSERT INTO #TEMP VALUES ('AIRPORT', 'KBLI', NULL , 'City')
INSERT INTO #TEMP VALUES ('AIRPORT', NULL , '21mi', 'City')
INSERT INTO #TEMP VALUES ('AIRPORT', 'KBLI', NULL , NULL )
INSERT INTO #TEMP VALUES ('AIRPORT', NULL , '21mi', NULL )
INSERT INTO #TEMP VALUES ('AIRPORT', NULL , NULL , 'City')
I have had some success with COALESCE
statement but I am looking for something efficient and readable. Later I will change to four columns.
Assign a present value as if it were a number (a name=4, a distance=3, a proximity=2), then sum them and sort by that:
select ...
from ...
order by
case when name is null then 0 else 4 end +
case when distance is null then 0 else 3 end +
case when proximity is null then 0 else 2 end desc
The trick here is that 3+2 > 4, so a distance and proximity beats a name.
This doesn't have any cool fancy math, but if you were to have multiple values for the same [type] then mine would sort those in order as well.
SELECT *
FROM #Temp
ORDER BY [Type],
LEN(CONCAT(LEFT(Name,1),LEFT(Distance,1),LEFT(Proximity,1))) DESC, --counts number of non null columns
--LEN(ISNULL(LEFT(Name,1),'') + ISNULL(LEFT(Distance,1),'') + ISNULL(LEFT(Proximity,1),'')) DESC, /*SQL 2008R2 and below alternative for counting non-null columns*/
ISNULL(Name,'zz'), --ISNULL then 'zz' which when ordered, goes at the end
ISNULL(Distance,'zz'),
ISNULL(Proximity,'zz')
For the general question, you can use:
order by ((case when name is not null then 1 else 0 end) +
(case when distance is not null then 1 else 0 end) +
(case when proximity is not null then 1 else 0 end)
) desc
EDIT:
I didn't realize you wanted sub-ordering as well. You might have to list out all the possibilities:
order by (case when name is not null and proximity is not null and distance is not null then 1
when name is not null and distance is not null then 2
when name is not null and proximity is not null then 3
when proximity is not null and distance is not null then 4
when name is not null then 5
when distance is not null then 6
when proximity is not null then 7
else 8
end)
This should do it:
SELECT t.*
FROM dbo.Tablename t
ORDER BY
CASE WHEN Name IS NOT NULL
AND Distance IS NOT NULL
AND Proximity IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Name IS NOT NULL
AND Distance IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Name IS NOT NULL
AND Proximity IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Distance IS NOT NULL
AND Proximity IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Name IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Distance IS NOT NULL THEN 0 ELSE 1 END ASC,
CASE WHEN Proximity IS NOT NULL THEN 0 ELSE 1 END ASC
Demo with your sample data.
So the most important order criteria is the first and the least important the last.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.