MySQL, devolver tuplas inversas a la query

Una de las cosas principales por las que se escribe un blog es para dar información y conocimiento, pero también es importante la parte en la que se recibe, bien a través de comentarios o como es este el caso, con un caso concreto sobre MySQL a ver si a alguien le ha sucedido y tiene la respuesta. Lo intenté en google, pero realizar la pregunta ya de por sí es complicado, buscar la respuesta más.
Vamos a simplificar el caso a una tabla como la que sigue, con usuarios.
mysql> SELECT * FROM users;
+-----------+--------+--------------------+
| host      | user   | password           |
+-----------+--------+--------------------+
| localhost | javier | 0.1332591382165059 |
| localhost | javi   | passwd             |
+-----------+--------+--------------------+
2 rows in set (0.00 sec)
Lo que quiero obtener son exactamente las tuplas que no están en la query que realizo. Por ejemplo, realizo la siguiente query,
mysql> SELECT * FROM users WHERE user='javier';
+-----------+--------+--------------------+
| host      | user   | password           |
+-----------+--------+--------------------+
| localhost | javier | 0.1332591382165059 |
+-----------+--------+--------------------+
1 row in set (0.00 sec)
Donde obtengo una tupla que cumple la condición where. Si ahora mi interesa obtener las tupas que no cumplen dicha condición, lo puedo hacer tal que así,
mysql> SELECT * FROM users WHERE user NOT IN (
 -> SELECT user FROM users WHERE user='javier'
 -> );
+-----------+------+----------+
| host      | user | password |
+-----------+------+----------+
| localhost | javi | passwd   |
+-----------+------+----------+
1 row in set (0.00 sec)
Esto es correcto, pero la pregunta es, existe alguna forma de obtener este resultado sin tener que emplear el 'WHERE NOT IN'. En la query real que me atañe, realizar la consulta que devuelve valores tarda 3 segundos, pero realizar la otra, con el 'NOT IN', debido a la gran cantidad de valores, se alarga mucho en el tiempo.
¿Alguna idea o operador de MySQL que realice esta operación con poco coste? Desde ya, gracias.
Por cierto, la query que lanzo es similar a esta, y tarda alrededor de 3 segundos, devolviendo algo más de 2000 tuplas.
mysql> SELECT se.sysmapid
 -> FROM ug, r, hg, se
 -> WHERE ug.userid = 6 AND
 -> r.groupid = ug.usrgrpid AND
 -> hg.groupid = r.id AND
 -> se.elementid = hg.hostid AND
 -> se.elementtype = 0
 -> GROUP BY se.sysmapid;
Sin embargo, si quiero emplear el truco anterior y uso un NOT IN, su ejecución se alarga en el tiempo y queda la query como sigue,
mysql> SELECT sysmapid FROM se
 -> WHERE sysmapid NOT IN (
 ->   SELECT se.sysmapid
 ->   FROM ug, r, hg, se
 ->   WHERE ug.userid = 6 AND
 ->   r.groupid=ug.usrgrpid AND
 ->   r.permission NOT IN (2,3) AND
 ->   hg.groupid = r.id AND
 ->   se.elementid = hg.hostid AND
 ->   se.elementtype=0
 ->   GROUP BY se.sysmapid
 -> );


4 comentarios :

  1. Tengo el MySQL olvidado, pero probaste a negar el WHERE usando las Leyes de Demorgan? ;-
    En tu caso como tienes condiciones de JOIN que supongo que quieres mantener, entiendo que la query debe quedar como sigue:
    SELECT se.sysmapid FROM ug, r, hg, se WHERE (r.groupid = ug.usrgrpid AND hg.groupid = r.id AND se.elementid = hg.hostid) AND (ug.userid <> 6 OR se.elementtype <> 0) GROUP BY se.sysmapid;

    ResponderEliminar
  2. No he probado, pero lo haré a ver qué pasa. Por la peculiaridad del asunto, la forma mas simple es sacar la que puse. Obtengo las tuplas o ids para los que no tengo permiso, para luego hacer la inversa de la query, que serán en las que sí tenga permiso.

    La query que pegaste, con el OR ahí me descoloca, ya que pierdo los permisos. Probaré con Demorgan, pero pensé que mysql tendría algo para devolver todo lo no seleccionado :-)

    ResponderEliminar
  3. Estoy de acuerdo con Pepe, creo que la solución que él da es la mejor. Lo único es que quizás a ti te interesa mantener también la condición de que el ug.userid sea 6. Si esto fuera así, lo quitarías de la parte del WHERE que niegas. En dicho caso la query quedaría:

    SELECT se.sysmapid FROM ug, r, hg, se WHERE (r.groupid = ug.usrgrpid AND hg.groupid = r.id AND se.elementid = hg.hostid) AND (ug.userid = 6) AND (se.elementtype <> 0) GROUP BY se.sysmapid;

    ResponderEliminar
  4. @Javi: lo del OR no es necesario, solo simplifiqué con Demorgan la expresión:

    ... AND NOT(ug.userid = 6 AND se.elementtype = 0)

    suponiendo que ese NOT está bien usado (ya digo que tengo el Mysql olvidado). ¿Qué quiere decir que pierdes los permisos?


    Ah, acabo de ver que lo de "r.permission NOT IN (2,3)" no estaba en tu query inicial (la que yo usé). La negada de "r.permission NOT IN (2,3)" sería "r.permission=2 OR r.permission=3", por lo que la query que buscas podría ser así:

    SELECT se.sysmapid FROM ug, r, hg, se WHERE (r.groupid = ug.usrgrpid AND hg.groupid = r.id AND se.elementid = hg.hostid) AND (ug.userid <> 6 OR se.elementtype <> 0 OR r.permission = 2 OR r.permission = 3) GROUP BY se.sysmapid;

    Si te fijas, la query tendría la forma "... WHERE (condiciones_a_cumplir) AND (condiciones_negadas) ...". Como bien dice José Miguel, si quieres mantener ciertas condiciones las metes en el primer grupo.

    ResponderEliminar

Formulario de contacto

Nombre

Correo electrónico *

Mensaje *

Últimos comentarios