I need to fill month missing in this query, and set the missing one at 0 elements :)
SELECT MONTH(timestamp), YEAR(timestamp), count(*) as Total
FROM dialogs
WHERE timestamp BETWEEN '2021-09-01' AND '2023-01-01'
GROUP BY YEAR(timestamp), MONTH(timestamp);
the result is this:
7,2022,354
8,2022,4715
9,2022,2712
10,2022,1740
but i'm expect something like:
6,2022,0 // <--- missing month in table
7,2022,354
8,2022,4715
9,2022,2712
10,2022,1740
11,2022,0 // <--- missing month in table
and so on..
you will need a full calendar
WITH recursive years AS (
select 2010 as yr
union all
select yr + 1
from years
where yr <= 2030)
, months AS (
select 1 as mon
union all
select mon + 1
from months
where mon <= 12)
SELECT cal.yr, cal.mon, ifnull(tbl.total,0) total
FROM (SELECT MONTH(timestamp) mon, YEAR(timestamp) yr, count(*) as Total
FROM dialogs
GROUP BY YEAR(timestamp), MONTH(timestamp)) tbl
right join ( select * from years join months ) cal ON cal.mon=tbl.mon and cal.yr=tbl.yr
WHERE cal.yr BETWEEN 2019 AND 2023
order by 1,2;
Related
I have a table called months - this contains all 12 months of the calendar, the IDs correspond to the month number.
I will be running a query to retrieve 2 or 3 sequential months from this table, e.g
April & May
June, July, August
December & January
However I want to ensure that whenever December are January and retrieved, that it retrieves them in that order, and not January - December. Here is what I have tried:
SELECT * FROM `months`
WHERE start_date BETWEEN <date1> AND <date2>
ORDER BY
FIELD(id, 12, 1)
This works for December & January, but now when I try to retrieve January & February it does those in the wrong order, i.e "February - January" - I'm guessing because we specified 1 in the ORDER BY as the last value.
Anybody know the correct way to achieve this? As I mentioned this should also work for 3 months, so for example "November, December, January" and "December, January, February" should all be retrieved in that order.
If you want December first, but the other months in order, then:
order by (id = 12) desc, id
MySQL treats booleans as numbers, with "1" for true and "0" for false. The desc puts the 12s first.
EDIT:
To handle the more general case, you can use window functions. Assuming the numbers are consecutive, then the issue is trickier. This will work for 2 and 3-month spans:
order by (case min(id) over () > 1 then id end),
(case when id > 6 1 else 2 end),
id
I'm reluctant to think about a more general solution based only on months. After all, you can just use:
order by start_date
Or, if you have an aggregation query:
order by min(start_date)
to solve the real problem.
This is not "mysql solution" properly :
with cte (id, month) AS (
select id, month from months
union all
select id, month from months
)
, cte1 (id, month, r) as (select id, month, row_number() over() as r from cte )
select * from cte1
where id in (12, 1)
and r >= 12 order by r limit 2 ;
DECLARE
#monthfrom int = 12,
#monthto int = 1;
with months as (select 1 m
union all
select m+1 from months where m<12)
select m
from months
where m in (#monthfrom,#monthto)
order by
case when #monthfrom>#monthto
then
m%12
else
m
end
result:
12
1
Basically in MySQL this can be done the same way:
set #from =12;
set #to =1;
with recursive months(m) as (
select 1 m
union all
select m+1 from months where m<12)
select *
from months
where m in (#from,#to)
order by case when #from>#to then m%12 else m end;
I have the entries of monthly dues like,
Table name : month_dues,
Columns:
customer_id,
due_date,
due_amount
These table have lot of due entries.
customer_id due_date due_amount
--------------------------------------
1 2018-12-01 100
1 2019-01-01 100
1 2019-02-01 100
1 2019-10-01 100
so, how to select pending due months from these record?
in my table customer 1 not paid dues for these months,
2019-03,2019-04,2019-05,2019-06,2019-07,2019-08, 2019-09
customer pay the due every month so
For select paid dues from table using,
SELECT customer_id, due_date, due_amount FROM month_dues where customer_id='1' where due_date>='2019-01-01' and due_date<='2019-10-18';
How to get pending due month and year using this table?
Which means, find month and year not in this record.
IF you are searching between the two dates
YOu can use this
select * from month_dues
where due_date between '2012-03-11 00:00:00' and '2012-05-11
23:59:00' && customer_id='1'
order by month_dues desc;
If you want to search the date(due_date) lower than today
SELECT * FROM month_dues WHERE due_date < CURDATE();
And you are refeering to the record that is not belong to the query you can find some NOT IN query
Like
`SELECT * FROM month_dues NOT IN ( select * from month_dues
where due_date between '2012-03-11 00:00:00' and '2012-05-11
23:59:00' && customer_id='1'
order by month_dues desc;
)`
So basically you need to validate your table against some kind of calendar, here is a simple solution that only works for the current year, maybe you can use it as a start or someone else could improve it
SELECT m.MONTH
FROM (SELECT 1 AS MONTH
UNION SELECT 2 AS MONTH
UNION SELECT 3 AS MONTH
UNION SELECT 4 AS MONTH
UNION SELECT 5 AS MONTH
UNION SELECT 6 AS MONTH
UNION SELECT 7 AS MONTH
UNION SELECT 8 AS MONTH
UNION SELECT 9 AS MONTH
UNION SELECT 10 AS MONTH
UNION SELECT 11 AS MONTH
UNION SELECT 12 AS MONTH) as m
WHERE m.MONTH NOT IN (SELECT MONTH(due_date)
FROM due_months
WHERE customer_id = 1
AND YEAR(due_date) = YEAR(CURDATE()))
AND m.MONTH < MONTH(CURDATE()) -- needs to be improved as well
I have a table:
year val
---- ----
2013 4
2014 6
2014 2
2014 6
2015 1
2015 3
2016 7
is there a way to get the sum of the previous years for every year in the table?
the result should be like this:
2013 0
2014 4
2015 18
2016 22
2017 29
I tried something like this:
select year, sum(val) from
(select year, val from table ?????)
group by year
There should be an inner join somewhere maybe?
If you want old years only then use this query
SELECT DISTINCT year , ( SELECT SUM(val) FROM table as temp2 WHERE temp2.year < temp1.year ) as v FROM table as temp1
If you want to include year too then change temp2.year < temp1.year to <= , if you want to filter by years , then use comparison =
so
SELECT DISTINCT year , ( SELECT SUM(val) FROM table as temp2 WHERE temp2.year <= temp1.year ) as v FROM table as temp1
and
SELECT DISTINCT year , ( SELECT SUM(val) FROM table as temp2 WHERE temp2.year = temp1.year ) as v FROM table as temp1
but the latest could be easily done without subquery , just selecting year and sum(val) then group by year
Your question is a bit challenging because you want 0 for the first year:
select year,
( (#p := #p + sumval) - sumval) as cumeprev
from (select year, sum(val) as sumval
from table t
group by year
) t cross join
(select #p := 0) params
order by year;
Try
select year, #previous := #previous + sum(val)
from your_table
cross join (select #previous := 0) p
group by year
order by year
I wrote a query to get month-wise record in user table as follows
SELECT COUNT( `userID` ) AS total, DATE_FORMAT( `userRegistredDate` , '%b' ) AS
MONTH , YEAR( `userRegistredDate` ) AS year
FROM `users`
GROUP BY DATE_FORMAT( FROM_UNIXTIME( `userRegistredDate` , '%b' ) )
Output:
total MONTH year
---------------------------
3 May 2013
2 Jul 2013
--------------------------
Expected Output:
total MONTH year
---------------------------
0 Jan 2013
0 Feb 2013
0 Mar 2013
0 Apr 2013
3 May 2013
0 Jun 2013
2 Jul 2013
--------------------------
I need to show the record even if data not exist. How to do this?
I won't say much about efficiency as I have not tested it against other methods but without having a temp table this seem a fair way to go.
SELECT COUNT(u.userID) AS total, m.month
FROM (
SELECT 'Jan' AS MONTH
UNION SELECT 'Feb' AS MONTH
UNION SELECT 'Mar' AS MONTH
UNION SELECT 'Apr' AS MONTH
UNION SELECT 'May' AS MONTH
UNION SELECT 'Jun' AS MONTH
UNION SELECT 'Jul' AS MONTH
UNION SELECT 'Aug' AS MONTH
UNION SELECT 'Sep' AS MONTH
UNION SELECT 'Oct' AS MONTH
UNION SELECT 'Nov' AS MONTH
UNION SELECT 'Dec' AS MONTH
) AS m
LEFT JOIN users u
ON MONTH(STR_TO_DATE(CONCAT(m.month, ' 2013'),'%M %Y')) = MONTH(u.userRegistredDate)
AND YEAR(u.userRegistredDate) = '2013'
GROUP BY m.month
ORDER BY 1+1;
If you make the union based on a date format you can even reduce the work and load on the query.
SELECT COUNT(u.userID) AS total, DATE_FORMAT(merge_date,'%b') AS month, YEAR(m.merge_date) AS year
FROM (
SELECT '2013-01-01' AS merge_date
UNION SELECT '2013-02-01' AS merge_date
UNION SELECT '2013-03-01' AS merge_date
UNION SELECT '2013-04-01' AS merge_date
UNION SELECT '2013-05-01' AS merge_date
UNION SELECT '2013-06-01' AS merge_date
UNION SELECT '2013-07-01' AS merge_date
UNION SELECT '2013-08-01' AS merge_date
UNION SELECT '2013-09-01' AS merge_date
UNION SELECT '2013-10-01' AS merge_date
UNION SELECT '2013-11-01' AS merge_date
UNION SELECT '2013-12-01' AS merge_date
) AS m
LEFT JOIN users u
ON MONTH(m.merge_date) = MONTH(u.userRegistredDate)
AND YEAR(m.merge_date) = YEAR(u.userRegistredDate)
GROUP BY m.merge_date
ORDER BY 1+1;
Live DEMO of both queries.
You may need a table to hold every "month" record. A temp table can do the trick:
drop table if extists temp_months;
create temporary table temp_months
month date,
index idx_date(month);
insert into temp_months
values ('2013-01-31'), ('2013-02-28'), ...
And now, you can left join your data with this newly created temp table:
SELECT
COUNT( `userID` ) AS total,
DATE_FORMAT( m.month , '%b' ) AS
MONTH ,
YEAR( m.month ) AS year
FROM
months as m
left join `users` as u on m.month = last_day(FROM_UNIXTIME(`userRegistredDate`, '%b' )
GROUP BY
last_day(m.month);
Notice that you can put the temp table creation (and fill) in a stored procedure.
I use last_day for simplicity, but you are free to use any date in the month that you like, if you join it correctly.
Hope this helps
I want to return a row for every month in a range specified- what I'm having trouble with is- that if I do not have a row there is nothing returned; What i was want is the month name to be returned and winner_id to be null
my query so far.
SELECT MONTHNAME(dated) as Month, winner_id FROM competitions WHERE dated > '2012-01-01'
What i want my output to look like is
Month Winner_id
-------------------
January NULL
February 2654
March 19864
April NULL
the problem is i do not have a row for january or april in the db- but i still want a month returned?
should i do the months in php and do individual queries for each month?
There are ways to do this in MySql but it would be overkill in your case. I'd use a variation of your current query and fill the gaps in php:
$res = mysql_query( "SELECT MONTH(dated) as month, winner_id".
" FROM competitions WHERE dated > '2012-01-01'");
$months = array();
for($i = 1; $i <= 6; ++$i) $month[$i] = 0;
while($r = mysql_fetch_assoc($res)) {
$month[$r['month']] = $r['winner_id'];
}
...
You need to make a custom column for all months:
Try with below:
SELECT a,c.Winner_id
FROM
(
select 'January' as a union all select 'Febuary' union all select 'March'
union all select 'April' union all select 'May' union all select 'June' union all
select 'July' union all select 'August' union all select 'September' union all
select 'October' union all select 'November' union all select 'December'
) as a
LEFT JOIN competitions as c on a.a=monthname(c.dated)
WHERE c.dated > '2012-01-01'
Create a temporary table with numbers for six months (or just use a SELECT query with UNIONs) -
CREATE TEMPORARY TABLE months(m INT);
INSERT INTO months VALUE (1),(2),(3),(4),(5),(6);
Then join your table to this temp. table, e.g. -
SELECT t1.m Month, c.winner_id FROM
(SELECT #m:=MONTH('2012-01-01') + m, IF(#m > 12, #m - 12, #m) m FROM months) t1
LEFT JOIN competitions c
ON t1.m = c.MONTH(dated)
WHERE c.dated > '2012-01-01'
I am not sure if in MySQL there is function that return list of all months. So eventually if any month is missing in your table then its month name can not be fetched at run time. Either you might have to pass months from the script and fetch records accordingly.