I have the below dataset. In the below example records for the year 1993. The Tgrowth column is start - end. Started is the number of employees that joined on a specific month and ended is the number of employees that left for the same month.
SELECT
r.Tgrowth,
CASE
WHEN t.mon_num = 1 THEN 'JAN'
WHEN t.mon_num = 2 THEN 'FEB'
WHEN t.mon_num = 3 THEN 'MAR'
WHEN t.mon_num = 4 THEN 'APR'
WHEN t.mon_num = 5 THEN 'MAY'
WHEN t.mon_num = 6 THEN 'JUN'
WHEN t.mon_num = 7 THEN 'JUL'
WHEN t.mon_num = 8 THEN 'AUG'
WHEN t.mon_num = 9 THEN 'SEP'
WHEN t.mon_num = 10 THEN 'OCT'
WHEN t.mon_num = 11 THEN 'NOV'
WHEN t.mon_num = 12 THEN 'DEC'
END AS myMONTH
FROM
(SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t
LEFT JOIN Reports r ON t.mon_num = r.theMONTH
AND r.Tyear = 1993
GROUP BY r.Tgrowth , myMONTH
ORDER BY t.mon_num ASC
The result set for the above is as follows,
Tgrowth Month
1 JAN
0 FEB
2 MAR
0 APR
0 MAY
0 JUN
0 JUL
0 AUG
0 SEP
0 OCT
0 NOV
0 DEC
Instead I would like the result to show a rolling sum i.e. add to the Tgrowth field. Something like the below,
growth Emp_Count myMONTH
1 1 JAN
0 1 FEB
2 3 MAR
0 3 APR
0 3 MAY
0 3 JUN
0 3 JUL
0 3 AUG
0 3 SEP
0 3 OCT
0 3 NOV
0 3 DEC
There are 2 options:
use join
use variables
The method of using join is as following:
SELECT
t1.Tgrowth,
sum(t2.Tgrowth) as Emp_Count,
CASE
WHEN t1.Month = 1 THEN 'JAN'
WHEN t1.Month = 2 THEN 'FEB'
WHEN t1.Month = 3 THEN 'MAR'
WHEN t1.Month = 4 THEN 'APR'
WHEN t1.Month = 5 THEN 'MAY'
WHEN t1.Month = 6 THEN 'JUN'
WHEN t1.Month = 7 THEN 'JUL'
WHEN t1.Month = 8 THEN 'AUG'
WHEN t1.Month = 9 THEN 'SEP'
WHEN t1.Month = 10 THEN 'OCT'
WHEN t1.Month = 11 THEN 'NOV'
WHEN t1.Month = 12 THEN 'DEC'
END AS myMONTH
FROM (
SELECT
case
when r.growth is not null then r.growth
when r.growth is null then 0
END as Tgrowth,
t.mon_num AS Month
FROM
(SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t
LEFT JOIN Reports r ON t.mon_num = r.themonth
AND r.theYear = 1993
GROUP BY r.growth , Month
ORDER BY t.mon_num ASC
) as t1 join (
SELECT
case
when r.growth is not null then r.growth
when r.growth is null then 0
END as Tgrowth,
t.mon_num AS Month
FROM
(SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t
LEFT JOIN Reports r ON t.mon_num = r.themonth
AND r.theYear = 1993
GROUP BY r.growth , Month
ORDER BY t.mon_num ASC
) as t2 on t1.Month >= t2.Month group by t1.Month;
Use variables solution is as following:
SET #num := 0;
select
Tgrowth,
#num := #num + Tgrowth as Emp_Count,
CASE
WHEN t1.Month = 1 THEN 'JAN'
WHEN t1.Month = 2 THEN 'FEB'
WHEN t1.Month = 3 THEN 'MAR'
WHEN t1.Month = 4 THEN 'APR'
WHEN t1.Month = 5 THEN 'MAY'
WHEN t1.Month = 6 THEN 'JUN'
WHEN t1.Month = 7 THEN 'JUL'
WHEN t1.Month = 8 THEN 'AUG'
WHEN t1.Month = 9 THEN 'SEP'
WHEN t1.Month = 10 THEN 'OCT'
WHEN t1.Month = 11 THEN 'NOV'
WHEN t1.Month = 12 THEN 'DEC'
END AS myMONTH
from (
SELECT
case
when r.growth is not null then r.growth
when r.growth is null then 0
END as Tgrowth,
t.mon_num AS Month
FROM
(SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4
UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8
UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t
LEFT JOIN Reports r ON t.mon_num = r.themonth
AND r.theYear = 1993
GROUP BY r.growth , Month
ORDER BY t.mon_num ASC ) t1;
Since you are running MySQL 8.0, I would recommend a recursive query to generate the dates, and then window functions and aggregation.
If you want the whole 1993 year:
with dates as (
select '1993-01-01' dt
union all
select dt + interval 1 month from dates where dt < '1993-12-01'
)
select
date_format(d.dt, '%b') mymonth,
coalesce(sum(started), 0) - coalesce(sum(ended), 0) growth,
sum(coalesce(sum(started), 0) - coalesce(sum(ended), 0)) over(order by d.dt) emp_count
from dates d
left join reports r on r.theDate >= d.dt and r.theDate < d.dt + interval 1 month
group by d.dt
order by d.dt
This assumes that theDate is stored as a date datatype and not a string (else, you would need to convert it first, using str_to_date()).
This also takes in account the possibility that the table may contain several rows for a given month. If that's not the case, then there is no need for aggregation:
with dates as (
select '1993-01-01' dt
union all
select dt + interval 1 month from dates where dt < '1993-12-01'
)
select
date_format(d.dt, '%b') mymonth,
coalesce(started, 0) - coalesce(ended, 0) growth,
sum(coalesce(started, 0) - coalesce(ended, 0)) over(order by d.dt) emp_count
from dates d
left join reports r on r.theDate >= d.dt and r.theDate < d.dt + interval 1 month
order by d.dt
Related
I have the following data,
started ended theDate theYear themonth growth division teams status location
2 0 8/31/2019 2019 8 2 Unknown Team A ACTIVE Town A
1 0 5/31/1996 1996 5 1 Unknown Team B ACTIVE Town A
1 0 8/31/2014 2014 8 1 Unknown Team B ACTIVE Town B
1 0 1/31/1996 1996 1 1 Unknown Team B ACTIVE Town C
1 0 7/31/2004 1985 7 1 Unknown Team C ACTIVE Town E
1 0 7/31/1985 1985 7 1 Unknown Team B ACTIVE Town A
1 0 5/31/2019 2019 5 1 Unknown Team A ACTIVE Town F
The started column shows the employees that have joined on that particular date. The growth column is started - ended where ended is the number of employees that left.
I have the following query which will extract the data correctly as long as i specifiy the variables.
set #theYear = 2019 ;
set #team = 'Team A' ;
SELECT
t1.growth,
SUM(t2.growth) AS Emp_Count,
CASE
WHEN t1.theYear IS NULL THEN t1.theYear
ELSE t1.theYear
END AS theYear,
t1.team,
t1.location,
t1.division,
t1.status,
CASE
WHEN t1.Month = 1 THEN 'JAN'
WHEN t1.Month = 2 THEN 'FEB'
WHEN t1.Month = 3 THEN 'MAR'
WHEN t1.Month = 4 THEN 'APR'
WHEN t1.Month = 5 THEN 'MAY'
WHEN t1.Month = 6 THEN 'JUN'
WHEN t1.Month = 7 THEN 'JUL'
WHEN t1.Month = 8 THEN 'AUG'
WHEN t1.Month = 9 THEN 'SEP'
WHEN t1.Month = 10 THEN 'OCT'
WHEN t1.Month = 11 THEN 'NOV'
WHEN t1.Month = 12 THEN 'DEC'
END AS myMONTH
FROM
(SELECT
CASE
WHEN r.growth IS NOT NULL THEN r.growth
WHEN r.growth IS NULL THEN 0
END AS growth,
r.theYear,
r.team,
r.division,
r.location,
t.mon_num AS Month,
r.status
FROM
Reports r
RIGHT JOIN (SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t ON t.mon_num = r.themonth
AND r.theYear = (select #theYear)
AND r.team = (select #team)
GROUP BY r.growth , Month
ORDER BY t.mon_num ASC) AS t1
JOIN
(SELECT
CASE
WHEN r2.growth IS NOT NULL THEN r2.growth
WHEN r2.growth IS NULL THEN 0
END AS growth,
r2.theYear,
r2.team,
r2.division,
r2.location,
t.mon_num AS Month,
r2.status
FROM
Reports r2
RIGHT JOIN (SELECT 1 mon_num UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) t ON t.mon_num = r2.themonth
AND r2.theYear = (select #theYear)
AND r2.team = (select #team)
GROUP BY r2.growth , Month
ORDER BY t.mon_num ASC) AS t2 ON t1.Month >= t2.Month
GROUP BY t1.Month;
As you can see below the query will list the months i.e Jan,feb etc along with the selected data along with the incremental count.
However if I want to use the query without the variables i.e. get all data without any conditions/filters the result i get is incorrect. First off it should start with the year 1985 as i do have the year 1985 in my dataset.
That's quite a complex query. A wise programmer troubleshoots that sort of thing a subquery at a time.
Your query contains this code:
AND r.theYear = (select #theYear)
What is this supposed to do if your #-variable is not defined?
Do you want something like this instead?
AND (#theYear IS NULL OR r.theYear=#theYear)
That will not filter by year if your #-variable is NULL.
Hello I would like convert this query in DQL or in a pointagerepository , I have test in native query but return is [] an empty array
this is a query I would like convert
select m.mon, sum(p.hours)
from (select 'Jan' as mon, 1 as mm union all
select 'Feb' as mon, 2 as mm union all
select 'March' as mon, 3 as mm union all
select 'Apr' as mon, 4 as mm union all
select 'May' as mon, 5 as mm union all
select 'June' as mon, 6 as mm union all
select 'July' as mon, 7 as mm union all
select 'Aug' as mon, 8 as mm union all
select 'Sep' as mon, 9 as mm union all
select 'Oct' as mon, 10 as mm union all
select 'Nov' as mon, 11 as mm union all
select 'Dec' as mon, 12 as mm
) m left join
pointage p
on p.date_point >= '2020/01/01' and '2021/01/01' and
p.user_id = ? and
month(p.date_point) = m.mm
group by m.mon
order by min(m.mm)
And user set dynamic as would the repository.
I haven't any idea for convert it because is a subrequest and left join on my entity pointage
results like at :
//PointageRepository
public function findRecapTotalByUser($user)
{
return $this->createQueryBuilder('p')
->select('m.mon','SUM(p.hours)')
->from("(select 'Jan' as mon, 1 as mm union all
select 'Feb' as mon, 2 as mm union all
select 'March' as mon, 3 as mm union all
select 'Apr' as mon, 4 as mm union all
select 'May' as mon, 5 as mm union all
select 'June' as mon, 6 as mm union all
select 'July' as mon, 7 as mm union all
select 'Aug' as mon, 8 as mm union all
select 'Sep' as mon, 9 as mm union all
select 'Oct' as mon, 10 as mm union all
select 'Nov' as mon, 11 as mm union all
select 'Dec' as mon, 12 as mm
)", 'm')
->andWhere('p.user = :user') //with date point but is on the left join ...
->setParameter('user', $user)
->getQuery()
->getResult();
}
Native query attempt
$rm = new ResultSetMapping();
$rm->addEntityResult(Pointage::class, 'p')
->addFieldResult('p','hours', 'hours');
$user = $this->getUser()->getId();
$sql = $manager->createNativeQuery("
SELECT m.mon, SUM(p.hours) FROM (
SELECT 'Jan' as mon, 1 as mm union all
SELECT 'Feb' as mon, 2 as mm union all
SELECT 'March' as mon, 3 as mm union all
SELECT 'Apr' as mon, 4 as mm union all
SELECT 'May' as mon, 5 as mm union all
SELECT 'June' as mon, 6 as mm union all
SELECT 'July' as mon, 7 as mm union all
SELECT 'Aug' as mon, 8 as mm union all
SELECT 'Sep' as mon, 9 as mm union all
SELECT 'Oct' as mon, 10 as mm union all
SELECT 'Nov' as mon, 11 as mm union all
SELECT 'Dec' as mon, 12 as mm
) m left join
pointage p on p.date_point >= '2020-01-01' and '2021-01-01' and
p.user_id = 1 and
month(p.date_point) = m.mm
group by m.mon
order by min(m.mm)
", $rm)
And sub query is not allowed.
I have database
From tabel userlist
NO NAMA DATE
1 THIS 2019-01-17 18:40:45
2 IS 2019-01-17 18:40:45
3 NAME 2019-02-17 18:40:45
From tabel usertext
ID TEXT CREATE
1 THIS 2019-01-18 18:40:45
2 IS 2019-02-21 18:40:45
3 TEXT 2019-03-19 18:40:45
how to return like this with sql query
Month Name Text
Jan 2 1
Feb 1 1
Mar 0 1
i already try this
SELECT MONTHNAME(userlist.date) as Month, COUNT(userlist.no) as name, COUNT(usertext.id) as text FROM userlist, usertext WHERE MONTH(userlist.date) < 12 AND YEAR(userlist.date) = YEAR(CURRENT_TIMESTAMP) GROUP BY MONTHNAME(date)
and return like this
Month Name Text
Jan 1 1
Feb 1 1
Mar 1 1
Union, then aggregate.
SELECT Month, SUM(usr) as Name, SUM(txt) as Text
FROM
(
SELECT MONTH(t.date) AS monthnum, MONTHNAME(t.date) AS Month, 1 AS usr, 0 AS txt
FROM userlist t
WHERE YEAR(t.date) = YEAR(CURRENT_TIMESTAMP)
AND MONTH(t.date) < 12
UNION ALL
SELECT MONTH(t.create) AS monthnum, MONTHNAME(t.create) AS Month, 0 AS usr, 1 AS txt
FROM usertext t
WHERE YEAR(t.create) = YEAR(CURRENT_TIMESTAMP)
AND MONTH(t.create) < 12
) q
GROUP BY monthnum, Month
ORDER BY monthnum
And below a little piece of sql that generates 12 months. It could be an extra UNION ALL in the inner query, to fill gaps for the missing months.
select n as monthnum, monthname(100*n+1) as month, 0 as usr, 0 as txt
from (
select 1 as n union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9 union all select 10 union all select 11 union all select 12
) q
You could try joining the subquery for count
select t1.my_month month, t2.count_name, t3.count_text
from(
select month(create ) my_month
from userlist
union
select month(create)
from usertext
) t1
left join (
select month(create) month, count(*) count_name
from userlist
group by month(create)
) t2 on t2.month = t1.my_month
left join (
select month(create) month, count(*) count_text
from usertext
group by month(create)
) t3 on t3.month = t1.my_month
I have a query that count rows between dates(3 months). It shows me the total rows for each month which have records in that month. But if records in month not exists, the query isn`t showing me the null or zero value.
I've tried left join, cross join, coalesce(t2.id,0) and I'm don't achieve anything.
Here's my code:
SELECT CONCAT(t1.month, ' ' , y.years) as month, COUNT(t2.id) as quantity
FROM
( SELECT 'Jan' month UNION ALL
SELECT 'Feb' month UNION ALL
SELECT 'Mar' month UNION ALL
SELECT 'Apr' month UNION ALL
SELECT 'May' month UNION ALL
SELECT 'Jun' month UNION ALL
SELECT 'Jul' month UNION ALL
SELECT 'Aug' month UNION ALL
SELECT 'Sep' month UNION ALL
SELECT 'Oct' month UNION ALL
SELECT 'Nov' month UNION ALL
SELECT 'Dec' month ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) as years FROM negocio) y
LEFT JOIN
(SELECT id, data FROM negocio) t2
on t1.month = LEFT(monthname(t2.data), 3) AND y.years = YEAR(t2.data)
WHERE (t2.data BETWEEN NOW() - INTERVAL 3 MONTH AND NOW())
GROUP BY t1.month, y.years ORDER BY year(STR_TO_DATE(years, '%YYYY')), month(STR_TO_DATE(month, '%M'))
What I need is described in this example:
|--------------| |--------------|
|month|quantity| |month|quantity|
|-----|--------| and it must show -> |-----|--------|
| Feb | 5 | | Feb | 5 |
| Apr | 26 | | Mar | 0 |
|--------------| | Apr | 26 |
|--------------|
Any ideas?
Your WHERE clause is what is causing you not to get any data in your output for March, as it will fail for any row in negocio that has a NULL data value, thus effectively converting the LEFT JOIN into an INNER JOIN and removing rows for any month which has no values in negocio (see the manual). That condition should be moved to the ON condition. You do still need a WHERE clause, but it should be on the date generated from t1 and y. I've assumed your requirement on the 9th of May is to count values from the 9th of February onwards, in which case the required condition is:
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
This query should give you the results you want. For ease of ordering and joining to the negocio table, I've added a month number field to your months (t2) table:
SELECT CONCAT(t1.month, ' ' , y.year) as month,
COUNT(t2.id) as quantity
FROM
(SELECT 1 AS mnum, 'Jan' AS month UNION ALL
SELECT 2, 'Feb' UNION ALL
SELECT 3, 'Mar' UNION ALL
SELECT 4, 'Apr' UNION ALL
SELECT 5, 'May' UNION ALL
SELECT 6, 'Jun' UNION ALL
SELECT 7, 'Jul' UNION ALL
SELECT 8, 'Aug' UNION ALL
SELECT 9, 'Sep' UNION ALL
SELECT 10, 'Oct' UNION ALL
SELECT 11, 'Nov' UNION ALL
SELECT 12, 'Dec' ) t1 CROSS JOIN
(SELECT DISTINCT YEAR(data) AS year FROM negocio) y
LEFT JOIN negocio t2 ON t1.mnum = MONTH(t2.data) AND y.year = YEAR(t2.data) AND t2.data BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
WHERE STR_TO_DATE(CONCAT_WS('-', y.year, t1.month, DAY(CURDATE())), '%Y-%b-%d') BETWEEN CURDATE() - INTERVAL 3 MONTH AND CURDATE()
GROUP BY y.year, t1.month, t1.mnum
ORDER BY y.year, t1.mnum
Demo on dbfiddle
I have a query that I am using to pull back the total costs per months for the previous 6 months of data. The issue I need to solve is when there is no records for a specific month, nothing is returned and only 5 months are shown.
I need to modify this query to always show the 6 months, even when there is no data for a specific month but I am unsure how to accomplish this.
select sum(cost),
CASE
WHEN MONTH(collection_date) = 1 THEN 'January'
WHEN MONTH(collection_date) = 2 THEN 'February'
WHEN MONTH(collection_date) = 3 THEN 'March'
WHEN MONTH(collection_date) = 4 THEN 'April'
WHEN MONTH(collection_date) = 5 THEN 'May'
WHEN MONTH(collection_date) = 6 THEN 'June'
WHEN MONTH(collection_date) = 7 THEN 'July'
WHEN MONTH(collection_date) = 8 THEN 'August'
WHEN MONTH(collection_date) = 9 THEN 'September'
WHEN MONTH(collection_date) = 10 THEN 'October'
WHEN MONTH(collection_date) = 11 THEN 'November'
WHEN MONTH(collection_date) = 12 THEN 'December'
ELSE 'NULL'
END AS datemodified
from invoices
WHERE collection_date >= DATE_SUB(now(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date asc;
Sample of the results with an empty month
COST Datemodified
300 September
200 November
200 December
Desired output
COST Datemodified
0 August
300 September
0 October
200 November
200 December
You can create fake month data and join your invoices table to it. Try this:
SELECT SUM(cost), months.name AS datemodified
FROM (SELECT 1 AS num, 'January' AS name
UNION SELECT 2, 'February'
UNION SELECT 3, 'March'
UNION SELECT 4, 'April'
UNION SELECT 5, 'May'
UNION SELECT 6, 'June'
UNION SELECT 7, 'July'
UNION SELECT 8, 'August'
UNION SELECT 9, 'September'
UNION SELECT 10, 'October'
UNION SELECT 11, 'November'
UNION SELECT 12, 'December') months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;
However, that gives you all the 12 months. To get only the 6 last months, you need to dynamically generate your fake month data:
SELECT SUM(cost),
CASE num WHEN 1 THEN 'January'
WHEN 2 THEN 'February'
WHEN 3 THEN 'March'
WHEN 4 THEN 'April'
WHEN 5 THEN 'May'
WHEN 6 THEN 'June'
WHEN 7 THEN 'July'
WHEN 8 THEN 'August'
WHEN 9 THEN 'September'
WHEN 10 THEN 'October'
WHEN 11 THEN 'November'
WHEN 12 THEN 'December'
END AS datemodified
FROM (SELECT MONTH(NOW()) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 3 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 4 MONTH)) AS num
UNION SELECT MONTH(DATE_SUB(NOW(), INTERVAL 5 MONTH)) AS num) months
LEFT JOIN invoices.collection_date = months.num
WHERE collection_date >= DATE_SUB(NOW(), INTERVAL 5 MONTH)
GROUP BY MONTH(collection_date)
ORDER BY collection_date ASC;