sql query with current day comparison - mysql

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 |

Related

Mysql sum multiple column values with date condition

I have a client table with below columns which have data of every day purchase of every client month wise.
ID|MONTH|DAY1|DAY2|DAY3|DAY4|..........|DAY31
1 | 4 | 10 | 20 | 0 | 15 |..........|10
2 | 4 | 20 | 30 | 23 | 7 |..........| 5
1 | 5 | 5 | 10 | 20 | 4 |..........| 20
1 | 6 | 12 | 0 | 10 | 5 |..........| 10
2 | 6 | 10 | 10 | 5 | 10 |..........| 5
Now i want to find the total qty purchased by every client between 15/4/2015 to 15/6/2015.
I am new to mysql, so have no idea how to move forward.
Thanks in advance
It's not a good idea to use a column for every day of the month to store a value. But sometimes we are stuck on bad data formats, here's how you can get the count you need:
SELECT ID, SUM(qty)
FROM (
SELECT
ID,
MAKEDATE(2015,1) + INTERVAL (month-1) MONTH AS dt,
DAY1 AS qry
FROM yourtable
UNION ALL
SELECT
ID,
MAKEDATE(2015,1) + INTERVAL (month-1) MONTH AS dt + INTERVAL 1 DAY,
DAY2 AS qry
UNION ALL
SELECT
ID,
MAKEDATE(2015,1) + INTERVAL (month-1) MONTH AS dt + INTERVAL 2 DAY,
DAY3 AS qry
FROM yourtable
UNION ALL
...
until day 31
...
) s
WHERE
dt>='2015-04-15' AND dt<='2015-06-15'
GROUP BY ID
I'm using a subquery to normalize the data structure, then I'm doing the counts on the outer query, a simple where clause and group by will give the results that you need.

UPDATE + SET + WHERE - Dynamic minimum value

This is a follow-up to:
Dynamic minimum value for specfic range (mysql)
I do have the query to fetch the third column (lowest of the last 3 days) "Low_3_days" via SELECT command:
-----------------------------------------
| Date | Unit_ | Lowest_in_last_|
| | price | 3_days |
|----------------------------------------
| 2015-01-01 | 15 | 15 |
| 2015-01-02 | 17 | 15 |
| 2015-01-03 | 21 | 15 |
| 2015-01-04 | 18 | 17 |
| 2015-01-05 | 12 | 12 |
| 2015-01-06 | 14 | 12 |
| 2015-01-07 | 16 | 12 |
|----------------------------------------
select S.Date,Unit_price,
(select S.Date, Unit_price,
(SELECT min(s2.Unit_Price)
FROM table s2
WHERE s2.DATE BETWEEN s.DATE - interval 3 day and
s.DATE - interval 1 day
) as min_price_3_days
FROM table S;
My new challenge is - what is the best way to use UPDATE-SET-WHERE so I could add the ("Lowest_in_last_3_days") values to a new column in a table (instead of having temporary results displayed to me via SELECT).
By following the UPDATE-SET-WHERE syntax, the query would be:
UPDATE table
SET min_price_3_days =
(select S.Date, Unit_price,
(SELECT min(s2.Unit_Price)
FROM table s2
WHERE s2.DATE BETWEEN s.DATE - interval 3 day and
s.DATE - interval 1 day
) as min_price_3_days
but I have difficulties constructing the correct query.
What would be the correct approach to this? I do recognize this one is a tough one to solve.
Your UPDATE should look like:
update table set low_3_days=
(SELECT min(Unit_Price)
FROM (select unit_price, date as date2 from table) as s2
WHERE s2.date2 BETWEEN date - interval 3 day and date - interval 1 day
);
You can check it in SQLFiddle
In Fiddle I used different names for table and column. I prefer not to use SQL keywords as names

Get records where today is between 2 columns

So I have a events table with 4 records in it:
| id | date | active_from |
| 1 | 2015-01-27 | 2015-01-22 |
| 2 | 2015-01-30 | 2015-01-24 |
| 3 | 2015-01-29 | 2015-01-27 |
I'm trying to create a simple query just to grab the records when today is lower than or equals to date and today is greater than or equals to active_from.
So when its today 2015-01-26 I want to have the records with id 1 and 2,
because the event is active and is yet to come.
Currently I have this query:
select * from `events` where `active_from` <= '2015-01-26' AND `date` >= '2015-01-26'
But it's not working... I must be looking over something here.
You are putting the < and > the wrong way. Have a try with this one:
SELECT * FROM events WHERE CURDATE() BETWEEN active_from AND `date`

SQL "First relevant day"

I have a table with opening_hours for restaurants:
SELECT * FROM opening_hours;
+----+---------------+------------+----------+-----+
| id | restaurant_id | start_time | end_time | day |
+----+---------------+------------+----------+-----+
| 1 | 1 | 12:00:00 | 18:00:00 | 1 |
| 2 | 1 | 09:00:00 | 19:00:00 | 4 |
| 3 | 2 | 09:00:00 | 16:00:00 | 4 |
| 4 | 2 | 09:00:00 | 16:00:00 | 5 |
| 5 | 3 | 09:00:00 | 16:00:00 | 4 |
| 6 | 3 | 09:00:00 | 16:00:00 | 5 |
| 7 | 3 | 09:00:00 | 16:00:00 | 1 |
| 8 | 3 | 09:00:00 | 16:00:00 | 6 |
+----+---------------+------------+----------+-----+
http://www.sqlfiddle.com/#!2/eaea09/1
Now I want to fetch the "closest" next day or same day for every restaurant to the current day. For example when the current day is 1 the result would be:
restaurant_id: 1 day: 1
restaurant_id: 2 day: 4
restaurant_id: 3 day: 1
In the case of day 1 I could do this:
SELECT day FROM opening_hours WHERE day >= 1 GROUP BY restaurant_id LIMIT 1
But if today would be 6 that would not work. I would need the query to go get the maximum number of days (7) and if that could not be found it should start trying from 1 again. So the result for day 6 would be in this case:
restaurant_id: 1 day: 1
restaurant_id: 2 day: 4
restaurant_id: 3 day: 6
How could I achieve this with a query?
I'd think it could be something like this in pseudo SQL:
SELECT `day` FROM opening_hours WHERE `day` >= 'today' IF NOT FOUND WHERE `day` >= 1 GROUP BY `restaurant_id` LIMIT 1
edit:
I could run 2 queries, and determine if a match for a restaurant was found in the first. If not, run a second query. But there must be a better way.
Tested, this one works :) you can keep it simple.
Query:
SELECT * FROM (
SELECT
oh.*
FROM
opening_hours oh
ORDER BY restaurant_id,
`day` + IF(`day` < $current_day, 7, 0)
) sq
GROUP BY restaurant_id;
Explanation:
Note though, that this is a bit hacky. To select a column that is not used in the group by and has no aggregate function applied to it, usually isn't allowed, because theoretically it could give you a random row of each group. That's why it's not allowed in most database systems. MySQL is actually the only one I know of, that allows this (if not set otherwise via sql-mode). Like I said, in theory. Practically it's a bit different and if you do an order by in the subquery, MySQL will always give you the minimum or maximum value (depending on the sort order).
Tests:
Desired result with current day = 1:
root#VM:playground > SELECT * FROM (
-> SELECT
-> oh.*
-> FROM
-> opening_hours oh
-> ORDER BY restaurant_id,
-> `day` + IF(`day` < 1, 7, 0)
-> ) sq
-> GROUP BY restaurant_id;
+----+---------------+------------+----------+-----+
| id | restaurant_id | start_time | end_time | day |
+----+---------------+------------+----------+-----+
| 1 | 1 | 12:00:00 | 18:00:00 | 1 |
| 3 | 2 | 09:00:00 | 16:00:00 | 4 |
| 7 | 3 | 09:00:00 | 16:00:00 | 1 |
+----+---------------+------------+----------+-----+
3 rows in set (0.00 sec)
Desired result with current day = 6:
root#VM:playground > SELECT * FROM (
-> SELECT
-> oh.*
-> FROM
-> opening_hours oh
-> ORDER BY restaurant_id,
-> `day` + IF(`day` < 6, 7, 0)
-> ) sq
-> GROUP BY restaurant_id;
+----+---------------+------------+----------+-----+
| id | restaurant_id | start_time | end_time | day |
+----+---------------+------------+----------+-----+
| 1 | 1 | 12:00:00 | 18:00:00 | 1 |
| 3 | 2 | 09:00:00 | 16:00:00 | 4 |
| 8 | 3 | 09:00:00 | 16:00:00 | 6 |
+----+---------------+------------+----------+-----+
3 rows in set (0.00 sec)
This is a tricky one.
Best I can come up with is this, which seems to work but I might be missing some edge cases.
SELECT sub0.restaurant_id, MIN(sub1.day)
FROM
(
SELECT restaurant_id, MIN( LEAST(ABS(DAYOFWEEK(CURDATE()) - day), ABS(DAYOFWEEK(CURDATE()) - (day + 7)), ABS(DAYOFWEEK(CURDATE()) - (day - 7)))) AS difference
FROM opening_hours
GROUP BY restaurant_id
) sub0
INNER JOIN
(
SELECT restaurant_id, day, LEAST(ABS(DAYOFWEEK(CURDATE()) - day), ABS(DAYOFWEEK(CURDATE()) - (day + 7)), ABS(DAYOFWEEK(CURDATE()) - (day - 7))) AS difference
FROM opening_hours
) sub1
ON sub0.restaurant_id = sub1.restaurant_id
AND sub0.difference = sub1.difference
GROUP BY sub0.restaurant_id
The first sub query is getting the absolute difference between todays day and each restaurant day. It is using the day, the day plus 7 and the day minus 7 to compare with, using ABS to just get the difference in days and using LEAST to get the lowest of those differences. This way if the current day is 1 and there is a restaurant day of 6 it is comparing 1 + 7 with 6, 1 - 7 with 6 and 1 with 6 and getting the least of those (in this case that would be 1 + 7).
The 2nd sub query just gets that difference and the day of the week for each possible restaurant / day, and this is joined to the first sub query.
The outer query uses MIN just to pick a single day when 2 are just as close.
Something like that should do it:
SELECT oh1.*
-- set the start day
FROM (SELECT #start := 1) AS start,
-- calculate difference in days
(SELECT *, (CASE WHEN day-#start >= 0 THEN day-#start ELSE day-#start+7 END) AS diff
FROM opening_hours) AS oh1
-- find minimum difference
JOIN (SELECT restaurant_id, MIN(CASE WHEN day-#start >= 0 THEN day-#start ELSE day-#start+7 END) AS min_diff
FROM opening_hours
GROUP BY restaurant_id) AS oh2
ON oh1.restaurant_id = oh2.restaurant_id AND
oh1.diff = oh2.min_diff
Replace #start := 1 with your starting day or a call to DAYOFWEEK(CURDATE()), depending on how you want to do it!
First step is to simply add 7 to the result of your day difference calculation when the day is less than the day you are searching for:
SET #Day = 6;
SELECT ID, Restaurant_id, Day,
CASE WHEN Day < #Day THEN 7 ELSE 0 END + Day - #Day AS DaysFromNow
FROM opening_hours;
This will give:
ID RESTAURANT_ID DAY DAYSFROMNOW
1 1 1 2
2 1 4 5
3 2 4 5
4 2 5 6
5 3 4 5
6 3 5 6
7 3 1 2
8 3 6 0
Then to get the next relavant day you need to get the minimum DaysFromNow for each restaurant, then join back to your main table:
SET #Day = 6;
SELECT o.*
FROM opening_hours AS o
INNER JOIN
( SELECT Restaurant_id,
MIN(CASE WHEN Day < #Day THEN 7 ELSE 0 END + Day - #Day) AS DaysFromNow
FROM opening_hours
GROUP BY Restaurant_id
) AS mo
ON mo.Restaurant_id = o.Restaurant_id
AND mo.DaysFromNow = (CASE WHEN Day < #Day THEN 7 ELSE 0 END + Day - #Day);
Example on SQL Fiddle

Total Unique users for 30 days [duplicate]

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 |
+---+----------+