2012-10-04 47 views
6

Tengo varias tablas de las que estoy tratando de obtener algunos datos, y estoy muy cerca, pero no puedo cerrar el trato.Necesito un gurú de SQL para una consulta compleja

tengo las siguientes tablas:

  • EVENTO
  • USUARIO
  • AMIGO
  • USER__FRIEND
  • EVENT__INVITATION

USUARIO y AMIGO están vinculados a través de la mesa USER__FRIEND (que contiene un USER_ID y un campo FRIEND_ID)

EVENT__INVITATION vincula un evento con una amiga (que tiene EVENT_ID y INVITEE_ID)

que estoy tratando de conseguir todos los eventos en los que:

  • soy el creador del evento ($ MyUserID = EVENT.CREATOR_ID)
  • o me invitan al evento ($ MyUserID = EVENT__INVITATION.INVITEE_ID)
  • o uno de mis amigos es el creador del evento ($ MyUserID = USER__FRIEND.USER_ID Y eN EVENT.CREATOR_ID (lista de mis amigos))
  • o uno de mis amigos es invitado al evento ($ MyUserID = USER__FRIEND.USER_ID Y EN EVENT__INVITATION.INVITEE_ID (lista de mis amigos))

Hay algunas otras condiciones WHERE alrededor de otros parámetros, pero creo Puedo resolverlos por mi cuenta.

En este momento, la única forma en que podía hacer que esto funcionara era con UNION, que creo que debía ser una salida de emergencia, y si tenía mejores habilidades, podía usarla.

Entonces, la pregunta es, ¿se puede hacer esto con una consulta única y económica que no utiliza UNIÓN?

Esto es lo que tengo hasta ahora, que lleva a cabo todo excepto los eventos que mis amigos están invitados a (23 es la aprobada en ID de usuario, en este caso):

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 

y esta es la consulta con el 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 
); 

Hay más en la consulta que hace que el uso de UNION no sea deseable. Tengo un cálculo trigonométrico complejo que estoy haciendo en la selección principal, y estoy ordenando los resultados por ese valor. Creo que puede estropear el conjunto de resultados.

¡Gracias por cualquier ayuda!

+0

¿Estás seguro de que no te faltan las que el amigo es el creador? – CrazyCasta

+0

Sé que se siente más viril hacerlo todo en una gran unión, pero sospecho que la UNIÓN es más eficiente. – Barmar

+0

@Loco: No, ese está ahí (mira la última O de la cláusula WHERE). – Raconteur

Respuesta

3

¿Qué hay de lo siguiente:

-- 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 
+0

@Barmar (¿o fue la respuesta de Chase?) - Perdón por la demora ... RL se puso en el camino. Necesito elaborar algunos datos para poner esto a prueba, pero creo que funcionará. Lo aceptaré como respuesta tan pronto como lo pruebe. ¡Aclamaciones! – Raconteur

+0

La respuesta fue de Chase, acabo de corregir un error tipográfico en el SQL. – Barmar

+0

Gracias Chase y Barmar. Esto no era exactamente lo que necesitaba, pero definitivamente me consiguió el 90% del camino hasta allí, y pude terminarlo. ¡Aprecia tu ayuda y tenacidad! – Raconteur

0

No soy muy bueno en ajuste de las consultas en la actualidad, por lo que me acaba de dar algo como esto una oportunidad y dejar que el optimizador de poner su esfuerzo en averiguar la mejor manera de conseguir los resultados:

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) 
; 

Si esto no prueba suficientemente eficiente, siempre se puede ir con algo como @ChaseMedallion's suggestion, ya que puede resultar de hecho uno mejor para su caso.

Cuestiones relacionadas