Need SQL guru for a complex query - mysql

I have several tables that I am trying to get some data out of, and I am very close, but cannot quite close the deal.
I have the following tables:
EVENT
USER
FRIEND
USER__FRIEND
EVENT__INVITATION
USER and FRIEND are linked via the USER__FRIEND table (which contains a USER_ID and a FRIEND_ID field)
EVENT__INVITATION links an EVENT with a FRIEND (it has EVENT_ID and INVITEE_ID)
I am trying to get all EVENTS where:
I am the EVENT creator ($myUserID = EVENT.CREATOR_ID)
or I am invited to the event ($myUserID = EVENT__INVITATION.INVITEE_ID)
or one of my FRIENDs is the creator of the EVENT ($myUserID = USER__FRIEND.USER_ID AND EVENT.CREATOR_ID IN (list of my FRIENDs))
or one of my FRIENDs is invited to the EVENT ($myUserID = USER__FRIEND.USER_ID AND EVENT__INVITATION.INVITEE_ID IN (list of my FRIENDs))
There are some other WHERE conditions around other parameters, but I think I can sort those out on my own.
Right now the only way I could get this to work was with a UNION, which I think must be a cop-out, and if I had better chops I could get around using it.
So, the question is, can this be done with a single, inexpensive query that does not use a UNION?
Here is what I have so far, which accomplishes everything except the EVENTs that my FRIENDs are invited to (23 is the passed in userID in this case):
SELECT e.*
FROM event e
LEFT JOIN event__invitation ei ON ei.event_id = e.id
LEFT JOIN user__friend uf ON uf.friend_id = ei.invitee_id
LEFT JOIN friend f ON f.id = uf.friend_id
WHERE (ei.invitee_id = 23 OR e.creator_id = 23 OR uf.user_id = 23 OR f.user_id = e.creator_id)
AND e.start_time >= 1348000000
and this is the query with the UNION:
SELECT e.* FROM event e
INNER JOIN event__invitation ei ON ei.event_id = e.id
INNER JOIN user__friend uf ON uf.friend_id = ei.invitee_id
WHERE (e.creator_id = 23 OR ei.invitee_id = 23 OR uf.user_id = 23)
UNION
SELECT e1.* FROM event e1
WHERE e1.creator_id IN (
SELECT f1.user_id FROM friend f1
INNER JOIN user__friend uf1 ON uf1.friend_id = f1.id
WHERE uf1.user_id = 23
AND f1.user_id IS NOT NULL
);
There is more to the query that makes the use of the UNION undesireable. I have a complex trig calculation that I am doing in the main select, and am ordering the results by that value. I think may mess up the result set.
Thanks for any help!!

How about the following:
-- take only distinct events
SELECT DISTINCT e.*
-- start with the event
FROM event e
-- expand to include all invitees and their user_friend info
LEFT JOIN event__invitation ei
ON ei.event_id = e.id
LEFT JOIN user__friend invitee
ON invitee.friend_id = ei.invitee_id
-- now we join again to user_friend to get the friends of the invitees/the creator
LEFT JOIN user__friend invitedFriend
ON invitedFriend.user_id = invitee.user_id
OR invitedFriend.user_id = e.creator_id
-- finally we match on whether any of these friends of friends are myself
LEFT JOIN friend myselfAsAFriend
ON myselfAsAFriend.id = invitedFriend.friendId
AND myselfAsAFriend.userID = 23
WHERE
(
-- (1) I am the creator of the event
e.creator_id = 23
-- (2) I am invited to the event
OR invitee.user_id = 23
-- (3 and 4) for this join to match a friend of mine must be invited or the creator
OR myselfAsAFriend.id IS NOT NULL
)
AND e.start_time >= 1348000000

I'm not very good at query tuning presently, so I would just give something like this a try and let the optimiser to put its effort in figuring out the best way of getting the results:
SELECT DISTINCT e.*
FROM event e
INNER JOIN event__invitation ei ON ei.event_id = e.id
INNER JOIN (
SELECT friend_id
FROM user__friend
WHERE user_id = $myUserID
UNION ALL
SELECT $myUserID
) u ON u.friend_id IN (e.creator_id, ei.invitee_id)
;
If this doesn't prove efficient enough, you could always go with something like #ChaseMedallion's suggestion, as it may indeed turn out a better one for your case.

Related

Multiple joins on same table

I'm trying to achieve a query which seems simple but I can't make it work correctly. Here's my database tables structures:
members
-> id
-> last_name
-> first_name
activities
-> id
registrations
-> id
-> member_id
tandems
-> id
-> activitie_id
-> registration_member_one
-> registration_member_two
Here's what i want to achieve:
Mutliple members can register to an activity. Then, i group the registrations by tandems. I want a view with all the tandems listed and there's my problem. When I try a query, it gives me multiple rows, duplicated many times.
Below, an example of the table I want to have:
tandems.id | activities.id | registration_member_one.members.last_name | registration_member_two.members.last_name
1 | 3 | John Doe | Jane Doe
Here's the query I'm working on:
SELECT
tandems.*,
memberOne.id, memberOne.last_name, memberOne.first_name,
memberTwo.id, memberTwo.last_name, memberTwo.first_name,
memberOne_registration.member_id as memberOne,
memberTwo_registration.member_id as memberTwo
FROM tandems
JOIN registrations as memberOne_registration
ON memberOne_registration.member_id = tandems.registration_member_one
JOIN members as memberOne ON memberOne.id = memberOne_registration.member_id
JOIN registrations as memberTwo_registration
ON memberTwo_registration.member_id = tandems.registration_member_two
JOIN members as memberTwo ON memberTwo.id = memberTwo_registration.member_id
WHERE activitie_id = 3;
Any help appreciated!
The error is caused by joining wrong column (member_id) of registrations table with tandems table, instead column registrations.id should be used.
SELECT
tandems.*,
memberOne.id, memberOne.last_name, memberOne.first_name,
memberTwo.id, memberTwo.last_name, memberTwo.first_name,
memberOne_registration.id as memberOne,
memberTwo_registration.id as memberTwo
FROM tandems
JOIN registrations as memberOne_registration ON memberOne_registration.id = tandems.registration_member_one
JOIN members as memberOne ON memberOne.id = memberOne_registration.member_id
JOIN registrations as memberTwo_registration ON memberTwo_registration.id = tandems.registration_member_two
JOIN members as memberTwo ON memberTwo.id = memberTwo_registration.member_id
WHERE activitie_id = 3;
Although other query is virtually the same, I hate working with unnecessarily long alias names so worked with "r1" and "r2" for the two instances of the registration table, and "m1" and "m2" for the members joining context.
SELECT
t.id,
t.activitie_id,
m1.last_name LastName1,
m1.first_name FirstName1,
m2.last_name LastName2,
m2.first_name FirstName2
FROM
tandems t
LEFT join registrations r1
ON t.registration_member_one = r1.id
LEFT JOIN members m1
ON r1.member_id = m1.id
LEFT join registrations r2
ON t.registration_member_two = m2.id
LEFT JOIN members m2
ON r2.member_id = m2.id
WHERE
t.activitie_id = 3;
To help you on this and in the future... Although mentally done, I try to mentally draw out how do I get the pieces together from the first table downstream. This can be seen too by the visual indentation almost like a tree view extension from T to R1 to M1, then R2 to M2 is a different branch. I also prefer to list the left table/alias.column = right table/alias.column in the join condition. How does T get to R1, then how does R1 get to M1.
In this, I used LEFT JOIN to each respective registration and member -- just-in-case only one person registered and a second may be pending. Not sure how your registration is actually structured.

Fetching two matches in a MySQL query?

So I have some code I'm trying to figure out... I have two tables:
TABLE: matches
event_id
match_id (primary)
match_score
match_p1
match_p2
match_win
TABLE: results
event_id
user_id
result_id (primary)
result_name
result_extra
The weird thing about the data is the content of of the matches table actually links to the results table in multiple fashions.
There will be an integer in match_p1 and match_p2 that link to the results_extra field on the results table. This is designed because each match has two players in it (p1 and p2), and each player has one result for each event.
If I wanted to get a list of all matches in an event, I would do the following:
SELECT *
FROM matches
WHERE event_id = 324
If I wanted to get a list of all matches belonging to a single player, I would do:
SELECT matches.*
FROM matches
LEFT JOIN results
ON ((results.result_extra = matches.match_p1) OR
(results.result_extra = matches.match_p2))
WHERE results.user_id = 1566
However, this is where things get a bit complicated... What if I wanted to get a list of matches where player 1566 fought player 2058? Its the logic for this query I can't figure out. Could you guys help me out?
Here is one way. Join results twice, and match the 2 player combinations.
select a.*
from matches a
join results b on a.match_p1 = b.result_extra
join results c on a.match_p2 = c.result_extra
where (b.user_id = 1566 and c.user_id = 2058) or (b.user_id = 2058 and c.user_id = 1566)
Could be this
SELECT matches.*
FROM matches
LEFT JOIN results a
ON ((a.result_extra = matches.match_p1 AND
a.result_extra = matches.match_p2))
LEFT JOIN results b
ON ((b.result_extra = matches.match_p1 AND
b.result_extra = matches.match_p2))
WHERE a.user_id = 2058
AND b.user_id = 1566
If 1566 and 2059 is user_ids,maybe this help you
SELECT matches.*
FROM matches
LEFT JOIN results
ON ((results.result_extra = matches.match_p1) OR
(results.result_extra = matches.match_p2))
WHERE results.user_id in (1566,2058);

Simple query issue with multiple tables and mismatching IDs

I'm having trouble with a simple MySQL Query.
Here is the query:
SELECT distinct e.E_CODE, s.S_CODE, p.P_ID, p.P_NAME, p.P_FIRSTNAME, p.P_STATUS, e.E_BOSS, tp.TP_TITLE
from event_participation ep, worker p, type_participation tp, event e, section s
where ep.P_ID = p.P_ID
and s.S_ID = e.S_ID
and ep.TP_ID = tp.TP_ID
and e.E_CODE = ep.E_CODE
The problem is that ep.TP_ID sometimes has a value set to zero while tp.TP_ID has nothing with a zero ID. It's auto-increment and starts at 1 and so on.
The result is obviously that this query does not return records when the ep.TP_ID = 0 and there is no match in tp.TP_ID.
So I'm trying to figure out a way to get those results in there anyway. I was thinking of using a LEFT JOIN statement but couldn't figure out a proper way to insert it into the query.
Any advice on this matter would be greatly appreciated.
First of all, I advice you to use some general type for event_participation records without type; But, unless to take that decision, supposing you want to get all matching records between all tables but also get results with no type, you can use the following query:
SELECT DISTINCT e.E_CODE, s.S_CODE, p.P_ID, p.P_NAME, p.P_FIRSTNAME, p.P_STATUS, e.E_BOSS, tp.TP_TITLE
FROM event_participation ep
JOIN worker p ON (ep.P_ID = p.P_ID)
JOIN event e ON (e.E_CODE = ep.E_CODE)
JOIN section s ON (s.S_ID = e.S_ID)
LEFT JOIN type_participation tp ON (ep.TP_ID = tp.TP_ID)
SELECT DISTINCT e.E_CODE
, s.S_CODE
, p.P_ID
, p.P_NAME
, p.P_FIRSTNAME
, p.P_STATUS
, e.E_BOSS
, tp.TP_TITLE
FROM event_participation ep
JOIN worker p
ON p.P_ID = ep.P_ID
JOIN event e
ON e.E_CODE = ep.E_CODE
JOIN section s
ON s.S_ID = e.S_ID
LEFT
JOIN type_participation tp
ON tp.TP_ID = ep.TP_ID;

MySQL do INNER JOIN if specific value is 1

I have this query:
SELECT
e.*, u.name AS event_creator_name
FROM `edu_events` e
LEFT JOIN `edu_users` u
ON u.user_id = e.event_creator
INNER JOIN `edu_event_participants`
ON participant_event = e.event_id && participant_user = 1
WHERE
MONTH(e.event_date_start) = 6
AND YEAR(e.event_date_start) = 2013
It works perfect, however, I only want to do the INNER JOIN if the value: e.event_type equals 1. If not, it should ignore the INNER JOIN.
I have tried for some time to figure it out, but the solutions seems difficult to implment for my proposes (as it is only for select/specific values).
I'm thinking about something like:
SELECT
e.*, u.name AS event_creator_name
FROM `edu_events` e
LEFT JOIN `edu_users` u ON u.user_id = e.event_creator
if(e.event_type == 1) {
INNER JOIN `edu_event_participants` ON participant_event = e.event_id && participant_user = 1
}
WHERE MONTH(e.event_date_start) = 6
AND YEAR(e.event_date_start) = 2013
I have edited the below following further feedback from #Matthias
-- This will get all events for a given user plus all globals
SELECT
e.*,
u.name AS event_creator_name
FROM `edu_users` u
-- in the events
INNER JOIN `edu_events` e 
ON (
-- Get all the ones that the user is participant
e.event_creator = u.user_id
-- Or where event_type is 1
OR
e.event_type = 1
)
AND e.event_date_start BETWEEN DATE('2013-06-01') AND DATE('2013-07-01')
    
-- Add in event participants even though it doesn't seem to be used?
INNER JOIN `edu_event_participants` AS eep
ON eep.participant_event = e.event_id
AND eep.participant_user = 1
    
-- Add the user ID into the WHERE
WHERE u.user_id = 1;
This just might not make too much sence as it feels as though edu_event_participants has too much information in. event_creator should really be stored against the event itself, and then event_participants just containing an event id, user id, and user type.
If you are looking to get all users on an event, it may be better to do a seperate query for that event to select all users based off an event_id
The note on your use of MONTH() and YEAR(). This will trigger a table scan, as MySQL will need to apply the MONTH() and YEAR() functions to all rows to determine which match that WHERE statement. If you instead calculate the upper and lower limits (i.e. 2013-06-01 00:00:00 <= e.event_date_start < 2013-07-01 00:00:00) then MySQL can use a far more efficient range scan on an index (assuming one exists on e.event_date_start)
If I understand correctly you only want the results where there is an entry on edu_event_participants with the same event_id and participant_user = 1 but only if event_type = 1, but you don't really want to get any information from the edu_event_participants table. If that is the case:
SELECT
e.*, u.name AS event_creator_name
FROM `edu_events` e
LEFT JOIN `edu_users` u
ON u.user_id = e.event_creator
WHERE
-- as Simon at mso.net suggested
WHERE e.event_date_start BETWEEN DATE('2013-06-01') AND DATE('2013-07-01')
-- MONTH(e.event_date_start) = 6
-- AND YEAR(e.event_date_start) = 2013
AND (
-- either event is public
e.event_type = 1 or
-- or the user is in the participants table
exists
(select 1 from `edu_event_participants`
where participant_event = e.event_id
AND participant_user = 1)
)
Maybe what you're after is displaying the left table value even if there's no matching data from right table? On that case you can use outer join like so:
LEFT OUTER JOIN `edu_event_participants` ON participant_event = e.event_id && participant_user = 1 AND e.event_type = 1

left join with specific conditions help please

am having a problem constructing a query
here is simplified tables structure
3 tables
Event [Event_id , Event_name]
Event_files [Event_id(FK) , File_id(FK)]
Uploaded_Files[File_id , File_type, File_path]
we mainly have 2 file types
image = 2
document = 4
what am trying to do is to get the events along with their images (if they have an image )
am trying to do this with this Query
select e.id, e.name,uf.id as file_id,uf.path
from event e
left join event_file ef on ef.event_id = e.id
left join uploaded_file uf ON ef.file_id = uf.id
i know that i need to apply a condition but each time i do in the where or ON there is always problem with the Query
for example if i apply :
left join uploaded_file uf ON ef.file_id = uf.id AND (uf.type = 2 )
it will still return 2 records for the events that has both image and file one of them with file_path null .
on the other hand if i do the following :
where (uf.id is null OR (uf.id is not null AND uf.type=2))
the events with only files and no image will not be returned any more
is there is solution please ?
thanks in advance
SELECT e.id, e.name, f.file_id AS file_id, f.path
FROM event e
LEFT JOIN
(
SELECT ef.event_id, uf.id AS file_id, uf.path
FROM event_file ef
INNER JOIN uploaded_file uf ON ef.file_id = uf.id AND uf.type = 2
) f ON f.event_id = e.id
This should do (untested.)
The reason you're getting the empty record is because you only specify the uf.type condition on the uploaded_file table, which imposes nothing on the left join for event_file.