Here is your original UNION query
SELECT count(*)
FROM user AS u
JOIN user_event_role AS uer ON (uer.user_id=u.id)
WHERE
uer.event_id=$event_id AND
uer.term_to >= CURRENT_DATE() AND
uer.role != 'volunteer' AND
u.netid='{$netid}'
UNION
SELECT count(*)
FROM user AS u
JOIN user_project_role AS upr ON (upr.user_id=u.id)
JOIN event AS e ON (e.project_id=upr.project_id)
WHERE
e.id=$event_id AND
upr.term_to >= CURRENT_DATE() AND
u.netid='{$netid}'
UNION
SELECT count(*)
FROM user AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN event_sponsor AS es ON (es.organization_id=uor.organization_id)
WHERE
e.id=$event_id AND
uor.term_to >= CURRENT_DATE() AND
u.netid='{$netid}'
UNION
SELECT count(*)
FROM user AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN project_sponsor AS ps ON (ps.organization_id=uor.organization_id)
WHERE
uer.event_id=$event_id AND
uor.term_to >= CURRENT_DATE() AND
u.netid='{$netid}'
You need to make three changes.
- Count the distinct userids
- Refactor each subquery on the inline subquery for users
- Index the tables to better support the term_to columns
Here is the Refactored Query:
SELECT DISTINCT id
FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid='{$netid}') AS u
JOIN user_event_role AS uer ON (uer.user_id=u.id)
WHERE
uer.event_id=$event_id AND
uer.term_to >= CURRENT_DATE() AND
uer.role != 'volunteer'
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid='{$netid}') AS u
JOIN user_project_role AS upr ON (upr.user_id=u.id)
JOIN event AS e ON (e.project_id=upr.project_id)
WHERE
e.id=$event_id AND
upr.term_to >= CURRENT_DATE()
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid='{$netid}') AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN event_sponsor AS es ON (es.organization_id=uor.organization_id)
WHERE
e.id=$event_id AND
uor.term_to >= CURRENT_DATE()
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid='{$netid}') AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN project_sponsor AS ps ON (ps.organization_id=uor.organization_id)
WHERE
uer.event_id=$event_id AND
uor.term_to >= CURRENT_DATE()
) userids ORDER BY id;
Here are the Indexes You Need to Speed Up the Subqueries:
ALTER TABLE user_event_role ADD INDEX (event_id,term_to,role);
ALTER TABLE user_project_role ADD INDEX (term_to);
ALTER TABLE user_organization_role ADD INDEX (term_to);
Interestingly, @DTest gave a good idea in his answer. Make a Stored Function. However you have to pass in three parameters: NetID,UserID,EventID. Here is that Stored Function DoesUserHaveEditPrivs using the new refactored query:
DELIMITER $$
DROP FUNCTION IF EXISTS DoesUserHaveEditPrivs $$
CREATE FUNCTION DoesUserHaveEditPrivs
(GivenNetID INT,GivenUserID INT,GivenEventID INT) RETURNS INT
BEGIN
DECLARE rv INT;
DECLARE UserID_Tag VARCHAR(32);
DECLARE UserID_CSV VARCHAR(1024);
SET rv = 1;
SELECT idlist INTO UserID_CSV FROM (
SELECT CONCAT('.',GROUP_CONCAT(DISTINCT id SEPARATOR '.'),'.') idlist
FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_event_role AS uer ON (uer.user_id=u.id)
WHERE
uer.event_id=GivenEventID AND
uer.term_to >= CURRENT_DATE() AND
uer.role != 'volunteer'
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_project_role AS upr ON (upr.user_id=u.id)
JOIN event AS e ON (e.project_id=upr.project_id)
WHERE
e.id=GivenEventID AND
upr.term_to >= CURRENT_DATE()
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN event_sponsor AS es ON (es.organization_id=uor.organization_id)
WHERE
e.id=GivenEventID AND
uor.term_to >= CURRENT_DATE()
UNION
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN project_sponsor AS ps ON (ps.organization_id=uor.organization_id)
WHERE
uer.event_id=GivenEventID AND
uor.term_to >= CURRENT_DATE()
) userids_unioned ORDER BY id
) userids;
SET UserID_Tag = CONCAT('.',GivenUserID,'.');
IF LOCATE(UserID_Tag,UserID_CSV) THEN
SET rv = 0;
END IF;
RETURN rv;
END $$
DELIMITER ;
Here is what the Stored Function Does: It first creates a period separated list of Unique UserIDs from the Refactored Query called UserID_CSV using the GROUP_CONCAT function. An extra period is prepended and appended to UserID_CSV. It then creates another variable called UserID_Tag which has a string contains the GivenUserID surrounded by periods. I then use the LOCATE function to get the index position of the UserID_Tag as located in UserID_CSV. If LOCATE returns 0, function returns 0. Otherwise, it returns 1.
Give it a Try !!! (@DTest, +1 for suggesting the Stored Function)
UPDATE 2011-05-28 18:00
I find your error unusual since I create the storted procedure in MySQL Query Browser with no compiler errors. I changed the code to put the UNION queries in another subquery. Try it again, please !!!
UPDATE 2011-05-28 17:52
I have an alternate solution via the stored procedure. I eliminated the use of UNION and piled up unique ids in a temp table:
DELIMITER $$
DROP FUNCTION IF EXISTS DoesUserHaveEditPrivs $$
CREATE FUNCTION DoesUserHaveEditPrivs
(GivenNetID INT,GivenUserID INT,GivenEventID INT) RETURNS INT
BEGIN
DECLARE rv INT;
DECLARE UserID_Tag VARCHAR(32);
DECLARE UserID_CSV VARCHAR(1024);
DROP TABLE IF EXIST idlist;
CREATE TEMPORARY sfTABLE idlist (id INT,PRIMARY KEY (ID)) ENGINE=MEMORY;
SET rv = 1;
INSERT IGNORE INTO idlist
SELECT id FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_event_role AS uer ON (uer.user_id=u.id)
WHERE
uer.event_id=GivenEventID AND
uer.term_to >= CURRENT_DATE() AND
uer.role != 'volunteer'
);
INSERT IGNORE INTO idlist
SELECT id FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_project_role AS upr ON (upr.user_id=u.id)
JOIN event AS e ON (e.project_id=upr.project_id)
WHERE
e.id=GivenEventID AND
upr.term_to >= CURRENT_DATE()
);
INSERT IGNORE INTO idlist
SELECT id FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN event_sponsor AS es ON (es.organization_id=uor.organization_id)
WHERE
e.id=GivenEventID AND
uor.term_to >= CURRENT_DATE()
);
INSERT IGNORE INTO idlist
SELECT id FROM (
SELECT u.id
FROM (SELECT id FROM user WHERE netid=GivenNetID) AS u
JOIN user_organization_role AS uor ON (uor.user_id=u.id)
JOIN project_sponsor AS ps ON (ps.organization_id=uor.organization_id)
WHERE
uer.event_id=GivenEventID AND
uor.term_to >= CURRENT_DATE()
;
SELECT CONCAT('.',GROUP_CONCAT(id SEPARATOR '.'),'.') FROM idlist;
SET UserID_Tag = CONCAT('.',GivenUserID,'.');
IF LOCATE(UserID_Tag,UserID_CSV) THEN
SET rv = 0;
END IF;
RETURN rv;
END $$
DELIMITER ;