Total Unique users for 30 days [duplicate] - mysql

This question already has answers here:
Trailing Sum Query
(2 answers)
Closed 8 years ago.
Have a problem here that is I need to calculate the total of unque users for 30 days for each of the date. I mean for today it is the sum of unique between the range today-30 days and for yesterday it is yesterday-30 days and so on. Table structure is :
date uid visits
27-06-2013 11 40
27-06-2013 14 40
26-06-2013 13 45
25-06-2013 11 20
24-06-2013 12 40
It goes on like this. What I need is the sum for each day.

Something as simple as the following should work fine:
SELECT
`date`,
COUNT(DISTINCT `uid`) as `unique_visits`
FROM
`foo`
WHERE
`date` >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY
`date`
SQLFiddle DEMO

Assuming I have read your question correctly and you date field is of data type date then something like this maybe:-
SELECT Sub1.EndDate, COUNT(DISTINCT a.uid)
FROM SomeTable a
INNER JOIN
(
SELECT DISTINCT date AS EndDate, DATE_SUB(date, INTERVAL 30 DAY) AS StartDate
FROM SomeTable
) Sub1
ON a.date BETWEEN Sub1.StartDate AND Sub1.EndDate
This is using a subselect to get each range of 30 days, then joining that back against the table to get all the records in that date range and doing a count distinct to find all the distinct users

Consider this example. It provides a rolling sum on values within 3 points of the current value
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT x.i, SUM(y.i) FROM ints x JOIN ints y ON y.i BETWEEN x.i-3 AND x.i GROUP BY x.i;
+---+----------+
| i | SUM(y.i) |
+---+----------+
| 0 | 0 |
| 1 | 1 |
| 2 | 3 |
| 3 | 6 |
| 4 | 10 |
| 5 | 14 |
| 6 | 18 |
| 7 | 22 |
| 8 | 26 |
| 9 | 30 |
+---+----------+

Related

Query date column from join table

Here is the schema:
Customer (Customer_ID, Name, Address, Phone),
Porder (Customer_ID, Pizza_ID, Quantity, Order_Date),
Pizza (Pizza_ID, Name, Price).
I want to get all customers that ordered a pizza in the last 30 days, based on the Order_Date & who spent the most money in the last 30 days. Can these be combined into one?
Here is what I am trying and I am not sure about DATEDIFF or how the query would calculate the total money.
SELECT customer.customer_ID, customer.name FROM customer
JOIN porder ON customer.customer_ID = porder.customer_ID
GROUP BY customer.customer_ID, customer.name
WHERE DATEDIFF(porder.porder_date,getdate()) between 0 and 30
Who spent the most money last 30 days?
SELECT porder.customer_ID, porder.pizza_id, porder.quantity FROM order
JOIN pizza ON porder.pizza_ID = pizza.pizza_ID
GROUP BY porder.customer_ID
WHERE MAX((porder.quantity * pizza.price)) && DATEDIFF(porder.porder_date,getdate()) between 0 and 30
Remember that functions are blackboxes to query optimizer, so you better make the query fit the index, and not the other way around.
WHERE DATEDIFF(order.order_date,getdate()) between 0 and 30
can be rewritten, so that the query would use plain index on order_date
WHERE order.order_date >= CURRENT_DATE - INTERVAL 30 DAY
Who spent the most money in the last 30 days
SELECT
o.customer_id, SUM(p.price * o.quantity)
FROM
order o
INNER JOIN pizza p
ON o.pizza_id = p.pizza_id
WHERE
order_date >= CURRENT_DATE - INTERVAL 30 DAY
GROUP BY o.customer_id
ORDER BY SUM(p.price * o.quantity) DESC
LIMIT 1
Something to think about once you've sorted out your tables, and separated order details from orders.
SELECT * FROM ints;
+---+
| i |
+---+
| 0 |
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
+---+
SELECT x.*
, IF(x.i = y.maxi,1,0) is_biggest
FROM ints x
LEFT
JOIN (SELECT MAX(i) maxi FROM ints) y
ON y.maxi = x.i;
+---+------------+
| i | is_biggest |
+---+------------+
| 0 | 0 |
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
| 7 | 0 |
| 8 | 0 |
| 9 | 1 |
+---+------------+

sql query with current day comparison

I have a mysql table with a due_date field which is simply an integer value.
dealID | due_day
1 | 15
2 | 25
3 | 10
4 | 9
5 | 31
6 | 20
I would like to query this table to only display the data that would be 14 days before the due_day. For example, today is 01/05/13, if I query this table it should only show me dealID 1, 3 and 9. How should I go about this condition?
You can do that simply using DATE_SUB to substract the number of days from current date and then DAYOFMONTH to get the day.
You can create the query using the mentioned functions.
So based on user1951544's answer this is what I came up with.
SELECT due_day
FROM deals
WHERE (due_day - DAYOFMONTH( NOW( ) ) ) <=14
Query:
SQLFIDDLEExample
SELECT
dealID,
due_day
FROM Table1
WHERE due_day < 14 + DAYOFMONTH( NOW( ) )
Result:
| DEALID | DUE_DAY |
--------------------
| 1 | 15 |
| 3 | 10 |
| 4 | 9 |

Getting UNIX TIME with number

MySQL Query:
SELECT c.day,
COUNT(site_id)
FROM calendar c
LEFT JOIN
(
SELECT *
FROM visitors
WHERE site_id = 16
) d ON DAYOFMONTH(d.created) = c.day
WHERE c.day BETWEEN DAYOFMONTH('2012-10-01') AND DAYOFMONTH('2012-10-31')
GROUP BY c.day
ORDER BY c.day
My Tables
Calendar
id | day
---------
1 | 1
2 | 2
3 | 3
...
31 | 31
Visitors
id | site_id | created
-----------------------------------
1 | 16 | 2012-10-18 11:14:39
2 | 16 | 2012-10-18 11:15:17
3 | 11 | 2012-10-18 11:49:14
4 | 11 | 2012-10-18 11:49:43
5 | 16 | 2012-10-19 11:54:37
6 | 1 | 2012-10-19 05:56:31
7 | 2 | 2012-10-19 05:57:56
I used the above query to retrieve a daily result of visits to a site. The query solved my question here.
Results:
day | COUNT(*)
-------------
1 | 0
2 | 0
3 | 0
....
18 | 2
19 | 1
...
31 | 0
Although, now, I am having problems retrieving UNIX_TIMESTAMP from the day which I need for graphing purposes.
How do I retrieve it from the c.day in the query?
Edited:
SELECT
UNIX_TIMESTAMP('2012-10-01' + INTERVAL c.day - 1 DAY) unix_ts_day,
COUNT(v.site_id)
FROM
calendar c
LEFT JOIN (
SELECT * FROM visitors
WHERE site_id = 16 AND DATE(created) BETWEEN '2012-10-01' AND '2012-10-31'
) v
ON DAYOFMONTH(v.created) = c.day
GROUP BY
unix_ts_day

Counting messages per day (after 17:00 counts for next day)

I have two MySQL tables: stats (left) and messages (right)
+------------+---------+ +---------+------------+-----------+----------+
| _date | msgcount| | msg_id | _date | time | message |
+------------+---------+ +----------------------+-----------+----------+
| 2011-01-22 | 2 | | 1 | 2011-01-22 | 06:23:11 | foo bar |
| 2011-01-23 | 4 | | 2 | 2011-01-22 | 15:17:03 | baz |
| 2011-01-24 | 0 | | 3 | 2011-01-22 | 17:05:45 | foobar |
| 2011-01-25 | 1 | | 4 | 2011-01-22 | 23:58:13 | barbaz |
+------------+---------+ | 5 | 2011-01-23 | 00:06:32 | foo foo |
| 6 | 2011-01-23 | 13:45:00 | bar foo |
| 7 | 2011-01-25 | 02:22:34 | baz baz |
+---------+------------+-----------+----------+
I filled in stats.msgcount, but in reality it is still empty. I'm looking for a query way to:
count the number of messages for every stats._date (notice the zero msgcount on 2011-01-25)
messages.time is in 24-hour format. All messages AFTER 5 o'clock (17:00:00) should be counted for the next day (notice msg_id 3 and 4 count for 2011-01-23)
update stats.msgcount to hold all counts
I'm especially concerned about the "later than 17:00:00 count for next day" part. Is this possible in (My)SQL?
You could use:
UPDATE stats LEFT JOIN
( SELECT date(addtime(_date,time) + interval 7 hour) as corrected_date,
count(*) as message_count
FROM messages
GROUP BY corrected_date ) mc
ON stats._date = mc.corrected_date
SET stats.msgcount = COALESCE( mc.message_count, 0 )
However this query requires dates you are interested in to be in the stats table already, if you don't have them make _date primary or unique key if its not yet and use:
INSERT IGNORE INTO stats(_date,msgcount)
SELECT date(addtime(_date,time) + interval 7 hour) as corrected_date,
count(*) as message_count
FROM messages
GROUP BY corrected_date
Really, all you're doing is shifting the times by 7 hours. Something like this should work:
UPDATE stats s
SET count = (SELECT COUNT(msg_id) FROM messages m
WHERE m._date BETWEEN DATE_SUB(DATE_ADD(s._date, INTERVAL TIME_TO_SEC(m.time) SECOND), INTERVAL 7 HOUR)
AND DATE_ADD(DATE_ADD(s._date, INTERVAL TIME_TO_SEC(m.time) SECOND), INTERVAL 17 HOUR));
The basic idea is that it takes each date in your stats table, adjusts it by 7 hours, and looks for messages sent in that range. If you used a DATETIME column instead of separate DATE and TIME columns, you wouldn't need the extra DATE_ADD(..., TIME_TO_SEC) stuff.
There may be a better way to add a date and a time, I didn't see one with a quick look at the MySQL reference documents.
So all you'd need to do is insert a new row in the stats table with a 0 for the msgcount, and run the update command. If you only wanted to update a few days (since the message count probably isn't changing 6 days later) you just need a simple where clause on the update:
UPDATE stats s
SET ...
WHERE s._date BETWEEN '2012-04-03' AND '2012-04-08'

MySQL - how to select id where min/max dates difference is more than 3 years

I have a table like this:
| id | date | user_id |
----------------------------------------------------
| 1 | 2008-01-01 | 10 |
| 2 | 2009-03-20 | 15 |
| 3 | 2008-06-11 | 10 |
| 4 | 2009-01-21 | 15 |
| 5 | 2010-01-01 | 10 |
| 6 | 2011-06-01 | 10 |
| 7 | 2012-01-01 | 10 |
| 8 | 2008-05-01 | 15 |
I’m looking for a solution how to select user_id where the difference between MIN and MAX dates is more than 3 yrs. For the above data I should get:
| user_id |
-----------------------
| 10 |
Anyone can help?
SELECT user_id
FROM mytable
GROUP BY user_id
HAVING MAX(`date`) > (MIN(`date`) + INTERVAL '3' YEAR);
Tested here: http://sqlize.com/MC0618Yg58
Similar to bernie's approach, I'd keep date formats native. I'd also probably list the MAX first as to avoid an ABS call (secure a positive number is always returned).
SELECT user_id
FROM my_table
WHERE DATEDIFF(MAX(date),MIN(date)) > 365
DATEDIFF just returns delta (in days) between two given date fields.
SELECT user_id
FROM (SELECT user_id, MIN(date) m0, MAX(date) m1
FROM table
GROUP by user_id)
HAVING EXTRACT(YEAR FROM m1) - EXTRACT(YEAR FROM m0) > 3
SELECT A.USER_ID FROM TABLE AS A
JOIN TABLE AS B
ON A.USER_ID = B.USER_ID
WHERE DATEDIFF(A.DATE,B.DATE) > 365