please help me, I want to display data with eloquent, before that I have to create a Raw Query to make it easier for me when I want to implement it to Eloquent.
I want to get a result like this :
What I want
I've tried using this query, but the result is not what I want.
SELECT COUNT(t.pengaduan_id) jml, t.kanal_id, mpk.nama_kanal, DATE_FORMAT(t.created_at, '%M %Y') tgl
FROM tr_pengaduan AS t
LEFT JOIN ms_pengaduan_kanal mpk ON t.kanal_id = mpk.kanal_id
GROUP BY YEAR(t.created_at),MONTH(t.created_at) ORDER BY kanal_id
Result of the above query :
Result
Here are the details from the table :
1. Table Tr_pengaduan : Tr_pengaduan
2. Table ms_pengaduan_kanal : ms_pengaduan_kanal
Is there a way to get the result I want with/without creating a procedure or function? I really appreciate it if you reply with Raw Query or Eloquent or both.
UPDATE :
I tried #ProGu 's suggestion, but the result of the join is partially NULL.
SELECT COUNT(t.pengaduan_id) jml, t.kanal_id, mpk.nama_kanal, DATE_FORMAT(t.created_at, '%M %Y') tgl, mont.MONTH
FROM tr_pengaduan AS t
LEFT JOIN ms_pengaduan_kanal mpk ON t.kanal_id = mpk.kanal_id
LEFT JOIN (SELECT MONTH(CURRENT_DATE()) AS
MONTH
UNION SELECT MONTH(CURRENT_DATE())-1 AS
MONTH
UNION SELECT MONTH(CURRENT_DATE())-2 AS
MONTH
UNION SELECT MONTH(CURRENT_DATE())-3 AS
MONTH
UNION SELECT MONTH(CURRENT_DATE())-4 AS
MONTH) AS mont ON MONTH(t.created_at) = mont.MONTH
WHERE t.kanal_id = mpk.kanal_id
GROUP BY mpk.kanal_id ORDER BY kanal_id
Result : Update Result
try something like this:
three parts in the query
month5 - represent current month and previous 4 months
t - id and names for result set
counts - counting results for last 5 months
SELECT COALESCE(counts.jml, 0) counts, t.kanal_id, t.nama_kanal, DATE_FORMAT(CURDATE() - INTERVAL month5.months MONTH, '%M %Y') tgl
FROM (
SELECT 0 AS months
UNION
SELECT 1
UNION
SELECT 2
UNION
SELECT 3
UNION
SELECT 4
) month5
CROSS JOIN (
SELECT t.kanal_id, MAX(mpk.nama_kanal) nama_kanal
FROM tr_pengaduan t
LEFT JOIN ms_pengaduan_kanal mpk ON t.kanal_id = mpk.kanal_id
WHERE t.created_at BETWEEN CURDATE() - INTERVAL DAY(CURDATE()) - 1 DAY - INTERVAL 4 MONTH AND NOW()
GROUP BY t.kanal_id
) t
LEFT JOIN (
SELECT COUNT(t.pengaduan_id) jml, t.kanal_id, DATE(MAX(t.created_at)) tgl
FROM tr_pengaduan AS t
WHERE t.created_at BETWEEN CURDATE() - INTERVAL DAY(CURDATE()) - 1 DAY - INTERVAL 4 MONTH AND NOW()
GROUP BY t.kanal_id, YEAR(t.created_at), MONTH(t.created_at)
) counts ON LAST_DAY(counts.tgl) = LAST_DAY(CURDATE() - INTERVAL month5.months MONTH) AND t.kanal_id = counts.kanal_id
ORDER BY kanal_id, tgl
Related
I have problem with my query. I have two tables and I want join them to get the results based on primary key on first table, but I missing 1 data from first table.
this my fiddle
as you can see, I missing "xx3" from month 1
I have tried to change left and right join but, the results stil same.
So as you can see I have to set coalesce(sum(b.sd_qty),0) as total, if no qty, set 0 as default.
You should cross join the table to the distinct dates also:
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a
CROSS JOIN (
SELECT DISTINCT sd_date
FROM sales_details
WHERE sd_date >= '2020-04-01' - INTERVAL 3 MONTH AND sd_date < '2020-05-01'
) d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND b.sd_date = d.sd_date
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
Or, for MySql 8.0+, with a recursive CTE that returns the starting dates of all the months that you want the results, which can be cross joined to the table:
WITH RECURSIVE dates AS (
SELECT '2020-04-01' - INTERVAL 3 MONTH AS sd_date
UNION ALL
SELECT sd_date + INTERVAL 1 MONTH
FROM dates
WHERE sd_date + INTERVAL 1 MONTH < '2020-05-01'
)
SELECT a.item_code,
COALESCE(SUM(b.sd_qty), 0) total,
DATE_FORMAT(d.sd_date, '%m-%Y') month_year
FROM item a CROSS JOIN dates d
LEFT JOIN sales_details b
ON a.item_code = b.item_code AND DATE_FORMAT(b.sd_date, '%m-%Y') = DATE_FORMAT(d.sd_date, '%m-%Y')
GROUP BY month_year, a.item_code
ORDER BY month_year, a.item_code;
See the demo.
I have a table like this two
Table A
date amount B_id
'2020-1-01' 3000000 1
'2019-8-01' 15012 1
'2019-6-21' 90909 1
'2020-1-15' 84562 1
--------
Table B
id type
1 7
2 5
I have to show sum of amount until the last date of each month for the last 12 month.
The query i have prepared is like this..
SELECT num2.last_dates,
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 7 AND A.date<=num2.last_dates
),
(SELECT SUM(amount) FROM A
INNER JOIN B ON A.B_id = B.id
WHERE B.type = 5 AND A.date<=num2.last_dates)
FROM
(SELECT last_dates
FROM (
SELECT LAST_DAY(CURDATE() - INTERVAL CUSTOM_MONTH MONTH) last_dates
FROM(
SELECT 1 CUSTOM_MONTH UNION
SELECT 0 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 )num
) num1
)num2
ORDER BY num2.last_dates
This gives me the result like this which is exactly how i need it. I need this query to execute faster. Is there any better way to do what i am trying to do?
2019-05-31 33488.69 109.127800
2019-06-30 263.690 1248932.227800
2019-07-31 274.690 131.827800
2019-08-31 627.690 13.687800
2019-09-30 1533.370000 08.347800
2019-10-31 1444.370000 01.327800
2019-11-30 5448.370000 247.227800
2019-12-31 61971.370000 016.990450
2020-01-31 19550.370000 2535.185450
2020-02-29 986.370000 405.123300
2020-03-31 1152.370000 26.793300
2020-04-30 9404.370000 11894.683300
2020-05-31 3404.370000 17894.683300
I'd use conditional aggregation, and pre-aggregate the monthly totals in one pass, instead of doing twenty-six individual passes repeatedly through the same data.
I'd start with something like this:
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
(column amount isn't qualified in original query, so just guessing here which table that is from. adjust as necessary. best practice is to qualify all column references.
That gets us the subtotals for each month, in a single pass through A and B.
We can get that query tested and tuned.
Then we can incorporate that as an inline view in an outer query which adds up those monthly totals. (I'd do an outer join, just in case rows are missing, sow we don't wind up omitting rows.)
Something like this:
SELECT d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY AS last_date
, SUM(IFNULL(t.tot_type_5,0)) AS rt_type_5
, SUM(IFNULL(t.tot_type_7,0)) AS rt_type_7
FROM ( -- first day of next month
SELECT DATE(NOW()) + INTERVAL -DAY(DATE(NOW()))+1 DAY + INTERVAL 1 MONTH AS dt
) d
CROSS
JOIN ( -- thirteen integers, integers 0 thru 12
SELECT 0 AS n
UNION ALL SELECT 1 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
) i
LEFT
JOIN ( -- totals by month
SELECT CASE WHEN A.date < DATE(NOW()) + INTERVAL -14 MONTH
THEN LAST_DAY( DATE(NOW()) + INTERVAL -14 MONTH )
ELSE LAST_DAY( A.date )
END AS _month_end
, SUM(IF( B.type = 5 , B.amount , NULL)) AS tot_type_5
, SUM(IF( B.type = 7 , B.amount , NULL)) AS tot_type_7
FROM A
JOIN B
ON B.id = A.B_id
WHERE B.type IN (5,7)
GROUP
BY _month_end
) t
ON t._month_end < d.dt
GROUP BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY
ORDER BY d.dt + INTERVAL -i.n MONTH + INTERVAL -1 DAY DESC
The design is meant to do one swoop through the A JOIN B set. We're expecting to get about 14 rows back. And we're doing a semi-join, duplicating the oldest months multiple times, so approx . 14 x 13 / 2 = 91 rows, that get collapsed into 13 rows.
The big rock in terms of performance is going to be materializing that inline view query.
This is how I'd probably approach this in MySQL 8 with SUM OVER:
Get the last 12 months.
Use these months to add empty month rows to the original data, as MySQL doesn't support full outer joins.
Get the running totals for all months.
Show only the last twelve months.
The query:
with months (date) as
(
select last_day(current_date - interval 1 month) union all
select last_day(current_date - interval 2 month) union all
select last_day(current_date - interval 3 month) union all
select last_day(current_date - interval 4 month) union all
select last_day(current_date - interval 5 month) union all
select last_day(current_date - interval 6 month) union all
select last_day(current_date - interval 7 month) union all
select last_day(current_date - interval 8 month) union all
select last_day(current_date - interval 9 month) union all
select last_day(current_date - interval 10 month) union all
select last_day(current_date - interval 11 month) union all
select last_day(current_date - interval 12 month)
)
, data (date, amount, type) as
(
select last_day(a.date), a.amount, b.type
from a
join b on b.id = a.b_id
where b.type in (5, 7)
union all
select date, null, null from months
)
select
date,
sum(sum(case when type = 5 then amount end)) over (order by date) as t5,
sum(sum(case when type = 7 then amount end)) over (order by date) as t7
from data
group by date
order by date
limit 12;
Demo: https://dbfiddle.uk/?rdbms=mysql_8.0&fiddle=ddeb3ab3e086bfc182f0503615fba74b
I don't know whether this is faster than your own query or not. Just give it a try. (You'd get my query much faster by adding a generated column for last_day(date) to your table and use this. If you need this often, this may be an option.)
You are getting some complicated answers. I think it is easier. Start with knowing we can easily sum for each month:
SELECT SUM(amount) as monthtotal,
type,
MONTH(date) as month,
YEAR(date) as year
FROM A LEFT JOIN B on A.B_id=B.id
GROUP BY type,month,year
From that data, we can use a variable to get running total. Best to do by initializing the variable, but not necessary. We can get the data necessary like this
SET #running := 0;
SELECT (#running := #running + monthtotal) as running, type, LAST_DAY(CONCAT(year,'-',month,'-',1))
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
You really need to have a connector that supports multiple statements, or make multiple calls to initialize the variable. Although you can null check the variable and default to 0, you still have an issue if you run the query a second time.
Last thing, if you really want the types to be summed separately:
SET #running5 := 0;
SET #running7 := 0;
SELECT
LAST_DAY(CONCAT(year,'-',month,'-',1)),
(#running5 := #running5 + (CASE WHEN type=5 THEN monthtotal ELSE 0 END)) as running5,
(#running7 := #running7 + (CASE WHEN type=7 THEN monthtotal ELSE 0 END)) as running7
FROM
(SELECT SUM(amount) as monthtotal,type,MONTH(date) as month,YEAR(date) as year FROM A LEFT JOIN B on A.B_id=B.id GROUP BY type,month,year) AS totals
ORDER BY year,month
We still don't show months where there is no data. I'm not sure that is a requirement. But this should only need one pass of table A.
Also, make sure the id on table B is indexed.
I am trying to concat count of id and 1 inside group_concat. Below is my query.
MYSQL:
SELECT
months.name AS NAME,
CONCAT(
'[',
GROUP_CONCAT(
CONCAT(
'[\"',
COUNT(p_c_n_details.JPN_ID),
'\",',
1,
']'
)
),
']'
) AS DATA
FROM
p_c_n_details
INNER JOIN in_e_s_s__p_c_ns RIGHT OUTER JOIN months ON months.id =
MONTH(p_c_n_details.created_at) AND p_c_n_details.type IN('Process Change',
'Design Change') AND p_c_n_details.JPN_ID =
in_e_s_s__p_c_ns.juniperinternalpcnid AND p_c_n_details.created_at >=
last_day(NOW()) + INTERVAL 1 DAY - INTERVAL 3 MONTH
WHERE
months.name IN(
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 0 MONTH)),
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 1 MONTH)),
MONTHNAME(
DATE_SUB(CURDATE(), INTERVAL 2 MONTH))
)
GROUP BY
months.id
Expected Output:
Name | DATA
-------------------------
July [20,1]
August [33,1]
Table months contains month names.
But I am getting error #1111 - Invalid use of group function. I tried this link
mysql group_concat with a count inside?
but I am facing error when using in my query.
This is a guess.
Try:
SELECT months.name AS NAME,
CONCAT('{',COUNT(p_c_n_details.JPN_ID),',1}' AS DATA
in place of the SELECT you now have.
Your example result doesn't show a need for GROUP_CONCAT().
I would like to join two queries :
This first query get "pgm_posts.post_title" and "pgm_post_views.count" (WHERE pgm_post_views.type="4")
SELECT pgm_posts.post_title, pgm_post_views.count
FROM pgm_posts, pgm_post_views
WHERE pgm_post_views.type="4" AND pgm_post_views.id = pgm_posts.id
This second query get all the ids from "pgm_post_views" with condition.
pgm_post_views.type="0" : its the condition for get "period". (look img)
SELECT pgm_posts.id
FROM pgm_posts
INNER JOIN pgm_post_views
ON pgm_posts.id = pgm_post_views.id AND pgm_post_views.type="0" AND DATE_FORMAT(pgm_post_views.period, "%Y-%m-%d") <= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-%d') AND DATE_FORMAT(pgm_posts.post_date, "%Y-%m-%d") <= DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-%d')
Resume : First query get post_title and total count based on all ids of second query.
How i can do this ?
A simple way is a pure join based on selected table
SELECT p
gm_posts.post_title
, pgm_post_views.count
FROM pgm_posts
INNER JOIN pgm_post_views ON pgm_post_views.id = pgm_posts.id
AND pgm_post_views.type="4"
INNER JOIN (
SELECT
pgm_posts.id
FROM pgm_posts
INNER JOIN pgm_post_views ON pgm_posts.id = pgm_post_views.id AND pgm_post_views.type="0"
AND DATE_FORMAT(pgm_post_views.period, "%Y-%m-%d") <=
DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-%d')
AND DATE_FORMAT(pgm_posts.post_date, "%Y-%m-%d") <=
DATE_FORMAT(CURRENT_DATE - INTERVAL 1 MONTH, '%Y-%m-%d')
) T on T.id = pgm_posts
I want to retrieve data of last 1 week from emp_info table on per day basis.
So I have used :
SELECT DAYNAME(timestamp), COUNT(*)
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp);
According to the query I am getting result like:
Monday 5
Thursday 7
But I also want the result of weekday as 0 on which no record has been entered.
From suggestions I come to know about JOIN query. So I have tried to fix it but not getting any solution.
The result you are getting is right because there are no records on a specific dayname. Since you want to get all daynames, you need to project complete set of day (using UNION inside a SUBQUERY) and join it with your existing query.
SELECT a.day_name,
COALESCE(b.totalCount, 0) totalCount
FROM
(
SELECT 'Sunday' day_name, 1 ordby UNION ALL
SELECT 'Monday' day_name, 2 ordby UNION ALL
SELECT 'Tuesday' day_name, 3 ordby UNION ALL
SELECT 'Wednesday' day_name, 4 ordby UNION ALL
SELECT 'Thursday' day_name, 5 ordby UNION ALL
SELECT 'Friday' day_name, 6 ordby UNION ALL
SELECT 'Saturday' day_name, 7 ordby
) a
LEFT JOIN
(
SELECT DAYNAME(timestamp) day_name,
COUNT(*) totalCount
FROM `emp_info`
WHERE DATE(timestamp ) > DATE_SUB(CURDATE( ) , INTERVAL 1 WEEK )
GROUP BY DAYNAME(timestamp)
) b ON a.day_name = b.day_name
ORDER BY a.ordby
SQLFiddle Demo (simple example)