Current Table Events:
| eventId | personId | type | title | score |
+-----------+------------+--------+---------+---------+
| 1 | 1 | movie | Mission | 12 |
| 2 | 1 | movie | UNCLE | 32 |
| 3 | 1 | show | U2 | 17 |
| 4 | 1 | show | Leroy | 13 |
| 5 | 2 | movie | Holmes | 19 |
| 6 | 2 | movie | Compton | 14 |
| 7 | 2 | show | Imagine | 22 |
| 8 | 2 | show | Kane | 22 |
MySQL Example:
SELECT #personId:=personId as personId,
(
SELECT title FROM Events
WHERE rate = (SELECT MAX(score) FROM Events)
AND type = ‘movie’ AND personId=#personId
) as movie,
(
SELECT title FROM Events
WHERE rate = (SELECT MAX(score) FROM Events)
AND type = ‘movie’ AND personId=#personId
) as show,
FROM Events
GROUP BY personId
ORDER BY personId;
Desired Output:
| personId | movie | show |
+------------+----------+---------+
| 1 | UNCLE | U2 |
| 2 | Holmes | Imagine |
The desired result is to show the MAX() score for movie and show per personId in the Events table. My actual output contains NULLS and takes a very long time to load. My actual Events table has around 20,000 entries.
UPDATE Solution (derived from first two answers to increase performance)
SELECT e.personId,
(
SELECT o.title
FROM Events o
LEFT JOIN Events b
ON o.personId = b.personId AND o.score < b.score
AND o.type = b.type
WHERE o.type = 'movie' AND o.personId=e.personId LIMIT 1
) as best_movie ,
(
SELECT o.title
FROM Events o
LEFT JOIN Events b
ON o.personId = b.personId AND o.score < b.score
AND o.type = b.type
WHERE o.type = 'show' AND o.personId=e.personId LIMIT 1
) as best_show
FROM Events e
GROUP BY e.personId
ORDER BY e.personId
I made few modifications to your query.
Have a look:
SELECT #personId:=personId as personId,
(
SELECT title FROM Events
WHERE score = (SELECT MAX(score) FROM Events WHERE type = 'movie'
AND personId=#personId)
AND type = 'movie' AND personId=#personId LIMIT 1
) as movie ,
(
SELECT title FROM Events
WHERE score = (SELECT MAX(score) FROM Events WHERE type = 'show'
AND personId=#personId)
AND type = 'show' AND personId=#personId LIMIT 1
) as show1
FROM Events
GROUP BY personId
ORDER BY personId
I am not sure if it is exactly what you need.
But just my guess, probably you don't need multiple columns?
http://sqlfiddle.com/#!9/04b27/4
SELECT e.*
FROM `events` e
left join `events` e1
on e.type = e1.type
and e.score<e1.score
WHERE e1.eventId IS NULL
GROUP BY personId, type, score
Update If you need the max score per person + type you can
http://sqlfiddle.com/#!9/04b27/13
SELECT e.*
FROM `events` e
left join `events` e1
on e.personId = e1.personId
and e.type = e1.type
and e.score<e1.score
WHERE e1.eventId IS NULL
GROUP BY personId, score
Related
Lets say I have a table called events
event_id | event_name | event_time
---------|------------|-----------
1 | Holiday | 09:00
2 | Meeting | 10:00
Then I have a table called attendees
attendee_id | event_id | person_id
------------|----------|----------
1 | 1 | 19
2 | 1 | 28
3 | 1 | 89
4 | 1 | 100
5 | 2 | 7
6 | 2 | 19
7 | 2 | 22
8 | 2 | 28
Weirdly, I need to return the event and all of its attendees, if I have a match on just a single person. If there is no match, I don't want the event returned at all.
SELECT events.*, GROUP_CONCAT(attendee_id, event_id, person_id SEPARATOR ',') AS attendees
FROM events
LEFT JOIN attendees ON events.event_id = attendees.event_id
WHERE attendees.person_id IN (89)
GROUP BY event_id
This currently returns the event with just the single attendee in the 'attendees' column.
I know it seems counter intuitive, but is there a way I can actually return the event and all of the attendees included on the event without doing further filtering downstream?
Expected Result
event_id | event_name | event_time | attendees
-------- | ---------- | ---------- | ---------
1 | Holiday | 09:00 | 19,28,89,100
I think that you need to change the WHERE clause:
WHERE attendees.event_id IN (SELECT event_id FROM attendees WHERE person_id = 19)
So use this:
SELECT
e.event_id,
GROUP_CONCAT(a.person_id SEPARATOR ',') AS attendees
FROM events e LEFT JOIN attendees a
ON e.event_id = a.event_id
WHERE a.event_id IN (SELECT event_id FROM attendees WHERE person_id = 19)
GROUP BY e.event_id
See the demo.
Results:
| event_id | attendees |
| -------- | ------------ |
| 1 | 28,89,100,19 |
| 2 | 7,19,22,28 |
If you only want events that have attendees, the do not use the LEFT JOIN in your ON clause.
Also, remove the where clause unless you only want a specific person/attendee in your results.
Note that in the example, although you did not show the person table, I've added it and referenced one column from that table. Adjust if you like to use the actual column name.
SELECT
a.*,
GROUP_CONCAT(
a.attendee_id,
b.event_id,
b.person_id ,
c.person_name
SEPARATOR ',') AS attendees
FROM events a
JOIN attendees b
ON a.event_id = b.event_id
JOIN persons c
ON b.person_id = c.person_id
WHERE b.person_id IN (19) /* Remove unless you are looking for a specific person/attendee */
GROUP BY event_id
I have been trying to select all things from one table and then for each result select from another table only one newest result using MySQL.
The first table is a standard one, with AI id and text name
users
+----+-------+
| id | name |
+----+-------+
| 1 | John |
| 2 | Peter |
+----+-------+
then there is the second one with AI id, int user_id, text action and datetime date
actions
+----+---------+--------+------------+-----+
| id | user_id | action | date | str |
+----+---------+--------+------------+-----+
| 7 | 2 | drink | 2019-01-10 | 5 |
| 6 | 1 | sleep | 2019-02-14 | -2 |
| 5 | 2 | walk | 2019-04-24 | 4 |
| 4 | 1 | jump | 2019-03-14 | 3 |
| 3 | 2 | talk | 2019-04-30 | -8 |
| 2 | 2 | train | 2019-04-14 | -1 |
| 1 | 1 | drive | 2019-04-01 | 1 |
+----+---------+--------+------------+-----+
So now I want to select all from table_users and for each found row search table_actions and find only newest one, either based on id or date.
So it would look either like this (by id)
[0] => ['user_id'] = 1
['name'] = John
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
[1] => ['user_id'] = 2
['name'] = Peter
['action'] = drink
['date'] = 2019-01-10
['str'] = 5
or like this
[0] => ['id'] = 1
['name'] = John
['table_actions'] => ['id'] = 6
['user_id'] = 1
['action'] = sleep
['date'] = 2019-02-14
['str'] = -2
this sounds easy, but I tried few things and nothing worked like this. The closes one was with something like (dont have exact version on my hand, just remembering from on top of my head):
SELECT users.*, actions.*
FROM users
LEFT JOIN actions ON users.id = (
SELECT user_id FROM actions
WHERE users.id = actions.user_id
LIMIT 1
)
GROUP BY actions.user_id
with that I would get all results from users and then for each get one result from actions, but that result from actions would not be the newest one, apparently it groups as it likes to, I tried MAX(actions.id), but I have got no luck with that.
Does anyone know the solution ? for now I have to take all from users and for each result take another query in my php code and I feel there is an elegant and faster way to do that.
With this query:
select
user_id,
max(date) date
from actions
group by user_id
you get the latest date for each user and then you must join to it the 2 tables:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(date) date
from actions
group by user_id
) g on g.user_id = a.user_id and g.date = a.date
If you want the latest results by id and not by date:
select
u.id, u.name, a.id, a.action, a.date, a.str
from users u
inner join actions a on a.user_id = u.id
inner join (
select
user_id,
max(id) id
from actions
group by user_id
) g on g.user_id = a.user_id and g.id = a.id
I have a competition which counts how many species each user has collected.
this is managed by 3 tables:
a parent table called "sub" with collection,each collection is unique, has an id and is associated to a user id.
+----+---------+
| id | user_id |
+----+---------+
| 1 | 1 |
| 2 | 10 |
| 3 | 1 |
| 4 | 3 |
| 5 | 1 |
| 6 | 10 |
+----+---------+
the child table called "sub_items" contains multiple unique records of the specs and is related to the parent table by the sub id to id.(each sub can have multiple records of specs)
+----+--------+---------+--+
| id | sub_id | spec_id | |
+----+--------+---------+--+
| 1 | 1 | 1000 | |
| 2 | 1 | 1003 | |
| 3 | 1 | 2520 | |
| 4 | 2 | 7600 | |
| 5 | 2 | 1000 | |
| 6 | 3 | 15 | |
+----+--------+---------+--+
a user table with associated user_id
+--------+-------+--+
| usename | name |
+---------+-------+--+
| 1 | David |
| 10 | Ruth |
| 3 | Rick |
+--------+-------+--+
i need to list the users with the most unique specs collected in a decsending order.
output expected:
David has a total of 2 unique specs.Ruth has a total of 2 unique specs.
+--------+---------+
| id | total |
+----+-------------+
| David | 2 |
| Ruth | 2 |
| Rick | 2 |
+----+-------------+
so far i have this,it produces a result. but its not accurate, it counts the total records.
im probably missing a DISTINCT somewhere in the sub-query.
SELECT s.id, s.user_id,u.name, sum(t.count) as total
FROM sub s
LEFT JOIN (
SELECT id, sub_id, count(id) as count FROM sub_items GROUP BY sub_id
) t ON t.sub_id = s.id
LEFT JOIN user u ON u.username = s.user_id
GROUP BY user_id
ORDER BY total DESC
i have looked at this solution, but it doesn't consider the unique aspect
You'll first have to get the max "score" for all the users like:
SELECT count(DISTINCT si.id) as total
FROM sub INNER JOIN sub_items si ON sub.id = su.sub_id
GROUP BY sub.user_id
ORDER BY total DESC
LIMIT 1
Then you can use that to restrict your query to users that share that max score:
SELECT u.name, count(DISTINCT si.id) as total
FROM
user u
INNER JOIN sub ON u.usename = sub.user_id
INNER JOIN sub_items si ON sub.id = su.sub_id
GROUP BY u.name
HAVING total =
(
SELECT count(DISTINCT si.id) as total
FROM sub INNER JOIN sub_items si ON sub.id = su.sub_id
GROUP BY sub.user_id
ORDER BY total DESC
LIMIT 1
)
this worked for me, i have to add the
COUNT(distinct spec_id)
to the sub-query
SELECT s.id, s.user_id,u.name, sum(t.count) as total
FROM sub s
LEFT JOIN (
SELECT sub_id, COUNT(distinct spec_id) as count FROM sub_items group by sub_id
) t ON t.sub_id = s.id
LEFT JOIN user u ON u.username = s.user_id
GROUP BY user_id
ORDER BY total DESC
I have three tables:
mysql> select * from a;
+----+---------+
| ID | Name |
+----+---------+
| 1 | John |
| 2 | Alice |
+----+---------+
mysql> select * from b;
+------+------------+----------+
| UID | date | received |
+------+------------+----------+
| 1 | 2017-10-02 | 5 |
| 1 | 2017-09-30 | 1 |
| 1 | 2017-09-29 | 4 |
+------+------------+----------+
mysql> select * from c;
+------+------------+------+
| UID | date | sent |
+------+------------+------+
| 1 | 2017-09-25 | 7 |
| 1 | 2017-09-30 | 2 |
| 1 | 2017-09-29 | 3 |
+------+------------+------+
If I try to calculate the total number of sent for John, it would be 12. And for received, it would be 10.
But if I try to join all three tables, the result is weird. Here is my query to join three tables:
mysql> select sum(sent), sum(received) from a
-> join c on c.UID = a.ID
-> join b on b.UID = a.ID
-> where a.ID = 1;
+-----------+---------------+
| sum(sent) | sum(received) |
+-----------+---------------+
| 36 | 30 |
+-----------+---------------+
But I need correct numbers (12 and 10, respectively). How can I have correct numbers?
You should join the aggregated result and not the raw tables
select a.uid, t1.received, t2.sent
from a
inner join (
select uid, sum(received) received
from b
group by uid
) t1 on t1.uid = a.id
inner join (
select uid, sum(sent) sent
from c
group by uid
) t2 on t2.uid = a.id
where a.id = 1
You could try below
select bx.id, recieved, sum(c.sent) sent from
(
SELECT a.id, sum(b.received) recieved
from a
INNER JOIN b
ON a.id=b.uid
group by a.id
) bx
INNER JOIN c
ON c.uid=bx.id
group by bx.id, bx.recieved;
>>>Demo<<<
This gets rid of the subquery, but introduces something else you might not want:
( SELECT uid, 'Received' AS direction, SUM(received) AS HowMany
WHERE uid = 1
GROUP BY uid )
UNION ALL
( SELECT uid, 'Sent' AS direction, SUM(sent) AS HowMany
WHERE uid = 1
GROUP BY uid )
I have a table with a composite primary key on EID (event ID) and start_time. I have another column called attending.
Users make their events more popular by reusing the event ID and changing the date, however, I create a new line in the database in this instance.
I would like to create a 4th column, actual_attending which is equal to the attending value minus the previous event's attending value. If their is no previous ID, the column can be null. How can I calculate this via update.
Here is a sqlfiddle as an example: http://sqlfiddle.com/#!2/43f2c5
update event e1
set e1.actual_attending = (select e1.attending - e2.attending
from event e2
where e2.eid(+) = e1.previous_eid
)
SELECT a.*
, a.attending-b.attending new_actual_attending
FROM
( SELECT x.*
, COUNT(*) rank
FROM event x
JOIN event y
ON y.eid = x.eid
AND y.start_time <= x.start_time
GROUP
BY eid, start_time
) a
LEFT
JOIN
( SELECT x.*
, COUNT(*) rank
FROM event x
JOIN event y
ON y.eid = x.eid
AND y.start_time <= x.start_time
GROUP
BY eid, start_time
) b
ON b.eid = a.eid
AND b.rank = a.rank - 1;
+-----+------------+-----------+------------------+------+----------------------+
| eid | start_time | attending | actual_attending | rank | new_actual_attending |
+-----+------------+-----------+------------------+------+----------------------+
| 1 | 2013-06-08 | 29 | NULL | 1 | NULL |
| 2 | 2013-06-09 | 72 | NULL | 1 | NULL |
| 2 | 2013-06-16 | 104 | NULL | 2 | 32 |
| 3 | 2013-06-07 | 224 | NULL | 1 | NULL |
| 3 | 2013-06-14 | 222 | NULL | 2 | -2 |
+-----+------------+-----------+------------------+------+----------------------+
http://sqlfiddle.com/#!2/43f2c5/2