Select the row for the most recent day - mysql

I am trying to achieve the comment (title) written on the most recent day (review_date) from each user (username) in the database
My code is :
select tb1.*, tb2.* from
(select username, via_mobile as pc, max(review_date) as pcdate from tripadvisor where username != "" and via_mobile='false' group by username) tb1
join
(select username, via_mobile as mobile, max(review_date) as mobile from tripadvisor whereusername != "" and via_mobile='true' group by username) tb2
on tb1.username = tb2.username;
The problem is I cannot get the right review for the right date.
For example :
username; review_date; title
Alan; 2012-12-18 ;"If..."
But it should display Alan; 2012-12-18; "Nice hotel"
Can you help me to fix the code.

Your question is a little unclear, but if I understand correctly, you're looking for each full row with the highest date, selected distinctly/grouped by username? This should give you that:
SELECT
username,
review_date,
title
FROM
tripadvisor AS ta
INNER JOIN (
SELECT
username,
max(review_date) AS review_date
FROM
tripadvisor
GROUP BY
username
) AS max_table
ON ta.username = max_table.username
AND ta.review_date = max_table.review_date;
WHERE
username != ''
-- any extra where clauses go here
See: How can I SELECT rows with MAX(Column value), DISTINCT by another column in SQL?

If the goal is to return the most recent review title for "mobile" and for "pc", I'd do something like this:
SELECT q.username
, MAX(q.pc_date) AS pc_date
, MAX(p.title) AS pc_title
, MAX(q.mobile_date) AS mobile_date
, MAX(r.title) AS mobile_title
FROM ( SELECT t.username
, MAX(IF(t.via_mobile='false',t.review_date,NULL) AS pc_date
, MAX(IF(t.via_mobile='true',t.review_date,NULL) AS mobile_date
FROM tripadvisor t
WHERE t.username <> ''
AND t.via_mobile IN ('true','false')
GROUP
BY t.username
) q
LEFT
JOIN tripadvisor p
ON p.username = q.username
AND p.review_date = q.pc_date
AND p.via_mobile = 'false'
LEFT
JOIN tripadvisor r
ON r.username = q.username
AND r.review_date = q.mobile_date
AND r.via_mobile = 'true'
GROUP
BY q.username
If the user has only "mobile" reviews and no "pc" reviews, this query will return a row, but with NULL values for the "pc" columns. Similarly, the query will return NULL values for the "mobile" columns for a user that has only "pc" reviews.
The query could easily be changed to only returns rows for users that have both "mobile" and "pc" reviews, to be closer to the original using the INNER JOIN.
If the goal is simpler, just to return just the most recent review...
SELECT r.username
, r.review_date
, MAX(r.title) AS title
, MAX(r.via_mobile) AS via_mobile
FROM ( SELECT t.username
, MAX(t.review_date) AS max_review_date
FROM tripadvisor t
WHERE t.username <> ''
AND t.via_mobile IN ('true','false')
GROUP
BY t.username
) q
JOIN tripadvisor r
ON r.username = q.username
AND r.review_date = q.max_review_date
AND r.via_mobile IN ('true','false')
GROUP
BY r.username
, r.review_date
The results of this query are somewhat indeterminate when a username has multiple rows with identical (most recent) review_date. This guarantees a single row will be returned, but the title and via_mobile may not be from the same row.

Your question could be expressed as an existence test: show the rows for which the review date matches the latest review date for that user.
Existence is tested with EXISTS in a correlated subquery:
SELECT * FROM tripadvisor as T
where exists (
select 1 from tripadvisor
where username = T.username
group by username
having MAX(review_date) = T.review_date
);

Related

MySql throws error Subquery returns more than 1 row

I want to retrieve values from 3 table where i am getting error "Sub query returns more than 1 row " .
My concept is to retrieve all the post where i have to calculate the sum of votes from ttpostvotes table with respect to each post and if provided userid is voted for the that post then it will shows the post count like 1 or -1.
My query is as below:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes ,
(Select Votes From `ttpostvotes` where UserId=30 and x.PostId=r.PostId ) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount FROM `ttpost` r
left join ( SELECT PostId, sum(Votes) as Votes FROM `ttpostvotes` GROUP BY PostId ) x ON
x.PostId = r.PostId WHERE r.OffensiveCount<3 and r.SpamCount<5 and r.OtherCount<7 and r.`PeekId`=101 ORDER BY `r`.`PostTime` DESC
The 3 tables are like as below:
ttpost
ttpostvotes
ttreply
This is your select:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select Votes From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId=r.PostId ) AS ReplyCount
The first subquery has no aggregation, so I suppose a user could vote more than once for a post. This will fix the syntax error:
SELECT r.PostId, r.`Post`,r.PostTime, coalesce(x.Votes, 0) as Votes,
(Select SUM(Votes) From `ttpostvotes` where UserId = 30 and x.PostId = r.PostId
) as IsUservoted,
(Select Count(*) From ttreply where PostId = r.PostId ) AS ReplyCount
Whether it does what you want is a different question.
Note: if you want your original query to work, you should define a unique constraint/index on ttpostvotes:
create unique index unq_ttpostvotes_userid_postid on ttpostvotes(userid, postid);

Sql renaming several raw data

I have been through some solutions published here, but none of them solved my problem.
I want to set my username column to receive now unique usernames.
But for that I need to rename in certain situations, more than 1000 duplicated usernames already registered.
I tried this solution:
UPDATE profile n
JOIN (SELECT username, MIN(profile_id) min_id FROM profile
GROUP BY username HAVING COUNT(*) > 1) d
ON n.username = d.username AND n.profile_id <> d.min_id SET n.username = CONCAT(n.username, '1');
But it gives me for the same user name for example tony, tony1, tony11, tony111 and so on up to tony1111111111111... up to 1000, make the username have a long long lenght.
I would like a solution to get only up 4digites after the username word. 0001,0002,0003,0004....1000
Can somebody help me here?
Thank you in advance
how about something like:
UPDATE profile n JOIN (
SELECT profile_id, username, (#row_number:=#row_number+1) as cntr
FROM profile, (SELECT #row_number:=0) AS t
WHERE username IN ( SELECT username
FROM profile
GROUP BY username HAVING COUNT(*) > 1 )
and (username, profile_id) not in ( SELECT username, MIN(profile_id)
FROM profile
GROUP BY username HAVING COUNT(*) > 1 )
) d ON n.profile_id = d.profile_id
SET n.username = CONCAT(n.username, d.cntr);
This is the best I can come up with at the moment.... the problem is that it will share the counter between all usernames... you you will have Alejandro, Alejandro1, Pedro, Pedro2, Juan, Juan3 .....
I believe that this that you've commented is wrong...No Update. Only select: select * from profile n JOIN ( SELECT username, min_id, #row_number:=#row_number+1 as cntr FROM ( SELECT username, MIN(profile_id) min_id FROM profile GROUP BY username HAVING COUNT(*) > 1 ) AS t2 , (SELECT #row_number:=0) AS t ) d ON n.username = d.username

Using IF/ELSE in MySQL SELECT

I have a table of members and a table of venues.
I currently have this select statement:
SELECT VenueName, VenueID
FROM Venue
WHERE active='Y'
ORDER BY VenueName
The result of this statement is used to populate a drop down list with venue names, which works. I'll call it "ORIGINAL SELECT".
I want to change this so the dropdown list only shows venue names linked to the member:
SELECT Venue.VenueName, Venue.VenueID, members.id, members.venueid
FROM Venue, members
WHERE Venue.VenueID = members.venueid
AND members.id='$userid'
This also works. I'll call it "NEW SELECT".
I have two superadmins whose members.venueid is all rather than a venue id number, so I would like to create a statement that runs the NEW SELECT else if members.id='all' then run ORIGINAL SELECT.
Can anyone point me in the right direction? I've tried various things.
You could try something similar to the following:
SELECT Venue.VenueName, Venue.VenueID
FROM Venue, members
WHERE
(
Venue.VenueID = members.venueid
AND $userid <> 'all'
AND members.id = 'all'
)
OR
(
Venue.VenueID = members.id
AND $userid = 'all'
AND members.id = 'all'
)
Thanks #Chris you pointed me in the right direction, below is the final version that I got working:
SELECT DISTINCT Venue.VenueName, Venue.VenueID
FROM Venue, members
WHERE (
'$userid' = 'all'
AND active = 'Y'
)
OR (
Venue.VenueID = members.venueid
AND members.id = '$userid'
)
ORDER BY VenueName

Sql query to show only most recent message for specific user?

I have two sql tables one which has users and the other which has messages.
Right now my query is :
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid
FROM message,users
WHERE message.fromid = users.memberid AND message.toid = '$id' AND recieverdeleted='0'
ORDER BY datetime DESC LIMIT 55
Right now what is being returned is all the information for everymessage regardless of formid(sender's id)
The thing is , I want to only display the most recent message for every from id ..Kind of like how Facebook only shows you the most recent message that friend x has sent you, and not all the messages. I will work on showing all messages after the user has clicked on his friend's most recent message.
Thanks
Well, you tried. That's good.
SELECT u.memberid
, u.username
, u.profileimage
, u.gender
, m.messagebody
, m.fromid
, m.toid
, m.messageid
FROM users u
JOIN message m
ON m.fromid = u.memberid
JOIN (SELECT fromid,toid,MAX(datetime) max_datetime FROM message GROUP BY fromid,toid) n
ON n.fromid = m.fromid
AND n.toid = m.toid
AND n.max_datetime = m.datetime
WHERE message.toid = $id
AND recieverdeleted = 0
ORDER
BY datetime DESC LIMIT 55;
At first join the two tables than apply order by because at this moment the command is not sure for which table you want to apply the ordering.
Or better you create an intermediate table from the selection of two tables and then apply the ordering.
Something like:
SELECT username,messagebody,fromid FROM(
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid,message.datetime
FROM message,users
WHERE message.fromid = users.memberid AND message.toid = '$id' AND recieverdeleted='0' )INTERMEDIATE_TABLE ORDER BY datetime DESC
I might be wrong in syntax as I have done sql codes a long ago but you should try something very similar like this.
Try this its working
SELECT m1.*
FROM table_name m1
INNER JOIN (SELECT MAX(senddate) AS senddate,
IF(member_id2 = 3, member_id1, member_id2 ) AS user
FROM table_name
WHERE (member_id1 = 3 AND delete1=0) OR
(member_id2 = 3 AND delete2=0)
GROUP BY user) m2
ON m1.senddate = m2.senddate AND
(m1.member_id1 = m2.user OR m1.member_id2 = m2.user)
WHERE (member_id1 = 3 AND delete1=0) OR
(member_id2 = 3 AND delete2=0)
ORDER BY m1.senddate DESC
Try this::
SELECT users.memberid,users.username,users.profileimage,users.gender,message.messagebody, message.fromid,message.toid,message.messageid
FROM message inner join users on (message.fromid = users.memberid)
where message.toid = '$id' AND recieverdeleted='0'
ORDER BY message_datetime DESC limit 1

mysql where condition priority

I have the following query and it gets the first result "Edit: of each contact" as I want but I do not need any other results per contact after. So example if they have 3 phone # and one is main, it gives me the main then the other 2. I have tried grouping but it ends up getting the first one pulled no matter if main or not. Any thoughts to how I could do this without doing a php check in the loop for if contact already exists?
SELECT pc.firstname, pc.lastname, pp.areacode, pp.prefix, pp.last4
, if(pc.mainphone = pp.phoneid,1,0) as phone_priority
FROM contacts pc
JOIN phone pp ON ( (pp.contid = pc.contid && pp.phoneid = pc.mainphone) || (pp.contid = pc.contid) )
ORDER BY pc.lastname ASC, pc.firstname ASC, phone_priority DESC
Table setup:
contacts (id, firstname, lastname, mainphone)
phone (id, areacode, prefix, last4)
Using MySQL 4.
Use a LIMIT expression to narrow the result set down to the first result. See SELECT Syntax for details.
Use LIMIT 1 at the end of your query
Use a left join:
SELECT pc.firstname,
pc.lastname,
pp.areacode,
pp.prefix,
pp.last4 ,
CASE
WHEN pp.mainphone IS NULL THEN 0
ELSE 1
END AS phone_priority
FROM contacts pc LEFT JOIN phone pp
ON pp.contid = pc.contid
AND pp.phoneid = pc.mainphone
ORDER BY pc.lastname ASC, pc.firstname ASC, phone_priority DESC
Try this variant -
SELECT t.contid
, t.firstname
, t.lastname
, if(t.phoneid IS NULL, p2.phoneid, t.phoneid) phone
, if(t.phoneid IS NULL, p2.areacode, t.areacode) areacode
, if(t.phoneid IS NULL, p2.prefix, t.prefix) prefix
, if(t.phoneid IS NULL, p2.last4, t.last4) last4
FROM
(
SELECT c.contid
, c.firstname
, c.lastname
, c.mainphone
, p.phoneid
, p.areacode
, p.prefix
, p.last4
FROM
contacts c
LEFT JOIN phone p
ON c.contid = p.contid AND c.mainphone = p.phoneid) t
LEFT JOIN phone p2
ON t.contid = p2.contid AND t.mainphone <> p2.phoneid
GROUP BY
t.contid