I have a following SQL statement that should be returns the month, ph1, ph2 and ph3 values where minimum values of next month - minimum values of previous month
SELECT DATE_FORMAT(a1.date_time, '%M,%Y') AS month,
(a4.ph1_active_energy)*10 - a1.ph1_active_energy AS 'ph1', (a4.ph2_active_energy)*10 - a1.ph2_active_energy AS 'ph2', (a4.ph3_active_energy)*10 - a1.ph3_active_energy AS 'ph3'
FROM powerpro a1
JOIN (SELECT MONTH(date_time) month, MIN(date_time) AS min FROM powerpro GROUP BY MONTH(date_time)
) a2 ON a1.date_time = a2.min
JOIN (SELECT MONTH(date_time) month, MIN(date_time) AS min FROM powerpro GROUP BY DATE(date_time)
) a3 ON DATE(a1.date_time) = a3.date - INTERVAL 1 DAY
JOIN powerpro
a4 ON a4.date_time = a3.min
WHERE YEAR(a1.date_time) = YEAR(CURDATE()) ORDER BY a1.date_time
I want to get only the records of current year. But script returns the empty result set.
What may be going wrong. Can any one help me ?
Related
I've a mysql table with me
Now we want to do some calculations like this
count date wise for all courses enrolled
count where course id = 2 for date > start_date AND date < end_date
Expected output where we calculate all courses enrolled
Expected output where we calculate all courses enrolled where course id = 2
*
expected output where course_id = 2 AND date range is between 2022-11-15 to 2022-11-13
The query which I've right now
SELECT COUNT(*), DATE(registered_on)
FROM courses_enrolled
WHERE course_id = 1
GROUP BY DATE(registered_on), course_id
ORDER BY registered_on desc;
You need to use some kind of calendar table approach here:
SELECT d.dt AS date, COUNT(ce.id) AS cnt
FROM (
SELECT '2022-11-12' AS dt UNION ALL
SELECT '2022-11-13' UNION ALL
SELECT '2022-11-14' UNION ALL
SELECT '2022-11-15'
) d
LEFT JOIN courses_enrolled ce
ON DATE(ce.registered_on) = d.dt AND
ce.course_id = 2
GROUP BY d.dt
ORDER BY d.dt;
The calendar table ensures that all dates you want in the output appear. In practice, you may replace the subquery in d with a bona-fide table containing all dates of interest. The left join ensures that no dates are dropped which have no matching courses on that day.
If you are using MySQL 8 you can use a recursive CTE to create your date range.
For all enrolled courses for given date range -
WITH RECURSIVE calendar (date) AS (
SELECT '2022-11-13' # start date
UNION ALL
SELECT date + INTERVAL 1 DAY FROM calendar
WHERE date + INTERVAL 1 DAY <= '2022-11-15' # end date
)
SELECT COUNT(ce.id) count_all, c.date
FROM calendar c
LEFT JOIN courses_enrolled ce
ON ce.registered_on BETWEEN c.date AND (c.date + INTERVAL 1 DAY - INTERVAL 1 SECOND)
GROUP BY c.date
ORDER BY c.date DESC;
Note the use of BETWEEN start AND end of day in the join criteria. For a small dataset this offers negligible benefit but on a large dataset it would allow for use of an index on registered_on, which could offer significantly improved performance.
Or for just the selected course -
WITH RECURSIVE calendar (date) AS (
SELECT '2022-11-13' # start date
UNION ALL
SELECT date + INTERVAL 1 DAY FROM calendar
WHERE date + INTERVAL 1 DAY <= '2022-11-15' # end date
)
SELECT COUNT(ce.id) count_selected_course, c.date
FROM calendar c
LEFT JOIN courses_enrolled ce
ON ce.registered_on BETWEEN c.date AND (c.date + INTERVAL 1 DAY - INTERVAL 1 SECOND)
AND ce.course_id = 2
GROUP BY c.date
ORDER BY c.date DESC;
Or counting both at the same time -
WITH RECURSIVE calendar (date) AS (
SELECT '2022-11-13' # start date
UNION ALL
SELECT date + INTERVAL 1 DAY FROM calendar
WHERE date + INTERVAL 1 DAY <= '2022-11-15' # end date
)
SELECT COUNT(ce.id) count_all, COUNT(IF(ce.course_id = 2, ce.id, NULL)) count_selected_course, c.date
FROM calendar c
LEFT JOIN courses_enrolled ce
ON ce.registered_on BETWEEN c.date AND (c.date + INTERVAL 1 DAY - INTERVAL 1 SECOND)
GROUP BY c.date
ORDER BY c.date DESC;
I expect this query to give me the avg value from daily active users up to date and grouped by month (from Oct to December). But the result is 164K aprox when it should be 128K. Why avg is not working? Avg should be SUM of values / number of current month days up to today.
SELECT sq.month_year AS 'month_year', AVG(number)
FROM
(
SELECT CONCAT(MONTHNAME(date), "-", YEAR(DATE)) AS 'month_year', count(distinct id_user) AS number
FROM table1
WHERE date between '2020-10-01' and '2020-12-31 23:59:59'
GROUP BY EXTRACT(year_month FROM date)
) sq
GROUP BY 1
Ok guys thanks for your help. The problem was that on the subquery I was pulling the info by month and not by day. So I should pull the info by day there and group by month in the outer query. This finally worked:
SELECT sq.day_month, AVG(number)
FROM (SELECT date(date) AS day_month,
count(distinct id_user) AS number
FROM table_1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY 1
) sq
GROUP BY EXTRACT(year_month FROM day_month)
Do not use single quotes for column aliases!
SELECT sq.month_year, AVG(number)
FROM (SELECT CONCAT(MONTHNAME(date), '-', YEAR(DATE)) AS month_year,
count(distinct id_user) AS number
FROM table1
WHERE date >= '2020-10-01' AND
date < '2021-01-01'
GROUP BY month_year
) sq
GROUP BY 1;
Note the fixes to the query:
The GROUP BY uses the same columns as the SELECT. Your query should return an error (although it works in older versions of MySQL).
The date comparisons have been simplified.
No single quotes on column aliases.
Note that the outer query is not needed. I assume it is there just to illustrate the issue you are having.
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.
I have a table that stores each order made by a user, recording the date it was made , the amount and the user id. I am trying to create a query that returns the weekly transactions from Monday to Sunday for the last 12 weeks for a particular user. I am using the following query:
SELECT COUNT(*) AS Orders,
SUM(amount) AS Total,
DATE_FORMAT(transaction_date,'%m/%Y') AS Week
FROM shop_orders
WHERE user_id = 123
AND transaction_date >= now()-interval 3 month
GROUP BY YEAR(transaction_date), WEEKOFYEAR(transaction_date)
ORDER BY DATE_FORMAT(transaction_date,'%m/%Y') ASC
This produces the following result:
This however does not return the weeks where the user has made 0 orders, does not sum the orders from Monday to Sunday and does not return the weeks ordered from 1 to 12. Is there a way to achieve these things?
One way to accomplish this is with an self outer join (in this case, I use a right outer join, but of course a left outer join would work as well).
To start your weeks on Monday, subtract the result of WEEKDAY from your column transaction_date with DATE_SUB, as proposed in the most upvoted answer here.
SELECT
COALESCE(t1.Orders, 0) AS `Orders`,
COALESCE(t1.Total, 0) AS `Total`,
t2.Week AS `Week`
FROM
(
SELECT
COUNT(*) AS `Orders`,
SUM(amount) AS `Total`,
DATE(DATE_SUB(transaction_date, INTERVAL(WEEKDAY(transaction_date)) DAY)) AS `Week`
FROM
shop_orders
WHERE 1=1
AND user_id = 123
AND transaction_date >= NOW() - INTERVAL 12 WEEK
GROUP BY
3
) t1 RIGHT JOIN (
SELECT
DATE(DATE_SUB(transaction_date, INTERVAL(WEEKDAY(transaction_date)) DAY)) AS `Week`
FROM
shop_orders
WHERE
transaction_date >= NOW() - INTERVAL 12 WEEK
GROUP BY
1
ORDER BY
1
) t2 USING (Week)
To return the weeks with no Orders you have to create a table with all the weeks.
For the order order by the same fields in the group by
I've got IP address, email address and date in table. I need to find out how many mails was sent (grouped by IP and email together) during each month of each year (so group by month and year to). Tricky part is, that if no mail was sent, I need to get zero value on results (now I don't get empty rows). How can I do that?
Select distinct X.Sender, X.IP, MONTH(s.date) as Month, YEAR (s.date) as Year, Count(s.ID)
From
(
Select TOP 10 a.Sender, a.SenderIP as IP, COUNT(a.ID) as C
From AMA a Where a.Summary = 'SPAM'
Group by a.Sender, a.SenderIP
Order by C desc
)As X left outer join AMAs
on X.Sender = s.Sender and X.IP = s.SenderIP
Where s.Summary = 'SPAM'
Group by X.Sender, X.IP, YEAR(s.date), MONTH(s.date)
Order by X.Sender,X.IP,YEAR(s.date) asc, Month(s.date) asc`
The way to do this is to OUTER JOIN from a set of months onto your existing resultset. This way, all months with data will be present, and any months without data will have empty values.
You could create a table with that information, or you could use a CTE.
DECLARE #firstDate DATE = '2010-01-01';
WITH Months (fulldate) AS
(
SELECT #firstdate
UNION ALL
SELECT DATEADD(MONTH, 1, fulldate)
FROM Months
WHERE fulldate < GETDATE()
)
SELECT
m.*, q.*
FROM Months m
LEFT OUTER JOIN
(
-- your existing query
) q
ON q.[Year] = DATEPART(YEAR, fulldate)
AND q.[Month] = DATEPART(MONTH, fulldate)
-- you may need to increase the max recursion, if you want more than 100 months...
-- OPTION (MAXRECURSION 1000)