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
Related
I have query like this:
SELECT cs_event.*, cs_file.name, cs_file.extension, cs_user.first_name, cs_user.last_name
FROM cs_event
LEFT JOIN cs_file ON cs_event.idfile = cs_file.idfile
LEFT JOIN cs_user ON cs_event.iduser = cs_user.iduser
WHERE type != 51
AND idportal = 1
UNION DISTINCT
SELECT cs_event.*, cs_file.name, cs_file.extension, cs_user.first_name, cs_user.last_name
FROM cs_event
LEFT JOIN cs_file ON cs_event.idfile = cs_file.idfile
LEFT JOIN cs_user ON cs_event.iduser = cs_user.iduser
WHERE shared_with_users LIKE '%i:2;%'
AND idportal = 1
ORDER BY add_date DESC
LIMIT 6
The problem is following:
Regular user can't see certain types of events (for now it is type 51) and he can see only things which are shared with him.
shared_with_users column can be null or have value - this column have value only for one type of event (type = 50) and for other events it is null.
I need to perform following:
User can access all events except event with type 51 and if the the event is type of 50, I need to check if the event is shared with him (shared_with_users column), and collect that also. Is it possible to make this kind of query?
Try this
SELECT cs_event.*, cs_file.name, cs_file.extension, cs_user.first_name, cs_user.last_name
FROM cs_event
LEFT JOIN cs_file ON cs_event.idfile = cs_file.idfile
LEFT JOIN cs_user ON cs_event.iduser = cs_user.iduser
WHERE type != 51 o or (type = 50 and shared_with_users LIKE '%i:2;%')
AND idportal = 1
ORDER BY add_date DESC
LIMIT 6
I think you can do this as a single query, with logic in the WHERE clause:
SELECT e.*, f.name, f.extension, u.first_name, u.last_name
FROM cs_event e LEFT JOIN
cs_file f
ON e.idfile = f.idfile LEFT JOIN
cs_user u
ON e.iduser = u.iduser
WHERE idportal = 1 AND
(type <> 51 OR shared_with_users LIKE '%i:2;%');
Some notes:
I don't think the LEFT JOINs are necessary. The WHERE clause may be turning them into inner joins anyway, but it is hard to tell without qualified column names.
I added table aliases so the query is easier to write and to read.
The logic for shared_with_users suggests that you have stored a list of values in a string. That is a bad choice.
I have a selection and I wanted to search a column of a table by left join only if the User is enabled with the pre condition to essar search as a condition that adds tables I was a little confused, I do not know if I separate searches or I do everything at once ....
He says it's for me to look at the mysql system, but I see so
if .condition... then ... end if;
My code
select user. * from
u user
if (u.visible == 1,
left join houseUser hu on u.id = h.id_user
left join h house on hu.id_house = h.id_house
)
where
u.age> 30
if visible add that code to can see in what house the user is.
You can just incorporate the condition into the on clauses:
select u.*, hu.*, h.*
from user u left join
houseUser hu
on u.id = h.id_user and u.visible = 1 left join
h house
on hu.id_house = h.id_house and u.visible = 1
where u.age > 30;
The columns will come back, but they will be NULL when visible is not 1.
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;
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.
I have 3 tables
person (id, name)
area (id, number)
history (id, person_id, area_id, type, datetime)
In this tables I store the info which person had which area at a specific time. It is like a salesman travels in an area for a while and then he gets another area. He can also have multiple areas at a time.
history type = 'I' for CheckIn or 'O' for Checkout.
Example:
id person_id area_id type datetime
1 2 5 'O' '2011-12-01'
2 2 5 'I' '2011-12-31'
A person started traveling in area 5 at 2011-12-01 and gave it back on 2011-12-31.
Now I want to have a list of all the areas all persons have right now.
person1.name, area1.number, area2.number, area6.name
person2.name, area5.number, area9.number
....
The output could be like this too (it doesn't matter):
person1.name, area1.number
person1.name, area2.number
person1.name, area6.number
person2.name, area5.number
....
How can I do that?
This question is, indeed, quite tricky. You need a list of the entries in history where, for a given user and area, there is an 'O' record with no subsequent 'I' record. Working with just the history table, that translates to:
SELECT ho.person_id, ho.area_id, ho.type, MAX(ho.datetime)
FROM History AS ho
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
)
GROUP BY ho.person_id, ho.area_id, ho.type;
Then, since you're really only after the person's name and the area's number (though why the area number can't be the same as its ID I am not sure), you need to adapt slightly, joining with the extra two tables:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
WHERE ho.type = 'O'
AND NOT EXISTS(SELECT *
FROM History AS hi
WHERE hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
);
The NOT EXISTS clause is a correlated sub-query; that tends to be inefficient. You might be able to recast it as a LEFT OUTER JOIN with appropriate join and filter conditions:
SELECT p.name, a.number
FROM History AS ho
JOIN Person AS p ON ho.person_id = p.id
JOIN Area AS a ON ho.area_id = a.id
LEFT OUTER JOIN History AS hi
ON hi.person_id = ho.person_id
AND hi.area_id = ho.area_id
AND hi.type = 'I'
AND hi.datetime > ho.datetime
WHERE ho.type = 'O'
AND hi.person_id IS NULL;
All SQL unverified.
You're looking for results where each row may have a different number of columns? I think you may want to look into GROUP_CONCAT()
SELECT p.`id`, GROUP_CONCAT(a.`number`, ',') AS `areas` FROM `person` a LEFT JOIN `history` h ON h.`person_id` = p.`id` LEFT JOIN `area` a ON a.`id` = h.`area_id`
I haven't tested this query, but I have used group concat in similar ways before. Naturally, you will want to tailor this to fit your needs. Of course, group concat will return a string so it will require post processing to use the data.
EDIT I thikn your question has been edited since I began responding. My query does not really fit your request anymore...
Try this:
select *
from person p
inner join history h on h.person_id = p.id
left outer join history h2 on h2.person_id = p.id and h2.area_id = h.area_id and h2.type = 'O'
inner join areas on a.id = h.area_id
where h2.person_id is null and h.type = 'I'