MySQL get daily counts over an interval - mysql

How can I get the number of records over a date interval, including dates with no records?
For example I have the following table:
DATE | INSERTID
2011-12-10 | 1
2011-12-10 | 2
2011-12-12 | 3
2011-12-13 | 4
2011-12-15 | 5
2011-12-15 | 6
and the result to be:
DATE | COUNT(INSERTID)
2011-12-10 | 2
2011-12-11 | 0
2011-12-12 | 1
2011-12-13 | 1
2011-12-14 | 0
2011-12-15 | 2

I believe you're going to want to start by generating your list of days, then left join to your object table, grouping on date and doing a count() on the object table. There's already an answer that covers generating the dates, from the looks of it.

Related

MySQL: Create running sum for all dates in range?

I have a table Service, that stores records of sales and referral types that led to the sale. I need to know the total number of sales that resulted from a given referral type over a range of dates. The relevant data in Service looks like the following:
+------+-------+------------+
| uuid | sr_id | s_saledate |
+------+-------+------------+
| | 1 | 2020-01-01 |
| | 1 | 2020-01-01 |
| | 2 | 2021-01-01 |
| | 2 | 2021-01-01 |
| | 1 | 2021-01-01 |
+------+-------+------------+
I want to count the number of sales for each referral type (sr_id) in a given date range.
If my date range is 2020-01-01 thru 2021-01-01, my output should be something like:
+------+-------+------------+----------------------+
| uuid | sr_id | date | num_sales_as_of_date
+------+-------+-----------------------------------+
| | 1 | 2020-01-01 | 2 |
| | 1 | 2020-01-02 | 2 |
| | 1 | 2020-01-03 | 2 |
........................................................ < many rows for days in range
1 2021-01-01 | 3
| | 2 | 2020-01-01 | 0 |
| | 2 | 2020-01-02 | 0 |
........................................................ < many rows for days in range
| | 2 | 2020-01-01 | 2 |
+------+-------+-----------------------------------+
There should be a row for each referral type on each date in the range.
Right now my query looks like:
SELECT s.sr_id,
s.s_saledate AS date,
Count(s.uuid)
OVER (
partition BY s.sr_id
ORDER BY s.s_saledate) AS num_sales_as_of_date
FROM Service s
How do I get the running sum for each referral type on days that had no Service with that particular referral type id?
*** EDIT FOR CLARIFICATION***
For example, in the first table I give there is no Service row in the Service table with sr_id = '1' AND s_saledate === "2020-01-02". There were two rows from prior days where sr_id = '1'. (2020-01-01). My output row for "2020-01-02" is:
sr_id date num_sales_as_of_date
1 | 2020-01-02 | 2 |
You need to left join your Services table from a table with all the dates in the range and a table with all the referral types, so that you get a row with every combination of date and referral type:
WITH RECURSIVE dates AS (
SELECT date('2020-01-01') AS date
UNION ALL
SELECT dates.date + INTERVAL 1 DAY
FROM dates
WHERE dates.date <= '2020-01-05'
)
SELECT ServiceReferral.sr_id,
dates.date,
Count(s.uuid)
OVER (
partition BY ServiceReferral.sr_id
ORDER BY dates.date) AS num_sales_as_of_date
FROM dates
CROSS JOIN ServiceReferral
LEFT JOIN Service s ON s.s_saledate=dates.date AND s.sr_id=ServiceReferral.sr_id
fiddle
If you do this a lot, it may be more convenient to create an actual table dates with all the dates from 0000-01-01 to 9999-12-31 and use that instead (selecting dates in the desired range in the where clause).

Creating a crosstab query in MySQL

I have a MySQL table that tracks certain totals by both hour of the day and various locations. I am trying to create a query that will total not only each column, but also each row. The query I have so far totals each column, but I can't figure out how to get a total for each row as well.
This is my query:
SELECT * FROM
(SELECT IFNULL(hour,"Total") as hour, SUM(location1), SUM(location2), SUM(location3), SUM(location4), SUM(location), FROM counts WHERE ay = 'AY1617' GROUP BY hour WITH ROLLUP) as crossdata
ORDER BY FIELD (hour,'8:00am','9:00am','10:00am','11:00am','12:00pm','1:00pm','2:00pm','3:00pm','4:00pm','5:00pm','6:00pm','7:00pm','8:00pm','9:00pm','10:00pm','11:00pm')
This is ultimately what I want the output to look like:
hour location1 location2 location3 location4 totals
8am 4 3 2 1 10
9am 1 2 2 1 6
10am 2 3 2 3 10
totals 7 8 6 5 26
How can I achieve this?
For what it's worth, this is not a crosstab query. You aren't pivoting rows to columns.
I tried this query and got the result you want:
SELECT IFNULL(hour, 'Total') AS hour,
SUM(location1) AS location1,
SUM(location2) AS location2,
SUM(location3) AS location3,
SUM(location4) AS location4,
SUM(location1)+SUM(location2)+SUM(location3)+SUM(location4) AS totals
FROM counts
WHERE ay = 'AY1617'
GROUP BY hour WITH ROLLUP;
You should really use the TIME data type instead of strings for the hour. Then it just sorts correctly.
+----------+-----------+-----------+-----------+-----------+--------+
| hourt | location1 | location2 | location3 | location4 | totals |
+----------+-----------+-----------+-----------+-----------+--------+
| 08:00:00 | 4 | 3 | 2 | 1 | 10 |
| 09:00:00 | 1 | 2 | 2 | 1 | 6 |
| 10:00:00 | 2 | 3 | 2 | 3 | 10 |
| Total | 7 | 8 | 6 | 5 | 26 |
+----------+-----------+-----------+-----------+-----------+--------+

Count data for 15 minute intervals that are grouped by date

I have the following MySQL table:
+---------------------+-----------+
| timestamp | sensor_id |
+---------------------+-----------+
| 2010-04-09 01:42:31 | M049 |
| 2010-04-09 01:43:31 | M049 |
| 2010-04-09 01:44:31 | M049 |
| 2010-04-09 01:59:31 | M049 |
| 2010-04-10 01:10:31 | M049 |
| 2010-04-10 01:40:31 | M049 |
| 2010-04-10 01:42:31 | M049 |
| 2010-04-11 16:43:31 | M049 |
+---------------------+-----------+
I know how to query the db to get a count of the entries for a specific daytime intervall and group the result by date.
An example to query the event count between 1 am and 2 pm would look like this:
SELECT
date(timestamp) as date,
count(timestamp) as count
FROM
event_data
WHERE
EXTRACT(HOUR FROM TIME(timestamp)) BETWEEN 1 AND 14
GROUP BY
date
The query returns the following table:
+------------+-------+
| date | count |
+------------+-------+
| 2010-04-09 | 4 |
| 2010-04-10 | 3 |
+------------+-------+
Now I only want to count an event every 15 minutes.
The desired result would be:
+------------+-------+
| date | count |
+------------+-------+
| 2010-04-09 | 2 |
| 2010-04-10 | 2 |
+------------+-------+
How do I alter my query to get these results?
You have a good start to group by date and query for the hours you want. Similarly, you can write a query that gets the intervals. I would start by writing a case statement that reads each row you want, and adds a column specifying which interval of the hour it is. (0:14 - 1, 15:29 - 2...) like this:
SELECT timeCol,
HOUR(timeCol) AS hour,
CASE WHEN MINUTE(timeCol) BETWEEN 0 AND 14 THEN 1
WHEN MINUTE(timeCol) BETWEEN 15 AND 29 THEN 2
WHEN MINUTE(timeCol) BETWEEN 30 AND 44 THEN 3
ELSE 4 END AS minute
FROM myTable;
This gives you something like this:
| timeCol | hour | minute |
+---------------------+------+--------+
| 2010-04-09 01:42:31 | 1 | 3 |
| 2010-04-09 01:43:31 | 1 | 3 |
| 2010-04-09 01:44:31 | 1 | 3 |
Once you have that, you can select the distinct hour/minute pairs in each day, and that will give you what you want, as long as you use your WHERE clause accordingly:
SELECT DATE(timeCol) AS dateCol, COUNT(DISTINCT hour, minute) AS numEvents
FROM(
SELECT timeCol,
HOUR(timeCol) AS hour,
CASE WHEN MINUTE(timeCol) BETWEEN 0 AND 14 THEN 1
WHEN MINUTE(timeCol) BETWEEN 15 AND 29 THEN 2
WHEN MINUTE(timeCol) BETWEEN 30 AND 44 THEN 3
ELSE 4 END AS minute
FROM myTable) tmp
WHERE HOUR(timecol) BETWEEN 1 AND 14
GROUP BY dateCol;
Here is an SQL Fiddle example.
I would just like to add that you don't have to record the intervals as 1, 2, 3, 4. Make sure you use something readable, that will make sense to you again in the future. For example, maybe something like this would be better:
WHEN MINUTE(timeCol) BETWEEN 0 and 14 THEN 'firstInterval'...

a query that returns a single row for each foreign key

I have a table of routines. In this table, I have the column "grade" (which is not mandatory), and the column "date". Also, I have a number of days and an array of ids of users. I need a query that returns me the last routine that have a value != null for "grade" column and datediff(current_date,date) >= number_of_days for each id in the array and make an average of all these values.
e.g.
today = 2014/10/15
number_of_days = 10
ids(1,3)
routines
id | type | date | grade | user_id
1 | 1 | 2014-10-10 | 3 | 1
2 | 1 | 2014-10-04 | 3 | 1
3 | 1 | 2014-10-01 | 3 | 1
4 | 1 | 2014-09-24 | 2 | 1
5 | 1 | 2014-10-10 | 2 | 2
6 | 1 | 2014-10-04 | 3 | 2
7 | 1 | 2014-10-01 | 3 | 2
8 | 1 | 2014-09-24 | 1 | 2
9 | 1 | 2014-10-10 | 1 | 3
10 | 1 | 2014-10-04 | 1 | 3
11 | 1 | 2014-10-01 | 1 | 3
12 | 1 | 2014-09-24 | 1 | 3
In this case, my query would return an avg between "grade" of row id #2 and #10
I think you're saying that you want to consider rows having non-null values in the grade column, a date within a given number of days of the current date, and one of a given set of user_ids. Among those rows, for each user_id you want to choose the row with the latest date, and compute an average of the grade columns for those rows.
I will assume that you cannot have any two rows with the same user_id and date, both with non-null grades, else the question you want to ask does not have a well-defined answer.
A query along these lines should do the trick:
SELECT AVG(r.grade) AS average_grade
FROM
(SELECT user_id, MAX(date) AS date
FROM routines
WHERE grade IS NOT NULL
AND DATEDIFF(CURDATE(), date) >= 10
AND user_id IN (1,3)
GROUP BY user_id) AS md
JOIN routines r
ON r.user_id = md.user_id AND r.date = md.date
Note that in principle you need a grade IS NOT NULL condition on both the inner and the outer query to select the correct rows to average, but in practice AVG() ignores nulls, so you don't actually have to filter out the extra rows in the outer query.

mysql query get range between date not from table

I have payment table info like this
ID Costumer | start_pay | Payment
1 | 2014-01-01 | 1.500
2 | 2013-12-01 | 900
that information they must pay every month, i want calculating it for range between start_pay to CURDATE
if CURDATE is 2014-03-01 (Y-m-d) the result I want like this
ID Costumer | start_pay | Payment | total_to_pay | month_count
1 | 2014-01-01 | 1.500 | 4.500 | 3
2 | 2013-12-01 | 900 | 3.600 | 4
can i do that with mysql query?
Try
SELECT *,TIMESTAMPDIFF(MONTH, DATE_SUB(start_pay,INTERVAL 1 MONTH), CURDATE()) AS
month_count,Payment * month_count AS total_to_pay FROM TABLE
Note that if the difference is less than a month it will output 0
PERIOD_DIFF is basically made for this type of calculation:
SELECT PERIOD_DIFF(DATE_FORMAT(CURDATE(),'%Y%m'), DATE_FORMAT(start_pay, '%Y%m')) from your table;