2

I would like to create a custom order in my sql query, just changing one rows position.

This is my current sql results -

Age Category    Female  Male
-------------------------------

30-39           2772    3193
40-49           1587    2246
50-65           990     3718
Over 65         176     3487
Under 30        1359    1500

I would like them to sort like this, with the 'under 30' at the top -

Age Category    Female  Male
-------------------------------

Under 30        1359    1500    
30-39           2772    3193
40-49           1587    2246
50-65           990     3718
Over 65         176     3487

Here is my code -

SELECT DISTINCT


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN 'Under 30'
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN '30-39'
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN '40-49' 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN '50-65'
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN 'Over 65'
END as 'Age Category',


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) <= 30 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 30 AND 39 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 40 AND 49 and gender ='f' and status ='a' and member_type ='mm') 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 50 AND 64 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) >= 65 and gender ='f' and status ='a' and member_type ='mm')
END as 'Female',


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) <= 30 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 30 AND 39 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 40 AND 49 and gender ='m' and status ='a' and member_type ='mm') 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 50 AND 64 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) >= 65 and gender ='m' and status ='a' and member_type ='mm')
END as 'Male'


FROM NAME N1
WHERE [STATUS] ='A' AND 
  MEMBER_TYPE IN ('MM') AND
  (
   CASE 
        WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN 'Under 30'
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN '30-39'
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN '40-49' 
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN '50-65'
        WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN 'Over 65'
   END
  ) IS NOT NULL

group by datediff(YYYY,birth_date,getdate()), member_type

Much appreciated

3 Answers 3

2

Add a manually calculated SortOrder column, then order by that and [Age Category]

SELECT DISTINCT


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN 'Under 30'
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN '30-39'
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN '40-49' 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN '50-65'
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN 'Over 65'
END as 'Age Category',


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) <= 30 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 30 AND 39 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 40 AND 49 and gender ='f' and status ='a' and member_type ='mm') 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 50 AND 64 and gender ='f' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) >= 65 and gender ='f' and status ='a' and member_type ='mm')
END as 'Female',


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) <= 30 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 30 AND 39 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 40 AND 49 and gender ='m' and status ='a' and member_type ='mm') 
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate())BETWEEN 50 AND 64 and gender ='m' and status ='a' and member_type ='mm')
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) >= 65 and gender ='m' and status ='a' and member_type ='mm')
END as 'Male'

-- Newly inserted code starts


CASE 
    WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN 1
    WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 65 THEN 3
    WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN 2
END as 'SortOrder',


-- Newly Inserted Code Ends

FROM NAME N1
WHERE [STATUS] ='A' AND 
  MEMBER_TYPE IN ('MM') AND
  (
   CASE 
        WHEN datediff(YYYY,birth_date,getdate()) <= 30 THEN 'Under 30'
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 30 AND 39 THEN '30-39'
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 40 AND 49 THEN '40-49' 
        WHEN datediff(YYYY,birth_date,getdate()) BETWEEN 50 AND 65 THEN '50-65'
        WHEN datediff(YYYY,birth_date,getdate()) >= 65 THEN 'Over 65'
   END
  ) IS NOT NULL

group by datediff(YYYY,birth_date,getdate()), member_type
-- newly inserted code
ORDER BY SortOrder, [Age Category]
Sign up to request clarification or add additional context in comments.

2 Comments

@RyanGillooly - Though it does still rely on you use of literally dozens of sub-queries making this very resource hungry, which is not at all needed.
@CoolWilly's answer is far simpler than mine, and it'll work. I'm going to keep this out there as an example because a computed sort order column is sometimes necessary, but in this case, I'd go with his answer, not mine.
2

Isn't it enough to just add an order by?
order by datediff(YYYY,birth_date,getdate())

Otherwise, maybe you could also change the age categories to "30 and under" and "65 and above"

Edit This query may be easier / less repetitive. By the way, the BETWEEN clause is inclusive, so you should use < 30 instead of <= 30 (and > 65 instead of >= 65) to make sure those ages aren't counted twice.

SELECT 'Under 30' AS 'Age Category',
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) < 30 and gender ='f' and status ='a' and member_type ='mm') AS 'Female',
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) < 30 and gender ='m' and status ='a' and member_type ='mm') AS 'Male'
UNION ALL
SELECT '30-39',
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 30 AND 39 and gender ='f' and status ='a' and member_type ='mm')
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 30 AND 39 and gender ='m' and status ='a' and member_type ='mm')
UNION ALL
SELECT '40-49',
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 40 AND 49 and gender ='f' and status ='a' and member_type ='mm')
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 40 AND 49 and gender ='m' and status ='a' and member_type ='mm')
UNION ALL
SELECT '50-65',
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 50 AND 65 and gender ='f' and status ='a' and member_type ='mm')
    (select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) BETWEEN 50 AND 65 and gender ='m' and status ='a' and member_type ='mm')
UNION ALL
SELECT 'Over 65',
(select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) > 65 and gender ='f' and status ='a' and member_type ='mm')
(select count(*) from name n1 where datediff(YYYY,n1.birth_date,getdate()) > 65 and gender ='m' and status ='a' and member_type ='mm')

3 Comments

Better yet, order by birth_date desc. By the way, if anyone thinks this should have been a comment, at the time it was posted, @CoolWilly did not have a high enough rep to do that.
either way, both datediff(YYYY,birth_date,getdate()) and birth_date would have to be included in my select statement, which then brings back more results than i would like
Are you sure, have you tried it? To my knowledge, columns specified in an ORDER BY clause do not need to be specified in the SELECT clause. (But I only checked in a simpler query.)
1

If you create a table to join on this becomes so much easier.

CREATE TABLE dim_age_band (
  id            INT,
  title         VARCHAR(32),
  ordinal       INT,
  bound_lower   INT,
  bound_upper   INT
)

INSERT INTO dim_age_band SELECT 1, 'Under 30', 1,  0,   30;
INSERT INTO dim_age_band SELECT 2, '30-39',    2, 30,   40;
INSERT INTO dim_age_band SELECT 3, '40-49',    3, 40,   50;
INSERT INTO dim_age_band SELECT 4, '50-65',    4, 50,   66;
INSERT INTO dim_age_band SELECT 5, 'Over 65',  5, 66, 1000;

SELECT
  MAX([dim_age_band].[name]                            )   AS age_band_name,
  SUM(CASE WHEN [name].[gender] = 'f' THEN 1 ELSE 0 END)   AS female,
  SUM(CASE WHEN [name].[gender] = 'm' THEN 1 ELSE 0 END)   AS male
FROM
  dim_age_band
LEFT JOIN
  name
    ON  [name].[birth_date] <= DATEADD(YYYY, GetDate(), [dim_age_band].[bound_lower])
    AND [name].[birth_date] >  DATEADD(YYYY, GetDate(), [dim_age_band].[bound_Upper])
    AND [name].[STATUS]      = 'A'
    AND [name].MEMBER_TYPE  IN ('MM')
GROUP BY
  [dim_age_band].[id]
ORDER BY
  MAX([dim_age_band].[ordinal])
;

EDIT :

Note that I'm also not using DATEDIFF() on the [birth_date] field. Instead I'm running that calculation on today's date and the age band boundaries. This means that it becomes possible to use indexes to search the [name] table by the [birth_date]; if such indexes exist.

EDIT :

CTE Version...

WITH
  dim_age_band (name, ordinal, bound_lower, bound_upper)
AS
(
            SELECT 'Under 30', 1,  0,   30
  UNION ALL SELECT '30-39',    2, 30,   40
  UNION ALL SELECT '40-49',    3, 40,   50
  UNION ALL SELECT '50-65',    4, 50,   66
  UNION ALL SELECT 'Over 65',  5, 66, 1000
)
SELECT
  MAX([dim_age_band].[name]                            )   AS age_band_name,
  SUM(CASE WHEN [name].[gender] = 'f' THEN 1 ELSE 0 END)   AS female,
  SUM(CASE WHEN [name].[gender] = 'm' THEN 1 ELSE 0 END)   AS male
FROM
  dim_age_band
LEFT JOIN
  name
    ON  [name].[birth_date] <= DATEADD(YYYY, GetDate(), [dim_age_band].[bound_lower])
    AND [name].[birth_date] >  DATEADD(YYYY, GetDate(), [dim_age_band].[bound_Upper])
    AND [name].[STATUS]      = 'A'
    AND [name].MEMBER_TYPE  IN ('MM')
GROUP BY
  [dim_age_band].[id]
ORDER BY
  MAX([dim_age_band].[ordinal])
;

Inline View version:

SELECT
  MAX([dim_age_band].[name]                            )   AS age_band_name,
  SUM(CASE WHEN [name].[gender] = 'f' THEN 1 ELSE 0 END)   AS female,
  SUM(CASE WHEN [name].[gender] = 'm' THEN 1 ELSE 0 END)   AS male
FROM
(
            SELECT 'Under 30' AS name, 1 AS ordinal,  0 AS bound_lower,   30 AS bound_upper
  UNION ALL SELECT '30-39'    AS name, 2 AS ordinal, 30 AS bound_lower,   40 AS bound_upper
  UNION ALL SELECT '40-49'    AS name, 3 AS ordinal, 40 AS bound_lower,   50 AS bound_upper
  UNION ALL SELECT '50-65'    AS name, 4 AS ordinal, 50 AS bound_lower,   66 AS bound_upper
  UNION ALL SELECT 'Over 65'  AS name, 5 AS ordinal, 66 AS bound_lower, 1000 AS bound_upper
)
  AS dim_age_band
LEFT JOIN
  name
    ON  [name].[birth_date] <= DATEADD(YYYY, GetDate(), [dim_age_band].[bound_lower])
    AND [name].[birth_date] >  DATEADD(YYYY, GetDate(), [dim_age_band].[bound_Upper])
    AND [name].[STATUS]      = 'A'
    AND [name].MEMBER_TYPE  IN ('MM')
GROUP BY
  [dim_age_band].[id]
ORDER BY
  MAX([dim_age_band].[ordinal])
;

2 Comments

i cant create any tables unfortunately in our environment
@RyanGillooly - Then use an inline view, or CTE.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.