very frustrated with the mysql count function - mysql

I have racked my brain for two days on this. I need to count the members are in each age group, but cannot figure out how. I have made it this far, but this only shows the age groups of members but does not count them.
SELECT
CASE
WHEN YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) BETWEEN 12 AND 18 THEN '12-18'
WHEN YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) BETWEEN 19 AND 55 then '19-55'
ELSE '56 or older'
END AS age_group
FROM members
ORDER BY age_group

You have already extracted the data you need. To count it, embed it as a sub query and count the results for each group via a group by clause. By adding the unique identifier for each member in the sub query, it allows the counts of the members in each group to survive the group by (ie. Not get collapsed into a single row per group).
SELECT age_group, COUNT(*) AS group_count
FROM
(SELECT
- - add a unique identifier for each member here as a select attribute,
CASE
WHEN YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) BETWEEN 12 AND 18 THEN '12-18'
WHEN YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) BETWEEN 19 AND 55 then '19-55' ELSE '56 or older' END AS age_group
FROM members) AS tab_age_groups
GROUP BY age_group
ORDER BY age_group;

You need to tell the query to perform a count. You also need to group by the field you want counted.
Try this:
select count(*) as the_count,
case
when YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) between 12 AND 18 THEN '12-18'
when YEAR(CURRENT_DATE()) - YEAR(birth_date) - (RIGHT(CURRENT_DATE(), 5) < RIGHT (birth_date, 5)) between 19 AND 55 then '19-55'
else '56 or older'
end
as age_group
from members group by age_group order by age_group;
Click the link below for a running demo:
SQLFiddle

Related

SQL counting days from periods

My problem is that I want to sum periods of date from only may, but as you can see below some of events starts before first day of may and some end after last may day.
There is my code:
SELECT * FROM rooms p, bookings r WHERE p.id_room = r.id_room group by
r.id_room having
case
WHEN (month(r.start_date) = 5 AND month(r.end_date) = 5) THEN
sum(datediff(r.end_date, r.start_date)) < 21
WHEN (month(r.start_date) < 5 AND month(r.end_date) = 5) THEN
sum(datediff(r.end_date, '2022-05-01 12:00:00')) < 21
WHEN (month(r.start_date) = 5 AND month(r.end_date) > 5) THEN
sum(datediff('2022-05-31 12:00:00', r.start_date)) < 21
END;
Edit 1
I will only talk about example on bottom.
E.g.
date_Start - June 3
date_end - June 15
GREATEST(date_start, '2022-05-1') returns June 3
LEAST(date_end, '2022-05-31') retruns may 31
DATEDIFF(date_end, date_start) returns -3 and it is still counted as day from may while it should be skipped
Simplify the HAVING clause by using the functions LEAST() and GREATEST():
SELECT r.id_room
FROM rooms r LEFT JOIN bookings b
ON b.id_room = r.id_room
AND b.end_date > '2022-05-01 12:00:00' AND b.start_date < '2022-05-31 12:00:00'
GROUP BY r.id_room
HAVING SUM(COALESCE(DATEDIFF(
LEAST(b.end_date, '2022-05-31 12:00:00'),
GREATEST(b.start_date, '2022-05-01 12:00:00')
), 0)) < 21;
Also, use a proper join.

How to use multiple group by conditions in mysql

I have a customer_master table. In that table I have two columns called customer_id and date_of_birth.
what I want is get count of customers group by their age ranger. Something like this.
So far this is the only query I could try.
select COUNT(customer_id) AS count FROM customer_master
WHERE (DATEDIFF( CURDATE(),date_of_birth) / 365.25)<40
Please help me out with this. Thank you.
With everyone's help I found a perfect answer than you all.
SELECT CASE
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 20 THEN 'Below 20'
WHEN(DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 30 THEN 'Below 30'
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 40 THEN 'Below 40'
WHEN (DATEDIFF( CURDATE(),STR_TO_DATE(date_of_birth, '%Y-%m-%d')) / 365) <= 50 THEN 'Below 50'
ELSE 'Over 50'
END as age_group,
COUNT(customer_id)
FROM customer_master
GROUP BY age_group;
You can use the CASE operator.
SELECT CASE
WHEN (DATEDIFF( CURDATE(),date_of_birth) / 365.25) < 40 THEN 'Below 40'
ELSE 'Over 40'
END as age_group,
COUNT(customer_id)
FROM customer_master
GROUP BY age_group;
Sorry for poor formatting, it is my first answer
One solution would be to use cascading values with CASE within a subquery:
select age_group, count(customer_id) as 'count' from
(select customer_id,
year(curdate())-year(date_of_birth) as 'age',
case when (year(curdate())-year(date_of_birth)) < 20, "Below 20"
when (year(curdate())-year(date_of_birth)) < 30, "Between 20 and 29"
when (year(curdate())-year(date_of_birth)) < 40, "Between 30 and 39"
else "40 or Greater" end as 'age_group'
FROM customer_master) x
group by age_group
SELECT
(year(curdate())-year(date_of_birth)) div 20 as age_group,
COUNT(customer_id)
FROM
customer_master
GROUP BY age_group
Something like this will give you number of customers in every 20. If you want different size of group just change the number you divide by.
That is assuming you want each group to be same size e.g
1 - 20
21 - 40
41 - 60
...
If you want different sizes go with CASE solution as other have suggested.

Group by Case Statement MySQL

I am trying to group by the CASE statement but not having much luck. I have an orders table that I am trying to group the orders by total value for the month and categorise them based on their value.
SELECT
CASE
WHEN sum(order_total_price) IS NULL
THEN 'Unknown'
WHEN sum(order_total_price) <= 1000
THEN 'Not more than 1,000'
WHEN sum(order_total_price) <= 2000
THEN 'Between 1,001 and 2000'
WHEN sum(order_total_price) <= 3000
THEN 'Between 2001 and 3000'
WHEN sum(order_total_price) <= 4000
THEN 'Between 3001 and 4000'
WHEN sum(order_total_price) <= 5000
THEN 'Between 4001 and 5000'
ELSE 'Over 5000'
END
AS total_sales,
COUNT(*) as total
FROM orders
WHERE YEAR(order_time)=2014 and MONTH(order_time)=07
GROUP BY total_sales
The shorthand for this is
....
GROUP BY 1
Using the output column position. Otherwise you'd have to repeat the entire case block in the GROUP BY clause.
Your query should be returning a single row. Luck doesn't have anything do with it. What matters is the specification. And apart from the SQL query, it's not clear what resultset you want returned.
I'm guessing (and this is just a guess) that you want to return multiple rows, one for each of the order size categories derived by the CASE expression.
Perhaps the resultset returned by a query something like this:
SELECT CASE
WHEN o.order_total_price IS NULL
THEN 'Unknown'
WHEN o.order_total_price <= 1000
THEN 'Not more than 1,000'
WHEN o.order_total_price <= 2000
THEN 'Between 1,001 and 2000'
WHEN o.order_total_price <= 3000
THEN 'Between 2001 and 3000'
WHEN o.order_total_price <= 4000
THEN 'Between 3001 and 4000'
WHEN o.order_total_price <= 5000
THEN 'Between 4001 and 5000'
ELSE 'Over 5000'
END AS order_value_category
, SUM(o.order_total_price) AS total_sales
, COUNT(*) AS count_sales
FROM orders o
WHERE o.order_time >= '2014-07-01'
AND o.order_time < '2014-07-01' + INTERVAL 1 MONTH
GROUP BY order_value_category
For performance, to enable MySQL to make use of a suitable index to satisfy the predicate on the order_time category, we'd need to reference the "bare" order_time column in a range scan predicate.
Note that the GROUP BY clause implies an ORDER BY clause; if you want the rows returned in order other than by the derived order_value_category column, you'd need to specify a suitable ORDER BY clause.
If you use GROUP BY, output rows are sorted according to the GROUP BY columns as if you had an ORDER BY for the same columns. To avoid the overhead of sorting that GROUP BY produces, add ORDER BY NULL:
SELECT a, COUNT(b) FROM test_table GROUP BY a ORDER BY NULL;
Relying on implicit GROUP BY sorting in MySQL 5.6 is deprecated. To achieve a specific sort order of grouped results, it is preferable to use an explicit ORDER BY clause. GROUP BY sorting is a MySQL extension that may change in a future release; for example, to make it possible for the optimizer to order groupings in whatever manner it deems most efficient and to avoid the sorting overhead.
http://academy.comingweek.com/sql-groupby-clause/
As Jim Garrison said,
GROUP BY 1
would be best.
As an alternative, below will also work if you are not comfortable with numbers in group by. Put your original query without GROUP BY as inner query. Use the count and group by in an outer query.
SELECT A.TOTAL_SALES, COUNT(1) FROM
(SELECT
CASE
WHEN sum(order_total_price) IS NULL
THEN 'Unknown'
WHEN sum(order_total_price) <= 1000
THEN 'Not more than 1,000'
WHEN sum(order_total_price) <= 2000
THEN 'Between 1,001 and 2000'
WHEN sum(order_total_price) <= 3000
THEN 'Between 2001 and 3000'
WHEN sum(order_total_price) <= 4000
THEN 'Between 3001 and 4000'
WHEN sum(order_total_price) <= 5000
THEN 'Between 4001 and 5000'
ELSE 'Over 5000'
END
AS total_sales
FROM orders
WHERE YEAR(order_time)=2014 and MONTH(order_time)=07
) AS A
GROUP BY A.total_sales

Sort date by day before and by night after in one query

(first : I'm french and I'm sorry if I make some grammaticals faults...)
I have a table with TV programs. I want, in one query, to search the programs in this table and to sort the results with all the programs of the day before the programs of the night.
I have an input name fullDateStart with the date in DATETIME format for extract HOUR().
I use a LEFT JOIN in my research. Here my actual request :
SELECT programId, programTitle, COUNT(*) AS score,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS a
LEFT JOIN programsTable AS b ON a.programId = b.program_id
WHERE peopleId = :id AND timestamp > :twoWeeksAgo AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
ORDER BY score DESC, start DESC
LIMIT 0, 10
Here my try with UNION :
SELECT * FROM (
SELECT fullDateStart, programId, programTitle, COUNT(*) AS score1,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS a
LEFT JOIN db.epg_programs AS b ON a.programId = b.program_id
WHERE HOUR(fullDateStart) > 6 AND HOUR(fullDateStart) <= 23
AND peopleId = 826 AND timestamp > 1353420511000 AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
UNION
SELECT fullDateStart, programId, programTitle, COUNT(*) AS score2,
ROUND(startDate / 1000) AS start, ROUND(endDate / 1000) AS end
FROM people_appearances AS c
LEFT JOIN db.epg_programs AS d ON c.programId =d.program_id
WHERE HOUR(fullDateStart) >= 0 AND HOUR(fullDateStart) <= 6
AND peopleId = 826 AND timestamp > 1353420511000 AND programId != 0
AND redif = 0 AND channel_id IN(1,2,3,5,6,7,8,9)
GROUP BY programId
) AS s3
ORDER BY score1 DESC,start DESC
LIMIT 0, 10
Is somebody can help me please ? (I try with a Union with two request [one for the day, one for the night] but I don't succeed to sort the results, even if they was in two requests...)
The issue in your query is the order by. You are ordering everything first by the score, then by the start date.
If the goal is to keep everything in one day, then do something like:
order by score desc, date(fulldatestart),
(case when hour(fulldatestart) between 7 and 23 then 1
else 2
end),
hour(fulldatestart)
I added a third clause for the order by, so programs with the same score are ordered by hour.
If you want the early morning hours associated with the previous day, then you need to do something like:
order by score desc, date(fulldatestart - interval 7 hour),
hour(fulldatestart - interval 7 hour)
(once you subtract 7 hours, you can keep things in the order by hour.)

MySQL: group by date RANGE?

OK I have this query that groups 2 columns together quite nicely:
SELECT search_query_keyword, search_query_date, COUNT(1) as count
FROM search_queries
WHERE search_query_date >= '.$from.' AND search_query_date <= '.$to.'
GROUP BY search_query_keyword, search_query_date
ORDER BY count DESC
LIMIT 10
But what if I want to group by a date RANGE instead of just a date? Is there a way to do that?
Thanks!
EDIT: OK these answers are pretty complicated and I think what I want can be acheived a lot easier so let me re-explain. I want to select keywords over a time period ">= 20090601 AND <= 20090604" for example. But instead of getting repeated keywords I would rather just get the keyword ounce and how many times it occured. So for example instead of this:
keyword: foo
keyword: foo
keyword: foo
keyword: bar
keyword: bar
I would get:
keyword: foo, count: 3
keyword: bar, count: 2
I'm not exactly sure about the date range grouping -- you'd have to define the date ranging that you would want and then maybe you could UNION those queries:
SELECT
'Range 1' AS 'date_range',
search_query_keyword
FROM search_queries
WHERE search_query_date >= '.$fromRange1.' AND search_query_date <= '.$toRange1.'
UNION
SELECT
'Range 2' AS 'date_range',
search_query_keyword
FROM search_queries
WHERE search_query_date >= '.$fromRange2.' AND search_query_date <= '.$toRange2.'
GROUP BY 1,2
Or if you wanted to put them within a grouping of how many days old like "30 days, 60 days, etc" you could do this:
SELECT
(DATEDIFF(search_query_date, NOW()) / 30) AS date_group,
search_query_keyword
FROM search_queries
GROUP BY date_group, search_query_keyword
EDIT: Based on the further information you provided, this query should produce what you want:
SELECT
search_query_keyword,
COUNT(search_query_keyword) AS keyword_count
FROM search_queries
WHERE search_query_date >= '.$from.' AND search_query_date <= '.$to.'
GROUP BY search_query_keyword
You could group on a CASE statement or on the result of a function. For instance:
SELECT search_query_keyword, QUARTER(search_query_date), COUNT(1) as count
FROM search_queries
WHERE search_query_date >= '.$from.' AND search_query_date <= '.$to.'
GROUP BY search_query_keyword, QUARTER(search_query_date)
ORDER BY count DESC
look into the different DATE-based functions and build based on that, such as
select YEAR( of your date ) + MONTH( of your date ) as ByYrMonth
but the result in above case would need to be converted to character to prevent a year of 2009 + January ( month 1) = 2010 also getting falsely grouped with 2008 + February (month 2 ) = 2010, etc... Your string should end up as something like:
...
200811
200812
200901
200902
200903
...
If you wanted by calendar Quarters, you would have to do a INTEGER of the (month -1) divided by 4 so...
Jan (-1) = 0 / 4 = 0
Feb (-1) = 1 / 4 = 0
Mar (-1) = 2 / 4 = 0
Apr (-1) = 3 / 4 = 0
May (-1) = 4 / 4 = 1
June (-1)= 5 / 4 = 1 ... etc...
Yes, a previous example explicitly reference the QUARTER() function that handles more nicely, but if also doing based on aging, such as 30, 60, 90 days, you could apply the similar math above but divide by 30 for your groups.