I would prefer to get this done in MySQL.
Table:
tracker | user_id | timestamp | action
1 1 1234 1
2 2 1236 9
3 1 1237 2
I need to find all users who had the action of 1, and subtract the timestamp from their next action. Meaning, User 1's 1234 timestamp will be subtracted from his 1237 timestamp.
The idea is to calculate how long the user spent at action 1 until they went somewhere else.
Getting the first timestamp is easy. One idea is to record the tracker value, and then do a subquery for where user_id pops up again with a tracker higher than the first. In the example: WHERE user_id = 1 AND tracker > 1
Is there a more optimal solution?
Probably this helps you. This will join to all future actions
SELECT t1.*,
t2.`timestamp` - t1.`timestamp`
FROM table t1
JOIN table t2
ON t1.user_id = t2.user_id
AND t2.tracker > t1.tracker
WHERE t1.`action` = 1;
you probably want to extend to add also a condition to get the next one only.
this is some ad-hoc query, and can't precisely tell if will work or not
This is suppose to select only the next one for that user.
SELECT t1.*,
t2.`timestamp` - t1.`timestamp`
FROM table t1
JOIN table t2
ON t1.user_id = t2.user_id
AND t2.tracker = (SELECT Min(tracker)
FROM table t3
WHERE t3.user_id = t1.user_id
AND t3.tracker > t1.tracker)
WHERE t1.`action` = 1;
Related
My table is:
id user date
---|-----|------------|
1 | ab | 2011-03-04 |
2 | ab | 2016-03-04 |
3 | cd | 2009-03-04 |
4 | cd | 2013-03-03 |
5 | ef | 2009-03-03 |
I need to select each distinct user with :
"update" > date more than current date
Result would be:
id user date
---|-----|------------|
2 | ab | 2016-03-04 |
5 | ef | 2016-03-03 |
"expired" > date less than current date
Result would be:
id user date
---|-----|------------|
3 | cd | 2009-03-04 |
I have tried:
SELECT t1.* FROM tmp t1
WHERE t1.date = (SELECT MAX(t2.date)
FROM tmp t2 WHERE t2.user = t1.user
AND YEAR(MAX(t2.date))<(YEAR(CURRENT_TIMESTAMP)-1))
order by user asc
It doesn't work. Result-set has 0 rows.
Any ideas? Thanks. It'll help me so much..
For dates greater than the current date:
select distinct user
from t
where `update` >= curdate();
Actually, I think you want a group by:
select user
from t
group by user
having max(`date`) >= curdate();
This will also work for earlier dates.
A couple of observations:
"It doesn't work" isn't very descriptive of the behavior you observe. It doesn't describe whether the query is executing and is returning unexpected results, or whether the query is returning an error.
The values shown for date values are in an odd non-MySQL format. Standard format for DATE values is YYYY-MM-DD. This leaves us wondering what the actual datatype of the column named date. If it's a character string, with the month and day preceding the year, that presents problems in comparisons. String comparisons are done character by character, from left to right.
The use of the YEAR() function is very strange in this context. You say you want to compare "date" values. The YEAR function extracts the year value from a date. So it looks like your query is only going to be comparing a year.
And again, if the argument to the YEAR() function isn't a DATE datatype, there's going to be an implicit conversion of that argument to DATE... and for that implicit conversion to work correctly, the string will need to be in a format like 'YYYY-MM-DD'.
Also, it looks like the subquery is using an aggregate function MAX() in a WHERE clause. That's not valid. The predicates in the WHERE clause are evaluated when the rows are accessed. The value of the aggregate function will not be available until after the rows are accessed, and the group by operation is performed. If you need a condition on the aggregate, then that can go into a HAVING clause.
Your dates are in a peculiar format of MM/dd/YYYY, which may pose a problem for you when trying any of the answers given here. You should convert 04/03/2011 to 2011-04-03. That being said, assuming your date column really is a MySQL date type, then the following query will give you all distinct "update" users:
SELECT t1.id, t1.user, t1.date, 'update' AS status
FROM antivirus t1
INNER JOIN
(
SELECT user, MAX(date) AS date
FROM antivirus
GROUP BY user
HAVING MAX(date) >= CURDATE()
) t2
ON t1.user = t2.user AND t1.date = t2.date
SQLFiddle
Note that in my Fiddle I had to modify the format of the date inputs from your sample table.
If you also want the "expired" users in the same query, than you can add UNION ALL plus the following:
SELECT t1.id, t1.user, t1.date, 'expired' AS status
FROM antivirus t1
INNER JOIN
(
SELECT user, MAX(date) AS date
FROM antivirus
GROUP BY user
HAVING MAX(date) < CURDATE()
) t2
ON t1.user = t2.user AND t1.date = t2.date
Thanks to #TimBiegeleisen for the answer..
I've tried this code and it works..
This code for showing the 'update' rows:
select a.* from tmp a
inner join
(select user, max(date) as date from tmp group by user)
b on a.user=b.user and a.date=b.date
where year(a.date +interval 1 year) >= year(current_date())
order by a.user
and this for showing the 'expired' rows:
select a.* from tmp a
inner join
(select user, max(date) as date from tmp group by user)
b on a.user=b.user and a.date=b.date
where year(a.date +interval 1 year) < year(current_date())
order by a.user
I hope my question and all the answers can help other's problems..
I am building a news feed from multiple tables status, events and tracks. The data retrieved from these tables should correspond to the user-id of all the users that I follow. On the face of it I thought this seemed simple enough and I could do this with a few joins.
Every row in each of the status, events and tracks table has unique ID and they are also unique from each other, this should make matters easier later. I have done this using a unique_id table with a primary key to retrieve ID's before inserting.
My trouble is upon joining everything together the values duplicate.
Example
If I have this data.
----------
**Status**
user-id = 1
id = 1
status = Hello Universe!
----------
**Events**
user-id = 1
id = 2
event-name = The Big Bang
----------
**Tracks**
user-id = 1
id = 3
track-name = Boom
----------
Assuming I follow user 1 I would want to retrieve this.
user-id ---- id ---- status ---- event-name ---- track-name
1 1 Hello NULL NULL
Universe
1 2 NULL The Big Bang NULL
1 3 NULL NULL Boom
But in reality what I would get is something like this.
user-id ---- status.id ---- events.id ---- tracks.id ---- status ---- event-name ---- track-name
1 1 2 3 Hello The Big Bang Boom
Universe
And that row would be repeated 3 times.
Most of the queries I have tried will get something along those lines.
SELECT * FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN status ON followers.`follows-id` = status.`user-id`
LEFT JOIN events ON followers.`follows-id` = events.`user-id`
LEFT JOIN tracks ON followers.`follows-id` = tracks.`user-id`
WHERE users.`id` = 2
I am using laravel, so eventually this query will be put into Eloquent format. If there is a simpler and a not performance degrading way of doing this in Eloquent please let me know.
Edit
I cannot use a UNION as there is a different number of values in each table. The example is simplified for ease of reading.
Thanks to Frazz for pointing out I could use UNIONS. I have researched into them and come up with this query.
SELECT stream.*, users.id AS me FROM users
INNER JOIN followers ON users.id = followers.`user-id`
LEFT JOIN (
SELECT `id`,`user-id`,`created_at`, `name`, NULL as status
FROM events
UNION ALL
SELECT `id`,`user-id`, `created_at`,NULL AS name, `status`
FROM status
) AS stream ON stream.`user-id` = `followers`.`follows-id`
WHERE users.id = 2
Now comes the process of converting it to an eloquent model...
I need to search for the updates sent by the friends of a giving user.
There is a table called friendship. It has a column called profile1 and another one called profile2. It represents the friendship between two users in this websystem, and a friendship is the presence of two giving ids, no matter in what position. So the profile with id 1 may have 2 friends, profile with id 2 and with id 3 as following:
friendship
profile1 profile2
1 2 <--
3 1 <--
2 5
...
Now I want to search for the updates sent by some user's friends. There is this table update
update
id content time profile
1 A text ... 2
2 A text ... 2
3 A text ... 3
4 A text ... 2
5 A text ... 3
6 A text ... 2
7 A text ... 10
8 A text ... 11
If my profile/user is identified by the id 1, and it has only 2 friends (the profiles identified by id 2 and 3) and also I need my search to return only 2 results by each user, my SELECT has to return updates 1,2,3 and 5.
Preferably updates should be grouped by its author and it would be great if I could set the number of different profiles to be considered in this search (for example, if profile 1 had 10 friends and I wanted only updates from 3 profiles, the most recent must appear first).
Do you know how can I achieve this??
thank you very much!
#EDIT
This returns all updates sent by friends of profile 1. But i'm not sure whether or not i'm in the right direction
SELECT u.*
FROM `update` u
INNER JOIN friendship f1 ON f1.profile1 = u.author
WHERE f1.profile2 =1
UNION
SELECT u.*
FROM `update` u
INNER JOIN friendship f2 ON f2.profile2 = u.author
WHERE f2.profile1 =1
If you are willing to do it in two queries, you can do it like this. First, get three profiles who have most recently posted based on your constraints:
-- Get the three latest updated profiles from here.
-- (we can't use a CTE because MySQL doesn't support
-- them yet).
SELECT DISTINCT p.profile FROM
(
SELECT ui.profile, ui.time FROM
(
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile2 = u.profile
WHERE f.profile1 = 1
UNION ALL
SELECT u.profile, u.time
FROM `update` u
INNER JOIN `friendship` f ON f.profile1 = u.profile
WHERE f.profile2 = 1
) ui ORDER BY ui.time DESC
) p LIMIT 0, 3;
From that query, get the three profile IDs out and put them in place of <id1>, <id2> and <id3> in the following query
-- Use a union to get the result set back
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id1>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id2>
ORDER BY a.time DESC
LIMIT 0, 2)
UNION ALL
(SELECT a.content, a.time, a.profile FROM `update` a
WHERE a.profile = <id3>
ORDER BY a.time DESC
LIMIT 0, 2);
If you get less than three profiles back, either remove parts of the query in your PHP code, or set the WHERE clause to something like 0 so it always evaluates to fault (assuming you don't have a profile ID of zero)
The 2 in the limit clauses above can be changed if you want more or fewer results per profile.
Sample SQL fiddle: http://sqlfiddle.com/#!2/22e57/1 (updated fiddle to make the content more meaningful and to use times)
I would suggest doing a series of queries for each author within one transaction, that way there would not be a need for grouping - you could simply append results together outside of your SQL.
SELECT * FROM `update` WHERE
profile IN (SELECT profile2 FROM `friendship` WHERE profile1=1) OR
profile IN (SELECT profile1 FROM `friendship` WHERE profile2=1);
try this sqlFiddle
SELECT T1.profile,T1.content,T1.time
FROM
(SELECT UPD.profile,UPD.content,UPD.time,
IF (#prevProfile != UPD.profile,#timeRank:=1,#timeRank:=#timeRank+1) as timeRank,
#prevProfile := UPD.profile
FROM
(SELECT UP.profile,UP.content,UP.time
FROM
(SELECT profile,max(time) as latestUpdateTime
FROM friendship F INNER JOIN updates U
ON (F.profile1 = 1 AND U.profile = profile2) /* <-- specify profile on this line */
OR(F.profile2 = 1 AND U.profile = profile1) /* <-- specify profile on this line */
GROUP BY profile
ORDER BY latestUpdateTime DESC
LIMIT 3 /* limit to 3 friends profiles that have the most recent updates */
)as LU
INNER JOIN updates UP
ON (UP.profile = LU.profile)
ORDER BY profile,time DESC
)as UPD,(SELECT #prevProfile:=0,#timeRank:=0)variables
)T1
WHERE T1.timeRank BETWEEN 1 AND 2 /* grab 2 lastest updates for each profile */
ORDER BY T1.time DESC
in my example, profile id 1 has more than 3 friends, but i am only grabbing 3 friends that made the most recent updates.
explanation of above query.
LU grabs 3 profiles that are friends with profile id 1 that made the latest updates.
UPD grabs all contents that belong to these 3 friends.
T1 returns the contents along with a timeRank number for each content in order from 1 counting upward order by time DESCENDING for each profile
and finally the WHERE we only grab 2 content updates for each profile
then we finally ORDER these updates based on TIME starting from most recent.
I am trying to build a project where you can like other people's pictures, and when the other person likes your picture too, you have a match. Like the Tinder app if you know.
Now, I fetch 1 photo like so:
SELECT id, picture_path, profile_picture, username
FROM tusers
WHERE profile_picture IS NOT NULL
AND settings LIKE '1,%'
AND sex = :sex
AND last_visit BETWEEN CURDATE() - INTERVAL 21 DAY AND CURDATE()
AND dob BETWEEN :dob - INTERVAL 5 YEAR AND :dob2 + INTERVAL 5 YEAR
LIMIT 1
However, if you've already LIKED or PASSED someone's photo, I don't want to show it to you again. I am not sure how to do this part yet (right now, I have alreadyLiked() and alreadyPassed() functions and I am only doing a header("Location") redirect if they return true, but that will fail when you have liked/passed all the photos).
I have another table with these columns: id, user1_id, user2_id, liked, passed, matched
When you like or pass a picture, a 1 is inserted in the corresponding column.
user1_id is your ID. user2_id is the other person's ID.
Knowing the above information, what kind of query (or logic) would you use to make sure that you only show the right people (that you haven't liked or passed already) ?
suppose you have 2 tables
usr
id username
1 a
2 b
3 c
4 d
liked
id user1 user2 liked
1 1 4 1
2 1 3 1
assuming your id is 1 , from table liked it seems you have liked c,d . since 1(a) is your own id you need only b as output, your query goes as below
SELECT *
FROM usr
WHERE id NOT
IN (
SELECT user2
FROM liked
WHERE user1 =1
)
and id!=1
assuming 1 will come from session
You can use mysql join to get data from one table based on the another
In the upper case you can have join on the first tables id with the other tables user1_id, user2_id putting where clause on the liked, passed, matched
To know more about mysql joins
Try this as join is much better than a inner query suggested by ersumit $loggedINuser_id='2';//take from session $sql="SELECT tu.id, tu.picture_path, tu.profile_picture, tu.username FROM tusers tu LEFT JOIN secondtable st ON tu.id=st.user2_id WHERE tu.profile_picture IS NOT NULL AND tu.settings LIKE '1,%' AND tu.sex = :sex AND tu.last_visit BETWEEN CURDATE() - INTERVAL 21 DAY AND CURDATE() AND tu.dob BETWEEN :dob - INTERVAL 5 YEAR AND :dob2 + INTERVAL 5 YEAR AND st.user1_id != '".$loggedINuser_id."' LIMIT 1";
Have 3 Tables... Need to select users that haven't booked a lesson for the next week, and send an email.
Think I'm looking at
WHERE `LESSONS`.`DATE` > CURDATE()
AND `LESSONS`.`DATE` <= (CURDATE() + INTERVAL 7 DAY)
That works to select the range of dates... However I'm then having a problem joining that to the bookings to see if they have booked, and if they HAVEN'T then I need to grab their email address... I'd rather not use PHP to loop through and query data. I obviously just want to run the single query.
As you can probably tell I'm not in the know when it comes to MYSQL.
Help appreciated. Thanks.
USERS
-----------
ID | EMAIL, ETC
LESSONS
-----------
ID | DATE, ETC
BOOKINGS
-----------
ID | LESSON_ID | USER_ID, ETC
You can try something like
SELECT u.*
FROM USERS u LEFT JOIN
Bookings b ON u.ID = b.USER_ID LEFT JOIN
LESSONS l ON b.LESSON_ID = l.ID
AND l.DATE > CURDATE() AND l.DATE <= (CURDATE() + INTERVAL 7 DAY)
WHERE l.ID IS NULL
This should get all users that do not have any bookings for lessons for next week.
Have a look at Introduction to JOINs – Basic of JOINs
This is a nive visual representation of joins.
use in or exists?
SELECT users.* FROM users
WHERE users.id NOT IN
(SELECT Bookings.user_id FROM Bookings JOIN Lessons ON Bookings.lesson_id = lessons.id
WHERE lessons.date > CURDATE() AND lessons.date <= (CURDATE()+INTERVAL7 7 DAY)
There may be syntactical differences in MySQL opposed to SQL server, that I'm more familiar with.