selecting only distinct row based on highest timestamp on a join - mysql

I perform a join on these two tables in mysql
Challenges table
challenge_ID(int) |to_user(int)|from_user(int)|timestamp
Users table
iduser(int)|email(string)
My join query is this :
Select distinct u.email,c.challenge_id,c.status,c.timestamp from
test.challenges c join test.users u
on
c.to_user=u.iduser
where
c.from_user=9 and (c.status='open' || c.status='rejected')
Order by
c.timestamp DESC
The result what I get from this query is
email |challenge_id| status |timestamp (Descending)
Dan21#rab.edu 5 open 2015-12-09 21:20:26
tommy52#gump.com 4 open 2015-12-09 21:10:22
Dan21#rab.edu 1 rejected 2015-12-08 12:27:00
Notice how Dan21#rab.edu is repeated twice, I want it to display only once and the one displayed should have the latest timestamp i.e.
email |challenge_id| status |timestamp (Descending)
Dan21#rab.edu 5 open 2015-12-09 21:20:26
tommy52#gump.com 4 open 2015-12-09 21:10:22

You should write your query as:
Select u.email, c.challenge_id, c.status, c.timestamp
from test.challenges c join
test.users u
on c.to_user = u.iduser
where c.from_user = 9 and c.status in ('open', 'rejected')
Order by c.timestamp DESC;
Don't use select distinct unless necessary. Then you can do what you want in various ways. Because you have a join and other conditions, I think variables might be the simplest way:
select cu.*
from (Select u.email, c.challenge_id, c.status, c.timestamp,
(#rn := if(#e = u.email, #rn + 1,
if(#e := u.email, 1, 1)
)
) as rn
from test.challenges c join
test.users u
on c.to_user = u.iduser cross join
(select #e := '', #rn := 0) params
where c.from_user = 9 and c.status in ('open', 'rejected')
order by u.email, c.timestamp DESC
) cu
where rn = 1;
EDIT:
I thought the above worked with join and the variables in a single query. But, sometimes MySQL gets confused and you need to use a subquery with variables:
select cu.*
from (select cu.*,
(#rn := if(#e = u.email, #rn + 1,
if(#e := u.email, 1, 1)
)
) as rn
from (Select u.email, c.challenge_id, c.status, c.timestamp,
from test.challenges c join
test.users u
on c.to_user = u.iduser
where c.from_user = 9 and c.status in ('open', 'rejected')
order by u.email, c.timestamp DESC
) cu cross join
(select #e := '', #rn := 0) params
) cu
where rn = 1;

I figure out a query for this which works , not sure if its foolproof or most efficient way to do this
Here it is tho
Select distinct u.email,c.challenge_id,c.status,c.timestamp
from
(select ch.challenge_id,ch.status,ch.from_user,ch.to_user,timestamp,
(select Max(timestamp) from challenges
where to_user=ch.to_user
and from_user=9
) as latest
from test.challenges ch
)c
join test.users u
on
c.to_user=u.iduser
and
c.latest=c.timestamp
where (c.status='open' || c.status='rejected')

Related

Get ranking on mysql produce wrong ranks

i'm trying to get ranking based on rating percentage so mysql query like
select c.id , sum((r.value * 20))/ count(r1.pagetypeid) as score, #curRank := #curRank + 1 AS rank from (SELECT #curRank := 0) cr, rating as r
inner join rateelement as r1 on r.elementid = r1.id
inner join ratesubscription as r2 on r.subscriptionid = r2.id
inner join consultant as c on r2.consultantid = c.id
where r1.displayorder not in (6) and r2.agencyid = 38
group by c.id order by score desc
but it returns wrong raking indexes
what's wrong with the query?
Ranking with variables often has issues with group by -- and even order by in the most recent versions of MySQL. So, use a subquery:
select x.*, (#curRank := #curRank + 1) AS rank
from (select c.id, sum((r.value * 20))/ count(r1.pagetypeid) as score
from rating r inner join
rateelement r1
on r.elementid = r1.id inner join
ratesubscription r2
on r.subscriptionid = r2.id inner join
consultant c
on r2.consultantid = c.id
where r1.displayorder not in (6) and r2.agencyid = 38
group by c.id
order by score desc
) x cross join
(SELECT #curRank := 0) cr;

Mysql - dynamic UNION with multiple recieve ids

how to convert this multiple UNION to simple query?
number of artist_id is dynamic.
45,122,95
or
100,20
or
89,9449
or
22,495,700,98
....
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 45
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 122
ORDER BY b.`date` DESC
LIMIT 5)
UNION ALL
(SELECT b.`id`, b.`id`, b.`date`, b.`artist`, b.`title`, b.`photo`
FROM `tags_mp3s` a
INNER JOIN `mp3s` b ON b.`id` = a.`mp3_id` AND a.`artist_id` = 95
ORDER BY b.`date` DESC
LIMIT 5)
.....
thanks
In MySQL, you can use variables:
SELECT ta.*
FROM (SELECT m.*,
(#rn := if(#a = t.artist_id, #rn + 1,
if(#a := t.artist_id, 1, 1)
)
) as rn
FROM tags_mp3s t INNER JOIN
mp3s m
ON m.id = t.mp3_id CROSS JOIN
(SELECT #rn := 0, #a := -1) params
WHERE t.artist_id IN (. . . )
ORDER BY t.artist_id, m.date DESC
) ta
WHERE rn <= 5;

Rank function in MySQL assign a rank

I need to assign a rank to TC5 not the id.
SELECT a.id, a.user_id, a.tc1, a.tc4, min(a.tc5), a.tc2, b.avatar, c.username, #curRank := #curRank + 1 AS Rank
FROM
treningove_casy a INNER JOIN
sn_users b ON a.user_id=b.id INNER JOIN
users c ON a.user_id=c.id , (SELECT #curRank := 0) r
WHERE a.tc2 LIKE 'Motokáry Modrice'
GROUP BY a.user_id
So how is the picture. Please help
The modified code
SELECT x.*, (#curRank := #curRank + 1) as Rank
FROM (SELECT a.id, a.user_id, a.tc1, a.tc4, min(a.tc5) as tc5,
a.tc2, b.avatar, c.username,
FROM sbhgl_chronoengine_chronoforms_datatable_treningove_casy a INNER JOIN
sbhgl_jsn_users b
ON a.user_id = b.id INNER JOIN
sbhgl_users c
ON a.user_id = c.id
WHERE a.tc2 LIKE 'Motokáry Modřice'
GROUP BY a.user_id
) x CROSS JOIN
(SELECT #curRank := 0) params
ORDER BY tc5 DESC;
SELECT a.id, a.user_id, a.tc1, a.tc4, a.tc2, b.avatar, c.username,
(select rank from (SELECT
IF (#score=s.tc5, #rank:=#rank, #rank:=#rank+1) rank,
#score:=s.tc5 tc5s
FROM treningove_casy s,
(SELECT #score:=0, #rank:=0) r
ORDER BY tc5 DESC) s ) as rank
FROM
treningove_casy a INNER JOIN
sn_users b ON a.user_id=b.id INNER JOIN
users c ON a.user_id=c.id , (SELECT #curRank := 0) r
WHERE a.tc2 LIKE 'Motokáry Modrice'
GROUP BY a.user_id
You can try above solution, Hope this will help you.
In MySQL, you typically use variables for ranks. In your case, you would use a subquery. I find that the variable method doesn't always work with GROUP BY:
SELECT x.*, (#curRank := #curRank + 1) as Rank
FROM (SELECT a.id, a.user_id, a.tc1, a.tc4, min(a.tc5) as tc5,
a.tc2, b.avatar, c.username,
FROM treningove_casy a INNER JOIN
sn_users b
ON a.user_id = b.id INNER JOIN
users c
ON a.user_id = c.id
WHERE a.tc2 LIKE 'Motokáry Modrice'
GROUP BY a.user_id
) x CROSS JOIN
(SELECT #curRank := 0) params
ORDER BY tc5 DESC;
Note: The use of table aliases is good. It is much easier to understand a query, though, if the aliases are abbreviations for the table names.

Running counter for tickets in SQL

I am attempting to have a running counter as the first column to easily see how many tickets are being assigned to an agent. The number emulator works but instead of raising the ticket number for the agent it counts the ticket date. My current code is:
Set #row_num = 0;
SELECT #row_num := #row_num + 1 as row_number
, s.firstname
, s.lastname
, t.number
, ts.name AS status
, o.name, t.created
FROM ost_ticket t
JOIN ost_ticket_status ts ON t.status_id = ts.id
JOIN ost_staff s ON t.staff_id = s.staff_id
JOIN ost_user u ON t.user_id = u.id
JOIN ost_organization o ON u.org_id = o.id
JOIN (SELECT #row_num:= 0) N
WHERE s.staff_id IN (27,35,43,45)
AND t.created >= CURDATE() - INTERVAL 7 DAY
ORDER BY s.lastname
How best could I change it where it would have 1, 2, 3, for the agent and then reset for a new agent?
You need a diferent variable to keep track of when agent change.
SET #row_num := 0;
SET #agent :='';
SELECT
#row_number := IIF(#agent = s.firstname + s.lastname,
#row_number + 1, 1
) AS row_number,
#agent := s.firstname + s.lastname AS agent,
s.firstname,
s.lastname,
t.number,
ts.name AS status,
o.name,
t.created
FROM ost_ticket t
JOIN ost_ticket_status ts ON t.status_id = ts.id
JOIN ost_staff s ON t.staff_id = s.staff_id
JOIN ost_user u ON t.user_id = u.id
JOIN ost_organization o ON u.org_id = o.id
WHERE s.staff_id IN (27,35,43,45)
AND t.created >= CURDATE() - INTERVAL 7 DAY
ORDER BY s.lastname

Mysql how limit left join results

I have two tables: songs and groups
i want limit the songs are match the group to 3
i tried this:
SELECT
groups.`IDgroup`,
groups.`name` AS g_name,
songs.`IDsong`,
songs.`name` AS s_name
FROM `groups`
LEFT OUTER JOIN songs ON (groups.`IDgroup` = songs.`IDgroup` LIMIT 3)
Put the limit out of parentheses:
SELECT
groups.`IDgroup`,
groups.`name` AS g_name,
songs.`IDsong`,
songs.`name` AS s_name
FROM `groups`
LEFT OUTER JOIN songs
ON groups.`IDgroup` = songs.`IDgroup`
LIMIT 3
It is generally not a good idea to put a limit on a query that does not explicitly order its results. The reason is that it could return different results over time.
So, you may want to consider adding an
ORDER BY groups.IDgroup, songs.IDsong
to your query (before the LIMIT 3), assuming that this combination is unique.
SELECT
g.`IDgroup`,
g.`name` AS g_name,
s.`IDsong`,
s.`name` AS s_name
FROM `groups` g
LEFT OUTER JOIN songs s
using ('idgroup')
LIMIT 3
This query will return the last 3 songs for each group:
SELECT
c.`IDgroup`,
c.`name` AS g_name,
s.`IDsong`,
s.`name` AS s_name
FROM
groups c
JOIN (
SELECT
IF(#C != c.IDgroup, #ROWNUM := 1, #ROWNUM := #ROWNUM +1) AS RN,
#C := c.IDgroup,
c.IDgroup,
s.IDsong,
s.name
FROM groups c
LEFT JOIN songs s ON c.`IDgroup` = s.`IDgroup`
CROSS JOIN (SELECT #C := '') t2
ORDER BY c.IDgroup ASC
) s ON c.`IDgroup` = s.`IDgroup`
JOIN JOIN (
SELECT IDgroup, MAX(rn) AS mx
FROM (
SELECT
IF(#C != c.IDgroup, #ROWNUM := 1, #ROWNUM := #ROWNUM +1) AS rn,
#C := c.IDgroup,
c.IDgroup
FROM groups c
LEFT JOIN songs s ON c.`IDgroup` = s.`IDgroup`
CROSS JOIN (SELECT #C := '') t2
ORDER BY c.IDgroup ASC
) t
GROUP BY IDgroup
) maxsong ON maxsong.IDgroup = c.IDgroup AND s.rn BETWEEN maxsong.mx-2 AND maxsong.mx
ORDER BY c.IDgroup ASC, s.`name` ASC
Fiddle: http://sqlfiddle.com/#!2/b65c3b/1
Take the LIMIT out of the parentheses:
SELECT
groups.`IDgroup`,
groups.`name` AS g_name,
songs.`IDsong`,
songs.`name` AS s_name
FROM `groups`
LEFT OUTER JOIN songs USING (`IDgroup`)
LIMIT 3