2011-01-19 12 views
5

Tengo una base de datos de habitaciones y equipos. Deseo consultar la base de datos y devolver una lista de habitaciones con, p. tv, radio, satélite y nevera (eq1, eq2, eq3, ...., eqN).Consulta con múltiples EXIST

tengo la siguiente instrucción SELECT:

select * from rooms r where 
exists (select id from equipments where eq_id='eq1' and room_id=r.id) 
and 
exists (select id from equipments where eq_id='eq2' and room_id=r.id) 
and 
exists (select id from equipments where eq_id='eq3' and room_id=r.id) 
....... 
and 
exists (select id from equipments where eq_id='eqN' and room_id=r.id) 

¿Hay alguna manera de optimizar o hacer este corto?

Respuesta

0
select * from rooms r where 
(select count(id) from equipments where eq_id='eq1' and room_id=r.id) > 0 
and 
... 
1

probar esto (no tengo ninguna base de datos disponible para probarlo, también cuenta el rendimiento)

select * from 
    rooms r, 
    (
     select count(distinct id) as cnt, id from equipments where eq_id in ('eq1','eq2') group by id 
    ) as sub 
where sub.id = r.id 
and sub.cnt >= 2 'Options count 

Nota: 2 - es el número de opciones que necesite. En el ejemplo son: 'EQ1', 'EQ2'

+0

Devolverá también las filas para las cuales count (id)> = 2 simplemente debido a filas con eq1 (y puede que no tenga filas con eq_id = 'eq2') ..es decir, no funciona – Unreason

+0

Sí, claro, pero si la combinación "id + eq_id" es única, funcionará – vmg

+0

@Vitaly, pero lo más probable es que no, lo más probable es que eq_id es id del tipo de equipo y yo diría que es es más probable que sea posible tener varias instancias del mismo tipo de equipo en una habitación. Podría estar equivocado, pero diría que mi suposición es más segura ... :) – Unreason

3

Para acortar usted podría

select * 
from rooms r 
where @N = (select count(distinct eq_id) 
      from equipments 
      where eq_id IN ('eq1','eq2',...,'eqN') and room_id=r.id) 

EDITAR pero no está seguro si en realidad hará que sea más rápido ... todo lo contrario, la versión con EXISTS AND EXISTS tiene la posibilidad de podar la rama de ejecución en el primer falso, lo anterior realmente debe contar los valores distintos (pasar por todos los registros) y ver cuál es ese valor.

Así que usted debe pensar en lo que es más rápido:

  • ir una vez a través de todos los registros relacionados a una habitación (una consulta correlacionada) o
  • correr N (peor caso) se correlacionó (pero subconsultas altamente selectivos) para cada habitación

Depende de las estadísticas de sus datos (yo diría que si la mayoría de las habitaciones no tienen todos los equipos buscados, entonces su versión inicial debería ser más rápida, si la mayoría de las habitaciones tienen todos los equipos) entonces la versión propuesta podría Desempeñar mejor; También si el EXISTS versión es más rápida hacer un esfuerzo para primero las consultas que son más propensos a fallar es decir, primero comprobar si el equipo más raro)

También puede probar una versión con GROUP BY

select r.* 
from rooms r join 
    equipments e on r.id = e.room_id 
group by r.id 
where eg_id in ('eq1','eq2',...,'eqN') 
having count(distinct e.eq_id) = @N 

(por encima de SQL no probado)

+0

+1 Probé tu primera solución, parece estar bien. – jachim

0

Use un procedimiento almacenado.

aquí es el procedimiento para MySQL:

DELIMITER $$ 

CREATE DEFINER=`root`@`%` PROCEDURE `GetRooms`(IN roomtable TEXT, IN equipmenttable TEXT, IN equipments TEXT) 
BEGIN 
    DECLARE statement text; 
    DECLARE Pos int; 
    DECLARE cond text; 
    DECLARE element text; 
    DECLARE tmpTxt text; 

    set tmpTxt = equipments; 
    set cond = ""; 
    set Pos = instr(tmpTxt,';'); 
    while Pos <> 0 do 
    set element = substring(tmpTxt, 1, Pos-1); 

    if cond <> "" then 
     set cond = concat(cond,' and '); 
    end if; 

    set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' , element ,''' and room_id=r.id) '); 


    set tmpTxt = replace(tmpTxt, concat(element,';'), ''); 


    set Pos = instr(tmpTxt,';'); 
    end while; 

    if tmpTxt <> "" then 
     if cond <> "" then 
      set cond = concat(cond,' and '); 
     end if; 

     set cond = concat(cond,'exists (select id from ' , equipmenttable ,' where eq_id=''' , tmpTxt ,''' and room_id=r.id) '); 
    end if; 

    SET @statement = concat('Select * FROM ' , roomtable , " WHERE " , cond , ";"); 

    PREPARE stmt FROM @statement; 
    EXECUTE stmt; 
END 

ejecutarlo con: CALL GetRooms('RoomTableName','EquipmentTableName','EquipmentIDs')

Ejemplo:

Call GetRooms('rooms','equipemnts','eq1;eq2;eq3'); 

Espero que esto ayude.

Cuestiones relacionadas