Join two queries mysql - mysql

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

Related

COUNT AND MULTI GROUP BY MONTH MYSQL

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

How to set default value from mysql join interval yearmonth

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.

JOIN, WHERE and the QUERY

Here is the code. It returns table with 0 values in months where there are no data in database. Perfect!
SELECT YEAR(k.miesiace) as year,
MONTH(k.miesiace) AS month,
IFNULL(YEAR(data_zlozenia), YEAR(k.miesiace)) order_year,
IFNULL(MONTH(data_zlozenia), MONTH(k.miesiace)) order_month,
IFNULL(MONTHNAME(data_zlozenia), MONTHNAME(k.miesiace)) monthname,
IFNULL(SUM(zp.cena_netto * ilosc), 0) sum
FROM kalendarz k
LEFT OUTER JOIN zamowienia z
on MONTH(z.data_zlozenia) = MONTH(k.miesiace) AND YEAR(z.data_zlozenia) = YEAR(k.miesiace)
LEFT OUTER JOIN zamowienia_pozycje zp on z.id_ezamowienia = zp.id_ezamowienia
WHERE k.miesiace >= DATE_SUB(now(), INTERVAL 12 MONTH)
AND k.miesiace <= now()
GROUP BY MONTH(k.miesiace), YEAR(k.miesiace)
ORDER BY YEAR(k.miesiace), MONTH(k.miesiace);
But when I add to my WHERE clause this:
AND zp.id_artykulu = 9593
it ruins my code and limits output to only months where data existed instead of all 12 months. What should I change brothers?
Move your zp in a subquery.
SELECT YEAR(k.miesiace) as year,
MONTH(k.miesiace) AS month,
IFNULL(YEAR(data_zlozenia), YEAR(k.miesiace)) order_year,
IFNULL(MONTH(data_zlozenia), MONTH(k.miesiace)) order_month,
IFNULL(MONTHNAME(data_zlozenia), MONTHNAME(k.miesiace)) monthname,
IFNULL(SUM(zp.cena_netto * zp.ilosc), 0) sum
FROM kalendarz k
LEFT OUTER JOIN zamowienia z
on MONTH(z.data_zlozenia) = MONTH(k.miesiace) AND YEAR(z.data_zlozenia) = YEAR(k.miesiace)
LEFT OUTER JOIN
(select ilosc, cena_netto, id_ezamowienia from zamowienia_pozycje
where id_artykulu = 9593) zp on z.id_ezamowienia = zp.id_ezamowienia
WHERE k.miesiace >= DATE_SUB(now(), INTERVAL 12 MONTH)
AND k.miesiace <= now()
GROUP BY MONTH(k.miesiace), YEAR(k.miesiace)
ORDER BY YEAR(k.miesiace), MONTH(k.miesiace);
I am not sure to understand your exact need correctly but I think your WHERE clause you try to add is misplaced. It might work well like this :
SELECT YEAR(k.miesiace) as year,
MONTH(k.miesiace) AS month,
IFNULL(YEAR(data_zlozenia), YEAR(k.miesiace)) order_year,
IFNULL(MONTH(data_zlozenia), MONTH(k.miesiace)) order_month,
IFNULL(MONTHNAME(data_zlozenia), MONTHNAME(k.miesiace)) monthname,
IFNULL(SUM(zp.cena_netto * ilosc), 0) sum
FROM kalendarz k
LEFT OUTER JOIN zamowienia z
on (MONTH(z.data_zlozenia) = MONTH(k.miesiace) AND YEAR(z.data_zlozenia) = YEAR(k.miesiace))
LEFT OUTER JOIN zamowienia_pozycje zp on (z.id_ezamowienia = zp.id_ezamowienia AND zp.id_artykulu = 9593)
WHERE k.miesiace >= DATE_SUB(now(), INTERVAL 12 MONTH)
AND k.miesiace <= now()
GROUP BY MONTH(k.miesiace), YEAR(k.miesiace)
ORDER BY YEAR(k.miesiace), MONTH(k.miesiace);
zp is left joined so adding this where clause:
AND zp.id_artykulu = 9593
will turn the query into kind of an inner join... rows from kalendarz table will be suppressed if there is no match in the right table. Move the condition to the on clause:
LEFT OUTER JOIN zamowienia_pozycje zp on z.id_ezamowienia = zp.id_ezamowienia and zp.id_artykulu = 9593

MySql graph query multiple series aligned to same time x-axis

I have queries that I'm using to make a graph of earnings. But now people are able to earn from two different sources, so I want to separate this out into two lines on the same chart
This one for standard earnings:
SELECT DATE_FORMAT(earning_created, '%c/%e/%Y') AS day, SUM(earning_amount) AS earning_standard
FROM earnings
WHERE earning_account_id = ? AND earning_referral_id = 0 AND (earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
GROUP BY DATE(earning_created)
ORDER BY earning_created
And this one for referral earnings:
SELECT DATE_FORMAT(e.earning_created, '%c/%e/%Y') AS day, SUM(e.earning_amount) AS earning_referral
FROM earnings AS e
INNER JOIN referrals AS r
ON r.referral_id = e.earning_referral_id
WHERE e.earning_account_id = ? AND e.earning_referral_id > 0 AND (e.earning_created > DATE_SUB(now(), INTERVAL 90 DAY)) AND r.referral_type = 0
GROUP BY DATE(e.earning_created)
ORDER BY e.earning_created
How do I get it to run the queries together, so that it outputs two columns/series for the y-axis: earning_standard and earning_referral.
But with them both aligned to the same day column/scale for the x-axis - substituting zero when there are no earnings for a specific series.
You'll need to set both of those queries as subqueries
SELECT DATE_FORMAT(earnings.earning_created, '%c/%e/%Y') AS day,
COALESCE(es.earning_standard, 0) AS earning_standard,
COALESCE(er.earning_referral, 0) AS earning_referral
FROM earnings
LEFT JOIN (SELECT DATE_FORMAT(earning_created, '%c/%e/%Y') AS day,
SUM(earning_amount) AS earning_standard
FROM earnings
WHERE earning_account_id = ?
AND earning_referral_id = 0
AND (earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
GROUP BY DATE(earning_created)) AS es
ON (day = es.day)
LEFT JOIN (SELECT DATE_FORMAT(e.earning_created, '%c/%e/%Y') AS day,
SUM(e.earning_amount) AS earning_referral
FROM earnings AS e
INNER JOIN referrals AS r
ON r.referral_id = e.earning_referral_id
WHERE e.earning_account_id = ?
AND e.earning_referral_id > 0
AND (e.earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
AND r.referral_type = 0
GROUP BY DATE(e.earning_created)) AS er
ON (day = er.day)
WHERE earnings.earning_account_id = ?
ORDER BY day
where I'm assuming earning_account_id = ? is intended to be with a question mark because the language you're using to run the query is replacing it with the actual id before running the query.
SELECT
COALESCE(t1.amount,0) AS link_earnings,
COALESCE(t2.amount,0) AS publisher_referral_earnings,
COALESCE(t3.amount,0) AS advertiser_referral_earnings,
t1.day AS day
FROM
(
SELECT DATE_FORMAT(earning_created, '%c/%e/%Y') AS day, SUM(earning_amount) AS amount
FROM earnings
WHERE earning_referral_id = 0
AND (earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
AND earning_account_id = ?
GROUP BY DATE(earning_created)
) t1
LEFT JOIN
(
SELECT DATE_FORMAT(ep.earning_created, '%c/%e/%Y') AS day, (SUM(ep.earning_amount) * rp.referral_share) AS amount
FROM earnings AS ep
INNER JOIN referrals AS rp
ON ep.earning_referral_id = rp.referral_id
WHERE ep.earning_referral_id > 0
AND (ep.earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
AND ep.earning_account_id = ?
AND rp.referral_type = 0
GROUP BY DATE(ep.earning_created)
) t2
ON t1.day = t2.day
LEFT JOIN
(
SELECT DATE_FORMAT(ea.earning_created, '%c/%e/%Y') AS day, (SUM(ea.earning_amount) * ra.referral_share) AS amount
FROM earnings AS ea
INNER JOIN referrals AS ra
ON ea.earning_referral_id = ra.referral_id
WHERE ea.earning_referral_id > 0
AND (ea.earning_created > DATE_SUB(now(), INTERVAL 90 DAY))
AND ea.earning_account_id = ?
AND ra.referral_type = 1
GROUP BY DATE(ea.earning_created)
) t3
ON t1.day = t3.day
ORDER BY day
Seems to run ok....
You can simply use an outer join to retain earnings even when there is no matching referral, and then conditionally sum depending on whether a referral exists or not:
SELECT DATE_FORMAT(e.earning_created, '%c/%e/%Y') AS day,
SUM(IF(r.referral_id IS NULL, e.earning_amount, 0)) earning_standard,
SUM(IF(r.referral_id IS NULL, 0, e.earning_amount)) earning_referral
FROM earnings e LEFT JOIN referrals r ON r.referral_id = e.earning_referral_id
WHERE e.earning_account_id = ?
AND e.earning_created > CURRENT_DATE - INTERVAL 90 DAY
AND (r.referral_id IS NULL OR r.referral_type = 0)
GROUP BY 1
ORDER BY 1
I've assumed here that earnings.earning_referral_id is never negative, though you can add an explicit test to filter such records if so desired.
I've also changed the filter on earnings.earning_created to base from CURRENT_DATE rather than NOW() to ensure that any earnings created earlier than the current time on the first day of the series are still included—this would typically be what one actually wants, but feel free to change back if not.

left join with empty rows from right table mysql

I have restaurants and orders tables, in orders table I have restaurant_id, status and date fields - for each day I save one row in orders table. If for some day there is no order - it means the is no row for that day in orders table.
I want to show on the calendar the data for the current month for each restaurant, according to these 2 separate conditions.
1) in first case show only those restaurants that have at least one free
day during this month(which means for this month at least one date is missing in orders table).
2) in second case show only those restaurants that are free for today
(which means there is no row for today in orders table)
for both cases, if the condition is satisfied, I should fetch all the orders for the current month - this is the tricky part.
The usual anti-join with left, or inner join do not give the desired result.
Thanks.
edit
outputs should be like this
1) http://img826.imageshack.us/img826/3114/e6zt.png
2) http://img13.imageshack.us/img13/6397/44l0.png
This is all the listings for this month for all restaurants that are free today:
SELECT r.`id`, r.`name`, o.`date`, o.`status`, o.`id` order_id
FROM restaurants r
INNER JOIN orders o
ON r.id = o.restaurant_id
LEFT JOIN
( SELECT DISTINCT o2.Restaurant_ID
FROM orders o2
WHERE o2.date = DATE(CURRENT_TIMESTAMP)
) o2
ON r.id = o2.restaurant_id
WHERE o.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01')
AND o2.Restaurant_ID IS NULL;
This simply gets all the restaurants with bookings today (subquery o2), then excludes these restaurants:
AND o2.Restaurant_ID IS NULL;
This is all the listings for this month for all restaurants that have at least one free day this month:
SELECT r.`id`, r.`name`, o.`date`, o.`status`, o.`id` order_id
FROM restaurants r
INNER JOIN orders o
ON r.id = o.restaurant_id
AND o.date BETWEEN '2013-08-10' AND '2013-08-31'
INNER JOIN
( SELECT o2.Restaurant_ID
FROM orders o2
WHERE o2.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o2.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01')
GROUP BY o2.Restaurant_ID
HAVING COUNT(DISTINCT o2.Date) < DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
) o2
ON r.id = o2.restaurant_id
WHERE o.Date >= DATE_FORMAT(CURRENT_TIMESTAMP ,'%Y-%m-01')
AND o.Date <= DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01');
The trick is to get the number of days in this month:
DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
Then limit the results to restaurant_id's that have less bookings than this:
HAVING COUNT(DISTINCT o2.Date) < DAY(DATE_ADD(DATE_FORMAT(DATE_ADD(CURRENT_TIMESTAMP, INTERVAL 1 MONTH) ,'%Y-%m-01'), INTERVAL -1 DAY))
Example of Both on SQL Fiddle