Filling empty MySQL Result - mysql

I want to have a continuos date set with the sales.
SELECT *,
UNIX_TIMESTAMP(calendar.datefield) * 1000 AS time,
IFNULL(Sum(mos + mosnk), 0) AS mosfinal,
IFNULL(Sum(neukunden), 0) AS neukunden
FROM sms_stats
RIGHT JOIN calendar
ON ( DATE(sms_stats.date) = calendar.datefield )
WHERE calendar.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
GROUP BY Date_format(calendar.datefield, '%Y%m%d')
this returns me a list of the last 90 days. Now I want to filter it, but if I do
WHERE owner = 2 AND calendar.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
it just returns one result and not the list of dates.

The condition in the where clause "undoes" the right outer join. The solution is to move the condition to the where clause. I have a preference for left outer join over right outer join, so I'll swap the tables:
SELECT *,
UNIX_TIMESTAMP(c.datefield) * 1000 AS time,
IFNULL(Sum(mos + mosnk), 0) AS mosfinal,
IFNULL(Sum(neukunden), 0) AS neukunden
FROM calendar c LEFT JOIN
sms_stats ss
ON DATE(ss.date) = c.datefield and
ss.owner = 2
WHERE c.datefield BETWEEN Curdate() - INTERVAL 90 day AND Now()
GROUP BY Date_format(c.datefield, '%Y%m%d') ;
I added in table aliases to make the query a bit more readable.

Related

MySQL get number of records in each day between two given dates

I'm writing this query where it gets a row value and it will return the number of records for each day for that row between two given dates and returns 0 if there is no records for that day.
I've written a query which does this for the past week.
Current Query:
select d.day, count(e.event) as count
from (
select 0 day union all
select 1 union all
select 2 union all
select 3 union all
select 4 union all
select 5 union all
select 6
) d
left join event e
on e.timestamp >= current_date - interval d.day day
and e.timestamp < current_date - interval (d.day - 1) day
and e.event = ?
group by d.day
The problem is this returns only the results for a fixed number of days.. I want to be able to give it two dates (start and end dates) and get the record counts for each day where I don't know the number of dates in between.
You could use/create a bona-fide calendar table. Something like this:
SELECT
d.day,
COUNT(e.timestamp) AS cnt
FROM
(
SELECT '2020-01-01' AS day UNION ALL
SELECT '2020-01-02' UNION ALL
...
SELECT '2020-12-31'
) d
LEFT JOIN event e
ON e.timestamp >= d.day AND e.timestamp < DATE_ADD(d.day, INTERVAL 1 DAY)
WHERE
d.day BETWEEN <start_date> AND <end_date>
GROUP BY
d.day;
I have covered only the calendar year 2020, but you may extend to cover whatever range you want.

Combine two select statements grouped by date returning two columns of data

This seems like an easy task but my basic sql knowledge is failing me as I'm still learning.
Basically, I'm trying to combine:
SELECT DATE(created) DATE, COUNT(DISTINCT created) newpost FROM surveys
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY GROUP BY DATE(created);
and
SELECT DATE(TIMESTAMP) DATE,subs FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY;
into one query that will return data, grouped by date, into two additional columns - newposts and subs.
I've tried using UNION, which doesn't seem to be giving me the output I want. It combined the data into one column (newpost), and also didn't group by date.
I'm still fairly new to writing MySQL queries, and I've tried searching for answers to no avail. Hoping to seek the knowledge of those smarter than me here.
You could use JOIN
select t1.DATE, t1.newpost, t2.subs
from (
SELECT DATE(created) DATE, COUNT(DISTINCT created) newpost
FROM surveys
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
GROUP BY DATE(created)
) t1
left join (
SELECT DATE(TIMESTAMP) DATE, subs
FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
) t2 on t1.DATE = t2.DATE
I guess you want one row per distinct date, with two different count values shown.
This kind of query is slightly tricker than it seems at first glance, because the two summary queries might have different sets of dates.
So you need to start with a subquery that yields all possible dates of interest. You then need to LEFT JOIN each summary query to it. You must use LEFT JOIN instead of the ordinary inner JOIN, because LEFT JOIN doesn't suppress rows from the right side of the join when they don't match any rows from the left side.
Here goes:
All your dates. Notice the UNION operation is a setwise (duplicate-removing) union operation.
SELECT DISTINCT DATE(created) DATE FROM newpost
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
UNION
SELECT DISTINCT DATE(TIMESTAMP) DATE FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
Then you need your two summary subqueries. The first one is this. Notice that I changed COUNT(DISTINCT created) to COUNT(*) because I don't understand the logic behind the DISTINCT there. Can you have more than one row for a single post; do you tell them apart by timestamp? If you have a row for each post you should COUNT(*).
SELECT DATE(created), COUNT(*) newposts
FROM newpost
GROUP BY DATE(created)
The second summary is this. Again, I counted rows.
SELECT DATE(TIMESTAMP), COUNT(*) subs
FROM trafficstats
GROUP BY DATE(TIMESTAMP)
Finally, join those three subqueries like so. You get the dates from the first subquery, and the summary-by-date information from the second two subqueries.
SELECT dates.DATE, posts.newposts, subs.subs
FROM ( /* date subquery */ ) dates
LEFT JOIN ( /* posts subquery */ ) posts ON dates.DATE = posts.DATE
LEFT JOIN ( /* subs subquery */ ) subs ON dates.DATE = subs.DATE
ORDER BY dates.DATE
Putting it all together:
SELECT dates.DATE, posts.newposts, subs.subs
FROM (
SELECT DISTINCT DATE(created) DATE FROM newpost
WHERE created >= Last_day(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND created < last_day(CURRENT_DATE) + INTERVAL 1 DAY
UNION
SELECT DATE(TIMESTAMP) DATE FROM trafficstats
WHERE TIMESTAMP >= LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY - INTERVAL 1 MONTH
AND TIMESTAMP < LAST_DAY(CURRENT_DATE) + INTERVAL 1 DAY
) dates
LEFT JOIN (
SELECT DATE(created), COUNT(*) newposts
FROM newpost
GROUP BY DATE(created)
) posts ON dates.DATE = posts.DATE
LEFT JOIN (
SELECT DATE(TIMESTAMP), COUNT(*) subs
FROM trafficstats
GROUP BY DATE(TIMESTAMP)
) subs ON dates.DATE = subs.DATE
ORDER BY dates.DATE

Incorrect rows returned with date condition

I have a query that left joins two table with a date condition. I want to fetch rows for yesterday's transactions only.
Here the query:
When I add the AND condition still all the rows are returned but with null values to those not matching condition.
SELECT
B.txn_id,
B.txn_time,
B.svc_method,
B.customer_number,
B.amount,
B.amount_commission,
B.status,
A.partner_txn_id,
A.session_id as partner_session_id
FROM Partner A
LEFT JOIN Transaction B
ON A.log_id = B.txn_id
AND B.txn_time >= (CURDATE() - INTERVAL 1 DAY);
YOu should either change LEFT JOIN to INNER JOIN
or
move the call to WHERE section
B.txn_time >= (CURDATE() - INTERVAL 1 DAY)

"Every derived table must have its own alias" Error in SQL

I have a following SQL query and I'm getting an error "Every derived table must have its own alias.could any one please help me to solve this?
SELECT
c.clientID_PK,
c.clientName,
d1.draftCount,
d2.purchaseOrderValue,
d2.averageValue
FROM client c
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW())
)d1 ON TRUE
LEFT JOIN
(
SELECT
ROUND(sum(p.total_finalValue),2) as purchaseOrderValue
ROUND((p.poValue / 12),2) as averageValue
FROM paymentengine_data p
WHERE p.poDate between NOW() - INTERVAL 90 DAY and NOW()
)d2 ON TRUE
WHERE c.typeID_FK = 1 AND c.stateID_FK = 2 AND c.statusID_FK = 2
AND d1.clientID_FK = c.clientID_PK AND d2.purchaserID_FK = c.clientID_PK
GROUP BY c.clientID_PK
Your first LEFT JOIN has an extra closing brace and that is causing the error.
Change:
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()) -- <-- xtra )
)d1 ON TRUE
To:
LEFT JOIN
(select
COUNT(DISTINCT d.draftID_PK) as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()
)d1 ON TRUE
Here is a query that might actually work:
SELECT c.clientID_PK, c.clientName,
d1.draftCount, d2.purchaseOrderValue, d2.averageValue
FROM client c left join
(select clientID_FK, COUNT(DISTINCT d.draftID_PK)as draftCount
from draft d
where d.draftDate between NOW() - INTERVAL 90 DAY and NOW()
group by clientID_FK
) d1
on d1.clientID_FK = c.clientID_PK LEFT JOIN
(SELECT purchaserID_FK, ROUND(sum(p.total_finalValue),2) as purchaseOrderValue
ROUND((p.poValue / 12),2) as averageValue
FROM paymentengine_data p
WHERE p.poDate between NOW() - INTERVAL 90 DAY and NOW()
GROUP BY purchaserID_FK
) d2
ON d2.purchaserID_FK = c.clientID_PK
WHERE c.typeID_FK = 1 AND c.stateID_FK = 2 AND c.statusID_FK = 2;
Here are some additional changes (besides the extra closing paren):
The first subquery groups by clientID_FK and includes the column in the select.
The on condition has been moved from the where to the on.
The second subquery groups by purchaserID_FK and includes the column in the select.
The on condition has been moved from the where clause to the on.
The outer group by has been removed. Presumably, the primary key is unique on the client table.

Group by day with filled gaps

I am trying to generate a daily sales reports for a particular user based on this tutorial Using MySQL to generate daily sales reports with filled gaps.
To do this, I have three tables, records table, user table and calendar table
records user calendar
id id datefield
user_id
timestamp
The query below returns 0 as total and NULL as the user_id when data is not available for a particular day which is great:
SELECT calendar.datefield AS DATE,
IFNULL(COUNT(records.id),0) AS total, records.user_id
FROM records
RIGHT JOIN calendar ON (DATE(records.timestamp) = calendar.datefield)
WHERE (
calendar.datefield BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW()
)
GROUP BY DATE DESC
The idea is to generate this report for a particular user so I modified the above query to what follows:
SELECT calendar.datefield AS DATE,
IFNULL(COUNT(records.id),0) AS total, records.user_id
FROM records
RIGHT JOIN calendar ON (DATE(records.timestamp) = calendar.datefield)
WHERE (
calendar.datefield BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW()
AND records.user_id = SOME_EXISTING_USER_ID
)
GROUP BY DATE DESC
This return an empty result when there is no record but the idea is to return 0 for any particular day which does not have data.
How can I modify the first query to work for a particular user?
Wow. Been a while since I've seen a RIGHT JOIN in the wild! Anyway, try adding the user predicate from the WHERE clause into the RIGHT JOIN like this:
SELECT calendar.datefield AS DATE,
IFNULL(COUNT(records.id),0) AS total, records.user_id
FROM records
RIGHT JOIN calendar ON (DATE(records.timestamp) = calendar.datefield)
AND records.user_id = SOME_EXISTING_USER_ID
WHERE (
calendar.datefield BETWEEN (NOW() - INTERVAL 14 DAY) AND NOW()
)
GROUP BY DATE DESC;
For me this is one of the great benefits of explicit joins vs implicit joins...