MySQL Join Query on age range - mysql

I have 3 table that I am working with that contain what I need for my query. I need to return age groups for a given sale date range and display the profits for each. Here is the query that I am which is returning an error:
#1241 - Operand should contain 1 column(s)
I can't seem to wrap my head around this one.
SELECT DealInfo.DeliveryDate, Coalesce(SUM(Commissions.GrossProfit),0) as GP, CASE
WHEN age < 16 THEN 'Under 16'
WHEN age BETWEEN 16 and 20 THEN '16 - 20'
WHEN age BETWEEN 21 and 25 THEN '21 - 25'
WHEN age BETWEEN 26 and 30 THEN '26 - 30'
WHEN age BETWEEN 31 and 35 THEN '31 - 35'
WHEN age BETWEEN 36 and 40 THEN '36 - 40'
WHEN age BETWEEN 41 and 45 THEN '41 - 45'
WHEN age BETWEEN 46 and 50 THEN '46 - 50'
WHEN age BETWEEN 51 and 55 THEN '51 - 55'
WHEN age > 55 THEN 'Over 55'
WHEN age IS NULL THEN 'NULL' END as age_range,
COUNT(*) AS RC FROM (SELECT TIMESTAMPDIFF(YEAR, dob, CURDATE()) AS age
FROM customers WHERE Customers.dob<>'0000-00-00' and Customers.server_id = '0000001412') as derived
GROUP BY age_range
ORDER BY age_range ASC
from DealInfo
LEFT JOIN Commissions on Commissions.Deal_ID=DealInfo.Deal_Number and Commissions.server_id=DealInfo.server_id
WHERE DealInfo.server_id = '0000001412' and DealInfo.Status=2 and Coalesce(Commissions.GrossProfit,0)>0 and
dealinfo.DeliveryDate BETWEEN '2017-01-01' AND '2017-12-31'

SELECT DealInfo.DeliveryDate,
Coalesce(SUM(Commissions.GrossProfit),0) as GP,
(SELECT CASE WHEN age < 16 THEN 'Under 16'
WHEN age BETWEEN 16 and 20 THEN '16 - 20'
WHEN age BETWEEN 21 and 25 THEN '21 - 25'
WHEN age BETWEEN 26 and 30 THEN '26 - 30'
WHEN age BETWEEN 31 and 35 THEN '31 - 35'
WHEN age BETWEEN 36 and 40 THEN '36 - 40'
WHEN age BETWEEN 41 and 45 THEN '41 - 45'
WHEN age BETWEEN 46 and 50 THEN '46 - 50'
WHEN age BETWEEN 51 and 55 THEN '51 - 55'
WHEN age > 55 THEN 'Over 55'
WHEN age IS NULL THEN 'NULL' END as age_range,
COUNT(*) AS RC
FROM (SELECT TIMESTAMPDIFF(YEAR, dob, CURDATE()) AS age
FROM customers
WHERE Customers.dob<>'0000-00-00'
and Customers.server_id = '0000001412') as derived
GROUP BY age_range
ORDER BY age_range ASC)
from DealInfo
LEFT JOIN Commissions
on Commissions.Deal_ID = DealInfo.Deal_Number
and Commissions.server_id = DealInfo.server_id
WHERE DealInfo.server_id = '0000001412'
and DealInfo.Status=2
and Coalesce(Commissions.GrossProfit,0) > 0
Your scalar sub-query:
(SELECT CASE WHEN age < 16 THEN 'Under 16'
WHEN age BETWEEN 16 and 20 THEN '16 - 20'
WHEN age BETWEEN 21 and 25 THEN '21 - 25'
WHEN age BETWEEN 26 and 30 THEN '26 - 30'
WHEN age BETWEEN 31 and 35 THEN '31 - 35'
WHEN age BETWEEN 36 and 40 THEN '36 - 40'
WHEN age BETWEEN 41 and 45 THEN '41 - 45'
WHEN age BETWEEN 46 and 50 THEN '46 - 50'
WHEN age BETWEEN 51 and 55 THEN '51 - 55'
WHEN age > 55 THEN 'Over 55'
WHEN age IS NULL THEN 'NULL' END as age_range,
COUNT(*) AS RC
FROM (SELECT TIMESTAMPDIFF(YEAR, dob, CURDATE()) AS age
FROM customers
WHERE Customers.dob<>'0000-00-00'
and Customers.server_id = '0000001412') as derived
GROUP BY age_range
ORDER BY age_range ASC)
returns 2 columns, it should return only one column.

Related

Mysql add an extra column at end instead of using a union

I am trying to combine two queries so the data shows up in one table. I am using a union to combine the two queries. However, everything is added to the same column, what do I change so the results from the different queries take up a new column.
Here is an image of the query result.
Here is my code
select * from(
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
join subscriptions on users.id = subscriptions.user_id
where users.plan <> 'domain' and users.plan <> '' and users.plan <> 'domain_cpi' and users.birthdate_on is not null
) as derived
GROUP BY age_range
union
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range2,
COUNT(*) AS count2,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users) as derived2
GROUP BY age_range2
) as test2
ORDER BY ordinal
I want the result so only one under 25 shows, but the two results for under 25 493 and 2046 are in different columns. Same for all other ranges
Sounds like you want to put a JOIN to derived.age_range ON test2.age_range2
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal,
count, count2
FROM (
SELECT
derived.age,
COUNT(*) AS count
FROM (
SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
join subscriptions on users.id = subscriptions.user_id
where users.plan <> 'domain' and users.plan <> '' and users.plan <> 'domain_cpi' and users.birthdate_on is not null
GROUP BY age
) as derived
JOIN
SELECT
derived2.age,
COUNT(*) AS count2
FROM (
SELECT TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age FROM users
GROUP BY age
) as derived2
ON derived.age = derived2.age
)
ORDER BY ordinal ASC;
I don't believe you need 2 queries just a left join instead. The count() function ONLY increments for non-null values so you can have users counted even if they don't meet the subscription criteria.
SELECT
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range
, CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
, COUNT(DISTINCT id) AS user_count # distinct might not be needed
, COUNT(subscriber_id) AS subscriber_count
FROM (
SELECT
users.id
, TIMESTAMPDIFF(YEAR, users.birthdate_on, CURDATE()) AS age
, subscriptions.user_id AS subscriber_id
FROM users
LEFT JOIN subscriptions ON users.id = subscriptions.user_id
AND users.plan <> 'domain'
AND users.plan <> ''
AND users.plan <> 'domain_cpi'
AND users.birthdate_on IS NOT NULL
) d
GROUP BY
CASE
WHEN age BETWEEN 18 and 25 THEN 'Under 25'
WHEN age BETWEEN 25 and 40 THEN '25 - 40'
WHEN age >= 40 THEN 'Over 40'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END
, CASE
WHEN age between 18 and 25 THEN 1
WHEN age BETWEEN 25 and 40 THEN 2
WHEN age >= 40 THEN 8
WHEN age IS NULL THEN 9
END

count a grouped by and cased mysql

Im trying to get the age of each person by computing on their birthday. Now I need to group them which I already achieved. Now what I want is to count how many in group 20 and below and so forth and so on.
Here's my query
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group from members
The result is
Where I tried to group it by the query below
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group from members group by age_group
Result will be
But what I need is somethinglike this
**NOte: Photo edited.
How do I achieved that I need to place the count result in a chart.
Wrap the query similar to this:
Select age_group, count(*)
From (
// your original query here
) t
Group by age_group
Just add a count(*) to your last query:
select case when datediff(now(), birthday) / 365.25 > 50 then '51 & over' when datediff(now(), birthday) / 365.25 > 30 then '31 - 50' when datediff(now(), birthday) / 365.25 > 19 then '20 - 30' else 'under 20' end as age_group, count(*) from members group by age_group
SELECT
concat(
datediff(now(), birthday)/365.25 div 20 * 20 + 1,
'-',
(datediff(now(), birthday)/365.25 div 20 + 1) * 20) AS age,
count(*)
FROM members
GROUP BY datediff(now(), birthday)/365.25 DIV 20
This query will give the result in the following format
+-------+----------+
| age | count(*) |
+-------+----------+
| 1-20 | 2 |
| 21-40 | 1 |
| 41-60 | 1 |
+-------+----------+
Based on the following table definition and sample data:
CREATE TABLE `stack` (
`id` int(6) NOT NULL AUTO_INCREMENT,
`birthdate` date NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
insert into members values (null,'1973-10-02');
insert into members values (null,'1977-04-07');
insert into members values (null,'2006-07-03');
insert into members values (null,'2011-10-11');

Using case when mysql

I would like to query a total marks column using case and get the marks respective grades and store it as a separate column. I have been trying for a while now, but it keeps giving me errors.
SELECT gradecalc.StudentId,gradecalc.CourseCode, Total,
CASE TOTAL
WHEN >90 THEN 'AA'
WHEN <90 AND >85 THEN 'BA'
WHEN <85 AND >=80 THEN 'BB'
WHEN <80 AND >=75 THEN 'CB'
WHEN <75 AND >=70 THEN 'CC'
WHEN <70 AND >=65 THEN 'DC'
WHEN <65 AND >=60 THEN 'DD'
ELSE 'FF'
END AS Grade
FROM GRADECALC GROUP BY gradecalc.StudentId,gradecalc.CourseCode
The case <column> when <value> syntax can only handle equality checks. For other operators, your'd have to use a slightly different syntax:
SELECT gradecalc.StudentId,gradecalc.CourseCode, Total,
CASE
WHEN total > 90 THEN 'AA'
WHEN total < 90 AND total >= 85 THEN 'BA'
WHEN total < 85 AND total >= 80 THEN 'BB'
WHEN total < 80 AND total >=75 THEN 'CB'
WHEN total < 75 AND total >=70 THEN 'CC'
WHEN total < 70 AND total >=65 THEN 'DC'
WHEN total < 65 AND total >=60 THEN 'DD'
ELSE 'FF'
END AS Grade
FROM GRADECALC GROUP BY gradecalc.StudentId,gradecalc.CourseCode
You need to put the full expression in the case when. Your syntax of case <variable> when <constant> only works for constants.
Now, you can also simplify the expressions, because case is processed in order. So this is equivalent logic:
SELECT gc.StudentId, gc.CourseCode, gc.Total,
(CASE WHEN TOTAL > 90 THEN 'AA'
WHEN TOTAL > 85 THEN 'BA'
WHEN TOTAL >= 80 THEN 'BB'
WHEN TOTAL >= 75 THEN 'CB'
WHEN TOTAL >= 70 THEN 'CC'
WHEN TOTAL >= 65 THEN 'DC'
WHEN TOTAL >= 60 THEN 'DD'
ELSE 'FF'
END) AS Grade
FROM GRADECALC gc
GROUP BY gc.StudentId, gc.CourseCode ;
I don't think the GROUP BY is necessary, unless you have multiple rows for a given student and course. If you do, then you probably want AVG(Total) or something like that in the case statement.
With this formulation, you can readily see that "90" and "85" are suspicious. In fact, your original logic would give students with these marks an "FF", which I'm guessing is not the intention.
So:
SELECT gc.StudentId, gc.CourseCode, gc.Total,
(CASE WHEN TOTAL >= 90 THEN 'AA'
WHEN TOTAL >= 85 THEN 'BA'
WHEN TOTAL >= 80 THEN 'BB'
WHEN TOTAL >= 75 THEN 'CB'
WHEN TOTAL >= 70 THEN 'CC'
WHEN TOTAL >= 65 THEN 'DC'
WHEN TOTAL >= 60 THEN 'DD'
ELSE 'FF'
END) AS Grade
FROM GRADECALC gc;
You can try this, mate:
SELECT
gc.StudentId,
gc.CourseCode,
gc.Total,
CASE
WHEN gc.Total > 90 THEN 'AA'
WHEN gc.Total BETWEEN 85 AND 90 THEN 'BA'
WHEN gc.Total BETWEEN 80 AND 85 THEN 'BB'
WHEN gc.Total BETWEEN 75 AND 80 THEN 'CB'
WHEN gc.Total BETWEEN 70 AND 75 THEN 'CC'
WHEN gc.Total BETWEEN 65 AND 70 THEN 'DC'
WHEN gc.Total BETWEEN 60 AND 65 THEN 'DD'
ELSE 'FF'
END AS Grade
FROM
gradecalc gc
GROUP BY
gc.StudentId, gc.CourseCode;

Give every "empty" age band 0

I have a query like the following:
SELECT
COUNT(*) AS `members`,
CASE
WHEN age >= 10 AND age <= 20 THEN '10-20'
WHEN age >=21 AND age <=30 THEN '21-30'
WHEN age >=31 AND age <=40 THEN '31-40'
WHEN age >=41 AND age <= 50 THEN '41-50'
WHEN age >=51 AND age <=60 THEN '51-60'
WHEN age >=61 AND age <=70 THEN '61-70'
WHEN age >= 71 THEN '71+'
END AS ageband
FROM `members`
GROUP BY ageband
Retrieves
How do I populate the empty age bands with 0?
This is what I am looking to achieve from my query above:
members ageband
1 10-20
0 21-30
2 31-40
0 41-50
1 51-60
0 61-70
1 71+
There are NO member in range of 41-50, hence I put 0.
The rows you wish for are not displayed because there's no data for them. Outer join (left or right) all possibilities and they will be displayed:
SELECT
COUNT(members.whatever_column_best_would_be_primary_key) AS `members`,
ages.age_range
FROM `members`
RIGHT JOIN (
SELECT '10-20' as age_range
UNION ALL
SELECT '21-30'
UNION ALL
SELECT '31-40'
UNION ALL
SELECT '41-50'
UNION ALL
SELECT '51-60'
UNION ALL
SELECT '61-70'
UNION ALL
SELECT '71+'
)ages ON ages.age_range = CASE
WHEN members.age >= 10 AND age <= 20 THEN '10-20'
WHEN members.age >=21 AND age <=30 THEN '21-30'
WHEN members.age >=31 AND age <=40 THEN '31-40'
WHEN members.age >=41 AND age <= 50 THEN '41-50'
WHEN members.age >=51 AND age <=60 THEN '51-60'
WHEN members.age >=61 AND age <=70 THEN '61-70'
WHEN members.age >= 71 THEN '71+'
END
GROUP BY ages.age_range
I don't know if I got your question right. Did you miss the part with ELSE in the manual?
SELECT
COUNT(*) AS `members`,
CASE
WHEN age >= 10 AND age <= 20 THEN '10-20'
WHEN age >=21 AND age <=30 THEN '21-30'
WHEN age >=31 AND age <=40 THEN '31-40'
WHEN age >=41 AND age <= 50 THEN '41-50'
WHEN age >=51 AND age <=60 THEN '51-60'
WHEN age >=61 AND age <=70 THEN '61-70'
WHEN age >= 71 THEN '71+'
ELSE '0'
END AS ageband
FROM `members`
GROUP BY ageband
you may use coalsce !
try this
SELECT
COALESCE(COUNT(*),0) AS `members`,
CASE
WHEN age >= 10 AND age <= 20 THEN '10-20'
WHEN age >=21 AND age <=30 THEN '21-30'
WHEN age >=31 AND age <=40 THEN '31-40'
WHEN age >=41 AND age <= 50 THEN '41-50'
WHEN age >=51 AND age <=60 THEN '51-60'
WHEN age >=61 AND age <=70 THEN '61-70'
WHEN age >= 71 THEN '71+'
END AS ageband
FROM `members`
GROUP BY ageband
You can first build a table containing your age bands. Say CREATE TABLE ages(ageband text);:
min_age | max_age | ageband
---------------------------
10 | 20 | 10-20
21 | 30 | 21-30
31 | 40 | 31-40
41 | 50 | 41-50
51 | 60 | 51-60
61 | 70 | 61-70
71 | 99 | 71+
Then you FULL OUTER JOIN:
SELECT
COUNT(members.*) AS `members`,
ageband
FROM
`members`
FULL OUTER JOIN `ages` ON
age BETWEEN min_age AND max_age
GROUP BY ageband;
(You can't use COUNT(*) but have to be more specific and use COUNT(members.*) because there will be rows with an empty ageband and NULL values coming from members and you want to count them as 0. COUNT() counts non-NULL items only.)
(Furthermore, the advantage of FULL OUTER JOIN over RIGHT JOIN is that if you have an age not covered, such as 8, 20.5, 101 or text, it will still be listed with an ageband = NULL so you see the problem.)
even #FancyPants has been provided solution, you can also try below query in different way:
SELECT IFNULL(cnt,0) AS 'members', ages.age_range AS 'ageband' FROM (
SELECT '10-20' AS age_range
UNION ALL
SELECT '21-30'
UNION ALL
SELECT '31-40'
UNION ALL
SELECT '41-50'
UNION ALL
SELECT '51-60'
UNION ALL
SELECT '61-70'
UNION ALL
SELECT '71+') AS ages
LEFT JOIN
(SELECT COUNT(*) AS cnt,
CASE WHEN age>=10 AND age<=20 THEN '10-20'
WHEN age>=21 AND age<=30 THEN '21-30'
WHEN age>=31 AND age<=40 THEN '31-40'
WHEN age>=41 AND age<=50 THEN '41-50'
WHEN age>=51 AND age<=60 THEN '51-60'
WHEN age>=61 AND age<=70 THEN '61-70'
WHEN age>=71 THEN '71+' END age_range
FROM members GROUP BY age_range) a
ON a.age_range=ages.age_range

MySQL GROUP BY age range including null ranges

I'm trying to count the number of people by age ranges, and I can almost do it with 2 problems:
If there are no people in a given age range (NULL), then that age range does not appear in the results. For example, in my data there's no entries for "Over 80" so that date range does not appear. Basically, it looks like a mistake in the programming when there are missing date ranges.
I'd like to order the results in a specific way. In the query below, because the ORDER BY is by age_range, the results for '20 - 29' come before the results for 'Under 20'.
Here's a sample of the db table "inquiries":
inquiry_id birth_date
1 1960-02-01
2 1962-03-04
3 1970-03-08
4 1980-03-02
5 1990-02-08
Here's the query:
SELECT
CASE
WHEN age < 20 THEN 'Under 20'
WHEN age BETWEEN 20 and 29 THEN '20 - 29'
WHEN age BETWEEN 30 and 39 THEN '30 - 39'
WHEN age BETWEEN 40 and 49 THEN '40 - 49'
WHEN age BETWEEN 50 and 59 THEN '50 - 59'
WHEN age BETWEEN 60 and 69 THEN '60 - 69'
WHEN age BETWEEN 70 and 79 THEN '70 - 79'
WHEN age >= 80 THEN 'Over 80'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
GROUP BY age_range
ORDER BY age_range
Here's a simple solution based on the suggestion by Wrikken:
SELECT
SUM(IF(age < 20,1,0)) as 'Under 20',
SUM(IF(age BETWEEN 20 and 29,1,0)) as '20 - 29',
SUM(IF(age BETWEEN 30 and 39,1,0)) as '30 - 39',
SUM(IF(age BETWEEN 40 and 49,1,0)) as '40 - 49',
SUM(IF(age BETWEEN 50 and 59,1,0)) as '50 - 59',
SUM(IF(age BETWEEN 60 and 69,1,0)) as '60 - 69',
SUM(IF(age BETWEEN 70 and 79,1,0)) as '70 - 79',
SUM(IF(age >=80, 1, 0)) as 'Over 80',
SUM(IF(age IS NULL, 1, 0)) as 'Not Filled In (NULL)'
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
An alternative to the range table (which has my preference), a single-row answer could be:
SELECT
SUM(IF(age < 20,1,0)) as 'Under 20',
SUM(IF(age BETWEEN 20 and 29,1,0)) as '20 - 29',
SUM(IF(age BETWEEN 30 and 39,1,0)) as '30 - 39',
SUM(IF(age BETWEEN 40 and 49,1,0)) as '40 - 49',
...etc.
FROM inquiries;
One way of ordering the results would be introducing a column in the select statement and giving it a rank value of the way you want your results to be ordered with the rest and then order by that row, for example
SELECT
CASE
WHEN age < 20 THEN 'Under 20'
WHEN age BETWEEN 20 and 29 THEN '20 - 29'
WHEN age BETWEEN 30 and 39 THEN '30 - 39'
WHEN age BETWEEN 40 and 49 THEN '40 - 49'
WHEN age BETWEEN 50 and 59 THEN '50 - 59'
WHEN age BETWEEN 60 and 69 THEN '60 - 69'
WHEN age BETWEEN 70 and 79 THEN '70 - 79'
WHEN age >= 80 THEN 'Over 80'
WHEN age IS NULL THEN 'Not Filled In (NULL)'
END as age_range,
COUNT(*) AS count,
CASE
WHEN age < 20 THEN 1
WHEN age BETWEEN 20 and 29 THEN 2
WHEN age BETWEEN 30 and 39 THEN 3
WHEN age BETWEEN 40 and 49 THEN 4
WHEN age BETWEEN 50 and 59 THEN 5
WHEN age BETWEEN 60 and 69 THEN 6
WHEN age BETWEEN 70 and 79 THEN 7
WHEN age >= 80 THEN 8
WHEN age IS NULL THEN 9
END as ordinal
FROM (SELECT TIMESTAMPDIFF(YEAR, birth_date, CURDATE()) AS age FROM inquiries) as derived
GROUP BY age_range
ORDER BY ordinal
Create a table that contains all ranges and use outer join.
Order by numeric value in another column of that table
SELECT range, ....
FROM ranges
LEFT JOIN (Your subquery) ON (ranges.range = your_range)
...
ORDER BY range.year ASC