How to select none if condition is not? - mysql

I try to do a simple query to select from a database and only select rows which fit to the statement of being older than 12 months.
The table is like (mydate actually is a datetime field):
+----+---------+-------------+
| id | account | mydate |
+----+---------+-------------+
| 1 | 1 | last week | <-- this should disqualify account 1
| 2 | 1 | 2 years ago |
| 3 | 1 | 3 years ago |
| 4 | 2 | 2 years ago |
+----+---------+-------------+
And this is my query by now:
SELECT id, account, MAX(mydate)
FROM awesomeTable
WHERE
mydate < SUBDATE(CURDATE(), INTERVAL 12 MONTH)
GROUP BY account
Seems nice. But the results are like following:
+----+---------+-------------+
| id | account | mydate |
+----+---------+-------------+
| 2 | 1 | 2 years ago | <-- SQL doesnt understand me :(
| 4 | 2 | 2 years ago |
+----+---------+-------------+
But I don't want to let account 2 show up at all. I really just want to see accounts where mydate is older than 2 years.
With that query now it doesnt show ID 1 as this doesnt fit the condition in the WHERE clause. But it will show me the newest possible mydate of the other rows.
I want to get a list of all accounts which mydates are ALL older than 1 year. How can i achieve that?

There WHERE condition is done to select the rows first, then MAX() is applied to those results. Use HAVING to operate on the results after aggregating.
SELECT id, account, MAX(mydate) AS maxdate
FROM awesometable
GROUP BY account
HAVING maxdate < DATE_SUB(NOW(), INTERVAL 12 MONTH)
Note that this will not necessarily show the id of the line with the maximum date. For that, you need a join:
SELECT a.id, a.account, a.mydate
FROM awesometable AS a
JOIN (
SELECT account, MAX(mydate) AS maxdate
FROM awesometable
GROUP BY account
HAVING maxdate < DATE_SUB(NOW(), INTERVAL 12 MONTH)) AS b
ON a.account = b.account AND a.mydate = b.maxdate

Related

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

How can I fetch last 30 days left joined with values from my own table?

In my Symfony2/Doctrine2 application, I have an entity, respectively a table in my database where I keep track of every user, if he or she has done a specific action on a specified day.
My table looks like that, let's call it track_user_action:
+---------+------------+
| user_id | date |
+---------+------------+
| 1 | 2013-09-19 |
| 2 | 2013-09-19 |
| 1 | 2013-09-18 |
| 5 | 2013-09-18 |
| 8 | 2013-09-17 |
| 5 | 2013-09-17 |
+---------+------------+
I would like to retrieve a set of rows, where it shows the last 30 days, the corresponding weekday and if the specified user has an entry in this table, e.g. for user with user_id = 1:
+------------+--------------+-----------------+
| date | weekday | has_done_action |
+------------+--------------+-----------------+
| 2013-09-20 | Friday | false |
| 2013-09-19 | Thursday | true |
| 2013-09-18 | Wednesday | true |
| ... | | |
| 2013-08-20 | Tuesday | false |
+------------+--------------+-----------------+
I could think of a LEFT JOIN of a date-table and my track_user_action. But it seems senseless to create a special table just for the dates. MySQL should be able to handle the days, shouldn't it?
Approach:
SELECT
# somehow retrieve last 30 days
date AS date,
DAYNAME(date) AS weekday,
IF ... THEN has_done_action = true ELSE has_done_action = false
# and according weekdays
LEFT JOIN track_user_action AS t
ON t.date = # date field from above
WHERE t.user_id = 1
ORDER BY # date field from above
DESC
LIMIT 0,30
My questions:
What would be a good (My)SQL query that fetches this kind of result?
In how far is this query implementable in Doctrine2 (I know for fact that Doctrine2 doesn't support all MySQL statements, e.g. YEAR() or MONTH())?
This is a working query statement for seven days (adapt query for 30 days accordingly):
SELECT
d.date AS date,
DAYNAME(d.date) AS weekday,
IF(t.user_id IS NOT NULL, 'true', 'false') AS has_done_action
FROM (
SELECT SUBDATE(CURDATE(), 1) AS date UNION
SELECT SUBDATE(CURDATE(), 2) AS date UNION
SELECT SUBDATE(CURDATE(), 3) AS date UNION
SELECT SUBDATE(CURDATE(), 4) AS date UNION
SELECT SUBDATE(CURDATE(), 5) AS date UNION
SELECT SUBDATE(CURDATE(), 6) AS date UNION
SELECT SUBDATE(CURDATE(), 7) AS date
) AS d
LEFT JOIN track_user_action t
ON t.date = d.date

MySQL query based on time range, group users, and sum values over a sliding window

I want to create a new Table B based on the information from another existing Table A. I'm wondering if MySQL has the functionality to take into account a range of time and group column A values then only sum up the values in a column B based on those groups in column A.
Table A stores logs of events like a journal for users. There can be multiple events from a single user in a single day. Say hypothetically I'm keeping track of when my users eat fruit and I want to know how many fruit they eat in a week (7days) and also how many apples they eat.
So in Table B I want to count for each entry in Table A, the previous 7 day total # of fruit and apples.
EDIT:
I'm sorry I over simplified my given information and didn't thoroughly think my example.
I'm initially have only Table A. I'm trying to create Table B from a query.
Assume:
User/id can log an entry multiple times in a day.
sum counts should be for id between date and date - 7 days
fruit column stands for the total # of fruit during the 7 day interval ( apples and bananas are both fruit)
The data doesn't only start at 2013-9-5. It can date back 2000 and I want to use the 7 day sliding window over all the dates between 2000 to 2013.
The sum count is over a sliding window of 7 days
Here's an example:
Table A:
| id | date-time | apples | banana |
---------------------------------------------
| 1 | 2013-9-5 08:00:00 | 1 | 1 |
| 2 | 2013-9-5 09:00:00 | 1 | 0 |
| 1 | 2013-9-5 16:00:00 | 1 | 0 |
| 1 | 2013-9-6 08:00:00 | 0 | 1 |
| 2 | 2013-9-9 08:00:00 | 1 | 1 |
| 1 | 2013-9-11 08:00:00 | 0 | 1 |
| 1 | 2013-9-12 08:00:00 | 0 | 1 |
| 2 | 2013-9-13 08:00:00 | 1 | 1 |
note: user 1 logged 2 entries on 2013-9-5
The result after the query should be Table B.
Table B
| id | date-time | apples | fruit |
--------------------------------------------
| 1 | 2013-9-5 08:00:00 | 1 | 2 |
| 2 | 2013-9-5 09:00:00 | 1 | 1 |
| 1 | 2013-9-5 16:00:00 | 2 | 3 |
| 1 | 2013-9-6 08:00:00 | 2 | 4 |
| 2 | 2013-9-9 08:00:00 | 2 | 3 |
| 1 | 2013-9-11 08:00:00 | 2 | 5 |
| 1 | 2013-9-12 08:00:00 | 0 | 3 |
| 2 | 2013-9-13 08:00:00 | 2 | 4 |
At 2013-9-12 the sliding window moves and only includes 9-6 to 9-12. That's why id 1 goes from a sum of 2 apples to 0 apples.
You need years in your data to be able to use date arithmetic correctly. I added them.
There's an odd thing in your data. You seem to have multiple log entries for each person for each day. You're assuming an implicit order setting the later log entries somehow "after" the earlier ones. If SQL and MySQL do that, it's only by accident: there's no implicit ordering of rows in a table. Plus if we duplicate date/id combinations, the self join (read on) has lots of duplicate rows and ruins the sums.
So we need to start by creating a daily summary table of your data, like so:
select id, `date`, sum(apples) as apples, sum(banana) as banana
from fruit
group by id, `date`
This summary will contain at most one row per id per day.
Next we need to do a limited cross product self-join, so we get seven days' worth of fruit eating.
select --whatever--
from (
-- summary query --
) as a
join (
-- same summary query once again
) as b
on ( a.id = b.id
and b.`date` between a.`date` - interval 6 day AND a.`date` )
The between clause in the on gives us the seven days (today, and the six days prior). Notice that the table in the join with the alias b is the seven day stuff, and the a table is the today stuff.
Finally, we have to summarize that result according to your specification. The resulting query is this.
select a.id, a.`date`,
sum(b.apples) + sum(b.banana) as fruit_last_week,
a.apples as apple_today
from (
select id, `date`, sum(apples) as apples, sum(banana) as banana
from fruit
group by id, `date`
) as a
join (
select id, `date`, sum(apples) as apples, sum(banana) as banana
from fruit
group by id, `date`
) as b on (a.id = b.id and
b.`date` between a.`date` - interval 6 day AND a.`date` )
group by a.id, a.`date`, a.apples
order by a.`date`, a.id
Here's a fiddle: http://sqlfiddle.com/#!2/670b2/15/0
Assumptions:
one row per id/date
the counts should be for id between date and date - 7 days
"fruit" = "banana"
the "date" column is actually a date (including year) and not just month/day
then this SQL should do the trick:
INSERT INTO B
SELECT a1.id, a1.date, SUM( a2.banana ), SUM( a2.apples )
FROM (SELECT DISTINCT id, date
FROM A
WHERE date > NOW() - INTERVAL 7 DAY
) a1
JOIN A a2
ON a2.id = a1.id
AND a2.date <= a1.date
AND a2.date >= a1.date - INTERVAL 7 DAY
GROUP BY a1.id, a1.date
Some questions:
Are the above assumptions correct?
Does table A contain more fruits than just Bananas and Apples? If so, what does the real structure look like?

Select entries by day over the last 7 days

I want to get the count of the registered users in the past 7 days, grouped.
+-----+------------+--------------+
| id | username | created |
+-----+------------+--------------+
| 1 | Vlad | 1360168194 |
+-----+------------+--------------+
| 2 | Test | 1360168194 |
+-----+------------+--------------+
This is my table. I want to have 7 rows of results with the date of the day, and count(id) as the result for the users that registered.
I tried different solutions and none of them really fitted my needings. Are there any ideas?
SELECT DATE(FROM_UNIXTIME(columName)), COUNT(ID) totalCOunt
FROM tableName
WHERE DATE(FROM_UNIXTIME(columName)) BETWEEN CURDATE() + INTERVAL -7 DAY AND CURDATE()
GROUP BY DATE(FROM_UNIXTIME(columName))
SQLFiddle Demo
Other Source(s)
MySQL Date and Time Functions

Select difference between row dates in MySQL

I want to calculate the difference in unique date fields between different rows in the same table.
For instance, given the following data:
id | date
---+------------
1 | 2011-01-01
2 | 2011-01-02
3 | 2011-01-15
4 | 2011-01-20
5 | 2011-01-10
6 | 2011-01-30
7 | 2011-01-03
I would like to generate a query that produces the following:
id | date | days_since_last
---+------------+-----------------
1 | 2011-01-01 |
2 | 2011-01-02 | 1
7 | 2011-01-03 | 1
5 | 2011-01-10 | 7
3 | 2011-01-15 | 5
4 | 2011-01-20 | 5
6 | 2011-01-30 | 10
Any suggestions for what date functions I would use in MySQL, or is there a subselect that would do this?
(Of course, I don't mind putting WHERE date > '2011-01-01' to ignore the first row.)
A correlated subquery could be of help:
SELECT
id,
date,
DATEDIFF(
(SELECT MAX(date) FROM atable WHERE date < t.date),
date
) AS days_since_last
FROM atable AS t
Something like this should work :
SELECT mytable.id, mytable.date, DATEDIFF(mytable.date, t2.date)
FROM mytable
LEFT JOIN mytable AS t2 ON t2.id = table.id - 1
However, this imply that your id are continuous in your table, otherwise this won't work at all. And maybe MySQL will complain for the first row since t2.date will be null but I don't have the time to check now.