2012-04-25 7 views
8

Para una aplicación de citas, tengo algunas tablas que necesito consultar para una sola salida con un LÍMITE 10 de ambas consultas combinadas. Parece difícil de hacer en este momento, aunque no es un problema consultarlos por separado, pero el LIMIT 10 no funcionará ya que los números no son exactos (por ejemplo, no LIMIT 5 y LIMIT 5, una consulta puede devolver 0 filas , mientras que los otros 10, dependiendo del escenario).MySQL - Combinación de dos sentencias select en un resultado con LIMIT eficientemente

members table 
member_id | member_name 
------------------------ 
    1   Herb 
    2   Karen 
    3   Megan 

dating_requests 
request_id | member1 | member2 | request_time 
---------------------------------------------------- 
    1   1   2  2012-12-21 12:51:45 

dating_alerts 
alert_id | alerter_id | alertee_id | type | alert_time 
------------------------------------------------------- 
    5   3   2  platonic 2012-12-21 10:25:32 

dating_alerts_status 
status_id | alert_id | alertee_id | viewed | viewed_time 
----------------------------------------------------------- 
    4   5   2   0  0000-00-00 00:00:00 

Imagine que usted es Karen y apenas iniciada la sesión, debe ver estos 2 artículos:

1. Herb requested a date with you. 
2. Megan wants a platonic relationship with you. 

En una consulta con un límite de 10. En cambio aquí hay dos preguntas que necesitan ser combinada:

1. Herb requested a date with you. 
    -> query = "SELECT dr.request_id, dr.member1, dr.member2, m.member_name 
       FROM dating_requests dr 
       JOIN members m ON dr.member1=m.member_id 
       WHERE dr.member2=:loggedin_id 
       ORDER BY dr.request_time LIMIT 5"; 
2. Megan wants a platonic relationship with you. 
    -> query = "SELECT da.alert_id, da.alerter_id, da.alertee_id, da.type, 
         da.alert_time, m.member_name 
       FROM dating_alerts da 
       JOIN dating_alerts_status das ON da.alert_id=das.alert_id 
        AND da.alertee_id=das.alertee_id 
       JOIN members m ON da.alerter_id=m.member_id 
       WHERE da.alertee_id=:loggedin_id AND da.type='platonic' 
        AND das.viewed='0' AND das.viewed_time<da.alert_time 
       ORDER BY da.alert_time LIMIT 5"; 

una vez más, a veces ambas tablas puede estar vacío, o 1 tabla puede estar vacío, o ambos completo (donde límite de 10 patadas en) y ordenada por tiempo. ¿Alguna idea sobre cómo obtener una consulta para realizar esta tarea de manera eficiente? Pensamientos, consejos, carillones, optimizaciones son bienvenidos.

+1

Si las columnas devueltas por las dos consultas son las mismas, únelas con ['UNION'] (http://dev.mysql.com/doc/refman/5.6/en/union.html) y haga la totalidad Lo que una subconsulta a una consulta externa que realiza el 'LIMIT'. De lo contrario, podría determinar el 'LÍMITE' requerido para aplicar a la segunda consulta desde (10 menos el número de registros devueltos por la primera consulta); probablemente sea más fácil hacerlo en el idioma que esté utilizando para invocar consultas. – eggyal

+0

Crea una tabla con los resultados esperados. Verás el problema allí. –

+0

No es posible combinar 2 consultas con diferentes listas de selección. – vyegorov

Respuesta

17

Puede combinar múltiples consultas con UNION, pero solo si las consultas tienen el mismo número de columnas. Idealmente, las columnas son las mismas, no solo en el tipo de datos, sino también en su significado semántico; sin embargo, a MySQL no le importa la semántica y manejará diferentes tipos de datos al usar algo más genérico, por lo tanto, si es necesario, podría sobrecargar las columnas para que tengan diferentes significados en cada tabla, luego determine qué significado es apropiado en su nivel superior código de nivel (aunque no recomiendo hacerlo de esta manera).

Cuando el número de columnas difiere, o cuando desea lograr una alineación de datos mejor/menos sobrecargada a partir de dos consultas, puede insertar columnas literales ficticias en sus declaraciones SELECT. Por ejemplo:

SELECT t.cola, t.colb, NULL, t.colc, NULL FROM t; 

Incluso podría haber algunas columnas reservado para la primera tabla y otros por la segunda tabla, de modo que son NULL en otro lugar (pero recuerde que los nombres de las columnas proceden de la primera consulta, por lo que puede desee asegurarse de que todos ellos son el nombre existe):

SELECT a, b, c, d, NULL AS e, NULL AS f, NULL AS g FROM t1 
UNION ALL -- specify ALL because default is DISTINCT, which is wasted here 
    SELECT NULL, NULL, NULL, NULL, a, b, c FROM t2; 

Usted podría tratar de alinear sus dos consultas de esta manera, a continuación, la combinación de ellos con un operador UNION; mediante la aplicación de LIMIT a la UNION, que está a punto de alcanzar su objetivo:

(SELECT ...) 
UNION 
    (SELECT ...) 
LIMIT 10; 

La única cuestión que queda es que, según se ha presentado, 10 o más registros de la primera tabla se "empujar" los registros del segundo Sin embargo, podemos utilizar un ORDER BY en la consulta externa para resolver esto.

Poniendo todo junto:

(
    SELECT 
    dr.request_time AS event_time, m.member_name,  -- shared columns 
    dr.request_id, dr.member1, dr.member2,    -- request-only columns 
    NULL AS alert_id, NULL AS alerter_id,    -- alert-only columns 
     NULL AS alertee_id, NULL AS type 
    FROM dating_requests dr JOIN members m ON dr.member1=m.member_id 
    WHERE dr.member2=:loggedin_id 
    ORDER BY event_time LIMIT 10 -- save ourselves performing excessive UNION 
) UNION ALL (
    SELECT 
    da.alert_time AS event_time, m.member_name,  -- shared columns 
    NULL, NULL, NULL,         -- request-only columns 
    da.alert_id, da.alerter_id, da.alertee_id, da.type -- alert-only columns 
    FROM 
    dating_alerts da 
    JOIN dating_alerts_status das USING (alert_id, alertee_id) 
    JOIN members m ON da.alerter_id=m.member_id 
    WHERE 
    da.alertee_id=:loggedin_id 
    AND da.type='platonic' 
    AND das.viewed='0' 
    AND das.viewed_time<da.alert_time 
    ORDER BY event_time LIMIT 10 -- save ourselves performing excessive UNION 
) 
ORDER BY event_time 
LIMIT 10; 

Por supuesto, ahora le toca a usted para determinar qué tipo de fila que se está tratando con a medida que lee cada registro en el conjunto de resultados (sugieren probar request_id y/o alert_id para los valores NULL; alternativamente, se podría agregar una columna adicional a los resultados que indiquen explícitamente de qué tabla se originó cada registro, pero debe ser equivalente siempre que esas columnas id sean NOT NULL).

+0

Gracias por el ejemplo y la explicación eggyal. La lógica detrás de la recuperación de filas está en la cláusula ORDER BY, el tiempo de solicitud y el tiempo de alerta en orden tal como se insertaron en las diferentes tablas. Entonces, técnicamente, es muy probable que tenga 3 registros de la primera tabla, luego 2 registros de la segunda tabla, luego 1 registro de ida y vuelta entre las tablas hasta que se alcance el LÍMITE 10. – Wonka

+0

@Wonka: Parece que deberías ser capaz de lograr esto con un 'ORDER BY' en la consulta externa; avísame si no puedes resolverlo. – eggyal

+0

¿Quiere decir ORDER BY [time_here] LIMIT 10? ¿Qué pasa con las consultas en el interior, simplemente suelte el ORDEN BY dr.request_time LIMIT 5 y ORDER BY da.alert_time LIMIT 5? ¿Puede mostrarme cómo sería la consulta final con mis consultas, así que estoy seguro? – Wonka

Cuestiones relacionadas