I have a table that stores all users connections ( date & ip ) and i want to retrieve with a single query all the users data (nickname , avatar ...) + the last record of my connections history table of this user ...
SELECT
*
FROM
`users`
LEFT JOIN
`connections_history` ON `users`.`id` = `connections_history`.`guid`
How i can proceed thx
Assuming that connections_history table has an AUTO_INCREMENT column id:
SELECT *
FROM (
SELECT u.*, MAX(h.id) as hid
FROM users u
LEFT JOIN connections_history h ON u.id = h.guid
GROUP BY u.id
) u
LEFT JOIN connections_history h ON h.id = u.hid
Unfortunately Mysql does not support window functions, you need Correlated sub-query to do this.
Try something like this
SELECT *
FROM users
LEFT JOIN connections_history ch
ON users.id = ch.guid
AND EXISTS (SELECT 1
FROM connections_history ch1
WHERE ch.guid = ch1.guid
HAVING Max(ch1.date) = ch.date)
One way is finding the rows with max date for each guid in subquery and then join with users table.
Like this:
select *
from `users` u
left join (
select *
from `connections_history` c
where date = (
select max(date)
from `connections_history` c2
where c.`guid` = c2.`guid`
)
) t on u.`id` = t.`guid`;
You can do this with a correlated subquery in the ON clause:
SELECT u.*, ch.*
FROM `users` u LEFT JOIN
`connections_history` ch
ON ch.`guid` = u.`id` AND
ch.date = (SELECT MAX(ch2.date)
FROM connections_history ch2
WHERE ch.guid = ch2.guid
);
This formulation allows the query to take advantage of an index on connections_history(guid, date).
Related
Following query shows the results I need from table pointsList (latest records per user grouped by column idMatch)
select * from pointsList p
inner join ( select idMatch, max(datePointsCalculated ) as MaxDate from pointsList group by idMatch ) tm
on p.idMatch = tm.idMatch and p.datePointsCalculated = tm.MaxDate
order by p.idMatch ASC
But now I want to also select some additional information about the user (from table users). My naive way to tackle this was by making a new inner join like this:
select * from pointsList p, users u
inner join users
on u.idUser = p.idUser
inner join ( select idMatch, max(datePointsCalculated ) as MaxDate from pointsList group by idMatch ) tm
on p.idMatch = tm.idMatch and p.datePointsCalculated = tm.MaxDate
order by p.idMatch ASC
But I get the error message "unknown column 'p.idUser' in on clause". I tried using users.idUser and pointsList.idUser and other combinations (renaming pointsList in the on-clouse) but I always get the unknown column error (pointsList.idUser really does exist). Anyone could explain what I am doing wrong? I would like to extend this query to another table as well.
Thank you in advance!
Comma , priority in FROM clause is less than JOIN priority. And your query acts as:
select * from pointsList p,
(
users u
inner join users
on u.idUser = p.idUser
inner join ( select idMatch, max(datePointsCalculated ) as MaxDate from pointsList group by idMatch ) tm
on p.idMatch = tm.idMatch and p.datePointsCalculated = tm.MaxDate
)
order by p.idMatch ASC
Of course, table pointsList AS p is not accessible within the parenthesis.
Use CROSS JOIN instead of comma:
select * from pointsList p
CROSS JOIN users u
inner join users
on u.idUser = p.idUser
inner join ( select idMatch, max(datePointsCalculated ) as MaxDate from pointsList group by idMatch ) tm
on p.idMatch = tm.idMatch and p.datePointsCalculated = tm.MaxDate
order by p.idMatch ASC
I have the following query:
SELECT u.id, u.name, u.date_registered, p.time_created
FROM users u JOIN
prospect_notes p
ON u.id=p.subject_id
WHERE u.allocated_instructors = 668
AND p.time_created = (SELECT MAX(p2.time_created) FROM prospect_notes p2 WHERE p2.subject_id = p.subject_id)
ORDER BY p.time_created;
My problem is that when there are no rows in the prospect_notes table which match the following:
AND p.time_created = (SELECT MAX(p2.time_created) FROM prospect_notes p2 WHERE p2.subject_id = p.subject_id)
I get no result.
Instead, I want all the rows in the users table to return (presumably p.time_created would be NULL in such cases).
You need to be careful because of the JOIN clause. Presumably, if there are no matches for the correlated subquery, there are no matches in the JOIN either.
So, use LEFT JOIN and move the logic to the FROM clause:
SELECT u.id, u.name, u.date_registered, p.time_created
FROM users u LEFT JOIN
prospect_notes p
ON u.id = p.subject_id LEFT JOIN
(SELECT p2.subject_id, MAX(p2.time_created) as max_time_created
FROM prospect_notes p2
GROUP BY p2.subject_id
) p2
ON p2.subject_id = p.subject_id AND p2.time_created = p.time_created
WHERE u.allocated_instructors = 668
ORDER BY p.time_created;
That said, if you only want time_created from prospect_notes, then use a simpler query:
SELECT u.id, u.name, u.date_registered, MAX(p.time_created)
FROM users u LEFT JOIN
prospect_notes p
ON u.id = p.subject_id
WHERE u.allocated_instructors = 668
GROUP BY u.id -- okay, assuming id is unique or a primary key
ORDER BY MAX(p.time_created);
You need LEFT JOIN this clause permit you to have NULL values in the right table.
Try :
SELECT u.id, u.name, u.date_registered, p.time_created
FROM users u
LEFT JOIN prospect_notes p
ON u.id=p.subject_id
WHERE u.allocated_instructors = 668
AND p.time_created = (SELECT MAX(p2.time_created) FROM prospect_notes p2 WHERE p2.subject_id = p.subject_id)
ORDER BY p.time_created;
I have a query with one LEFT JOIN that works fine. When I add a second LEFT JOIN to a table with multiple records per field in the first table, however, I am getting the product of the results in the two tables ie books x publishers returned. How can I prevent this from happening?
SELECT a.*,b.*,p.*, group_concat(b.id as `bids`)
FROM authors `a`
LEFT JOIN books `b`
ON b.authorid = a.id
LEFT JOIN publishers `p`
on p.authorid = a.id
GROUP by a.id
EDIT:
Figured it out. The way to do this is to use subqueries as in this answer:
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;
If you do multiple LEFT Joins, your query will return a cartesian product of the results. To avoid this and get only one copy of fields you desire, do a subquery for each table you wish to join as below. Hope this helps someone in the future.
SELECT u.id
, u.account_balance
, g.grocery_visits
, f.fishmarket_visits
FROM users u
LEFT JOIN (
SELECT user_id, count(*) AS grocery_visits
FROM grocery
GROUP BY user_id
) g ON g.user_id = u.id
LEFT JOIN (
SELECT user_id, count(*) AS fishmarket_visits
FROM fishmarket
GROUP BY user_id
) f ON f.user_id = u.id
ORDER BY u.id;
I am trying to select the latest row from a LEFT JOIN not on the main query. This is my SQL:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I want to select the latest row from:
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
My column is called: message_reply_date - how can I use that to LEFT JOIN the latest row?
message_replies:
CREATE TABLE IF NOT EXISTS `message_replies` (
`message_reply_id` int(11) NOT NULL,
`message_reply_user` int(11) NOT NULL,
`message_reply_main` int(11) NOT NULL,
`message_reply_message` text NOT NULL,
`message_reply_date` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`message_reply_read` int(11) NOT NULL DEFAULT '0'
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
I am using this for the WHERE clause:
WHERE m.message_user = ?
OR m.message_to = ?
AND m.message_deleted=0
AND m.message_permdeleted=0
GROUP BY mr.message_reply_main
ORDER BY mr.message_reply_date DESC
If i understood the question, i'll do it this simple way:
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id OR messages.message_user = users.user_id
LEFT JOIN message_replies
ON messages.message_id = message_replies.message_reply_main
LEFT JOIN user_personal_information
ON users.user_id = user_personal_information.user_personal_information_user
WHERE message_replies.message_reply_date = (SELECT MAX(message_reply_date) FROM message_replies WHERE message_reply_main = messages.message_id)
/* AND more criterias */
No grouping in the main query but just using a MAX evaluation of message_reply_date in a subquery regarding the WHERE criteria on message_reply_date itself.
There is a "left join" method for getting the most recent message (as well as several others). But in keeping with the preferred join method of the question:
SELECT *
FROM messages m LEFT JOIN
users u
ON m.message_to = u.user_id OR m.message_user = u.user_id LEFT JOIN
message_replies mr
ON m.message_id = mr.message_reply_main LEFT JOIN
user_personal_information upi
ON u.user_id = upi.user_personal_information_user LEFT JOIN
message_replies mr2
ON mr2.message_reply_main = m.message_id AND
mr2.message_reply_date > mr.message_reply_date
WHERE mr2.message_reply_main IS NULL;
I also added table aliases because they make a query easier to write and to read.
The idea is to match to the table again, but only for later messages. Then, the WHERE clause checks that none exist -- so it has the latest one.
Give this a go.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN ( SELECT *
, MAX(message_reply_date) OVER (PARTITION BY message_id) AS most_recent_message_reply_date
FROM message_replies ) message_replies
ON messages.message_id = message_replies.message_reply_main
AND message_replies.message_reply_date = message_replies.most_recent_message_reply_date
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
I've substituted your direct call to the message_replies table with a Select that displays the table and the maximum reply date grouped by the message_id. I've then included this in the join to filter all answers other than the one your looking for.
Have a go, any problem drop me a message, always happy to help.
What you want to do is emulate the rank & partition functionality seen in MSSQL & PL/SQL, essentially ranking on date & partitioning by message_reply_main.
This can be done in MySQL using an outer join & a count on your partition, while using a < or > in the join for the ranking criteria. This allows you to do your standard join, but also where rank = 1, returning the largest date.
SELECT *
FROM messages
LEFT JOIN users
ON messages.message_to = users.user_id
OR messages.message_user = users.user_id
LEFT JOIN (select mri.message_reply_main
, mri.message_reply_date
, mri.message_reply_message
, mri.message_reply_read
, count(mri2.message_reply_main) + 1 as rank
FROM message_replies mri
left join message_replies mri2 on mri.message_reply_date < mri2.message_reply_date
and mri.message_reply_main = mri2.message_reply_main
group by mri.message_reply_main, mri.message_reply_message
, mri.message_reply_read, mri.message_reply_date) mr ON messages.message_id = mr.message_reply_man
AND mr.rank = 1
LEFT JOIN user_personal_information
ON users.user_id =
user_personal_information.user_personal_information_user
Is there anyway to select all from 1 table based on the result of one query which contains multiple values without having to do 2 separate queries?
Say long join query will produce a list of id's
SELECT xyz FROM table long join query WHERE id = array of ids found in result table
added example:
SELECT * FROM tweets as t
where t.user_id
in(
SELECT uff.id, uff.username
FROM users as uf
LEFT JOIN followlinks as fl
ON uf.id = fl.user_id
LEFT JOIN users as uff
ON fl.follow_id = uff.id
WHERE uff.id = 1
)
The bit in the parenthesis returns an id and user name of who the user is following (uff.id=1)
How do i then get all 'tweets' by all the id's in the generated resultset
You can use subquery:
SELECT * FROM `table1` WHERE `id` IN (SELECT `table2`.id FROM `table2` )
You might want to check documentation for syntax
SELECT xyz FROM table_A join table_B WHERE table_A.id IN (SELECT ID FROM table_C);
I think you mean something like
edited after the OP edit
SELECT * FROM tweets as t
WHERE t.user_id
in(
SELECT uff.id //remove the second field, you just need the id
FROM users as uf
LEFT JOIN followlinks as fl
ON uf.id = fl.user_id
LEFT JOIN users as uff
ON fl.follow_id = uff.id
WHERE uff.id = 1
)
After trying the in clause I coudnt get the results I was after but after rethinking what I was trying to do I got my results with an additional join clause
SELECT uff.username, t.content
FROM users as uf
JOIN followlinks as fl
ON uf.id = fl.user_id
JOIN users as uff
ON fl.follow_id = uff.id
JOIN tweets as t
ON t.user_id = uff.id
WHERE uf.id = 1