SQL-Server: Anti-Join con NOT EXISTS()

Habíamos visto dos técnicas para hacer un anti-join en SQL-Server sin recurrir al NOT IN(): la primera con un left join y comprobando qué campos vienen a nulo y la segunda con el operado EXCEPT. ¿Hay una tercera opción? Sí, la hay: NOT EXISTS(). Como recordaréis ya habíamos visto por aquí que EXISTS() es una función de SLQ-Server muy bien optimizada, y en este caso podemos usarla también.

Vamos a poner un ejemplo: Tenemos la tabla Oficinas y tenemos la tabla Empleados. Las oficinas tienen un campo IdOficina que las identifica, los empleados también tiene un campo IdOficina que nos dicen en qué oficina trabajan. Queremos sacar en una consulta qué oficinas no tienen empleado. Veamos la consulta:

Select 
  o.IdOficina
from 
  Oficinas o
where 
  NOT EXISTS 
    (Select * from Empleados e where e.IdOficina = o.IdOficina)

¿Cual de los tres métodos es mejor? La recomendación es que le pegues un ojo al plan de ejecución para que veas cual tiene un menor coste.

Anuncios

SQL-Server: Hacer un anti-join con EXCEPT

El otro día os comentaba cómo hacer un anti-join en SQL-Server (a decir verdad con un método que valdría para cualquier SGBD), pero me dejé fuera otra forma de hacerlo en SQL-Server: con el operador EXCEPT.

La definción de EXCEPT vendría a ser la siguiente: Devuelve los valores distintos de la consulta situada a la izquierda del operando EXCEPT que no se devuelven desde la consulta derecha.

Vamos con un ejemplo: Tenemos la tabla Productos y tenemos la tabla Ventas, y el campo IdProducto existe en ambas, siendo la clave primaria de la tabla Productos y figurando como clave externa en tabla Ventas. Entonces ¿Podemos con EXCEPT sacar los Id de todos los Productos que no existen en la tabla Ventas? Podemos. Tal que así:

Select IdProducto from Productos
EXCEPT
Select IdProducto from Ventas

Al igual que ocurren con el operador UNION, el operador EXCEPT nos obliga a tener las mismas columnas, con tipos de datos compatibles, en cada una de las consultas.

SQL: cómo hacer un ANTI JOIN

Te habrá pasado más de una vez: necesitas sacar todos los valores de la Tabla1 que no aparezcan en la Tabla2. Es decir, la operación opuesta a un INNER JOIN. En muchos casos vemos que hay quien usa un NOT IN() como solución:

Nota: Asumimos que las tablas están bien formadas y que la Tabla1 se relaciona con la Tabla2 por medio de una clave externa (en el ejemplo será el campo IdClaveExterna en Tabla2 que está relacionado con el campo id de Tabla1)

SELECT * FROM Tabla1 
WHERE Tabla1.id NOT IN (SELECT IdClaveExterna FROM Tabla2)

Y esto está mal. Muy mal. Porque el IN() tiene un rendimiento muy bajo, como hemos visto en el pasado, y su negación pues todavía ofrece uno peor. Entonces ¿cómo lo hacemos? No tenemos una unión que sea ANTI JOIN, pero lo podemos resolver de la siguiente forma ya que tenemos un campo Id en cada tabla para cada fila sirviendo como clave primaria:

SELECT * FROM Tabla1 
LEFT JOIN Tabla2 ON Tabla1.id = Tabla2.idClaveExterna
WHERE Tabla2.id IS NULL

De esta forma cogemos todos los valores de la Tabla1 unidos a la Tabla2 por la clave externa, pero descartando de la consulta aquellos valores que nos devuelvan nulo el valor de la clave primaria de la Tabla2.

Modificar los atributos data de HTML5 con jQuery

Si en una entrada anterior vimos cómo leer los atributos data de HTML5 con jQuery hoy vamos a ver el otro caso: modificar dichos atributos.

El ejemplo va a ser el mismo que en el anterior artículo:

<div id='fruta' data-fruta='fresa'></div>

Ok, tenemos un div con el id fruta, el atributo data-fruta y en principio el valor fresa. ¿Cómo lo modifico haciendo uso de jQuery? Pues de nuevo con el método data(), pero en este caso pasándole dos parámetros en lugar de uno:

$("#fruta").data("fruta","pera");
window.alert($("#fruta").data("fruta"));

Si el método data() recibe solo el nombre del atributo al que queremos acceder nos lo devuelve, pero si recibe como segundo atributo una cadena entonces modifica el valor del atributo asignándole el de ese segundo parámetro que le pasamos.

Y voy a repetir exactamente el mismo párrafo que en el artículo anterior sobre la nomenclatura (para que no tengas que abrirlo): Fíjate que cuando le paso el nombre del atributo lo hago quitándole el prefijo data-. Y hay otra peculiaridad: si el nombre tiene varios guiones entonces estos desaparecen y escribimos el atributo siguiendo la convención camelCase.

<div id='fruta' data-fruta-oferta='pera'></div>
<script type="javascript">
$("#fruta").data("frutaOferta","fresa");
window.alert($("#fruta").data("frutaOferta"));
</script>

X11 forwarding sobre ssh

Llevo tanto tiempo lidiando con servidores Windows que se me empiezan a anquilosar las habilidades linuxeras más allá de lo que viene a ser el día a día. Creo que necesito preparar el LPIC aunque sólo sea para mantenerme en forma. El caso, que hoy tenía que ejecutar remotamente una aplicación con entorno gráfico de un servidor Linux y tuve que ir a ojear los viejos apuntes de sistemas operativos que guardo en una carpeta en la nube con manuales de todo tipo. Seguro que hay mil entradas por ahí pero yo os lo cuento igual:

Paso 1: Habilitamos el X11 forwarding en el servidor. ¿Cómo? Editando el fichero /etc/ssh/ssh_config y buscando allí la línea que pone X11Forwarding  para ponerle el valor en YES. Y tras esto reiniciamos el demonio de SSH.

Paso 2: nos conectamos mediante ssh pasando el parámetro -X:

ssh -X usuario@nombreEquipo

Y luego ya ejecutamos el programa en cuestión.

Si el cableado o la conexión es malo puede que te interese usar la opción que comprime los datos que te enviará el servidor. Carga más trabajo en la memoria del servidor y del cliente, pero reduce el tamaño del tráfico de red:

ssh -X -C usuario@nombreEquipo

 

Seguir partidos de la NBA desde un terminal de Linux

Mis amigos saben que me encanta el baloncesto, Linux y las cosas altamente freaks. Y esta mañana, cuando me levanté, me encontré con que uno me había compartido en el muro de Facebook esta entradade OMGUbuntu: Ver partidos de la NBA desde el terminal con NBA-Go.

¿Cómo va esto?

Bueno, lo primero es tener npm instalado instalado en vuestro equipo. Está en los repositorios de Ubuntu:

sudo apt-get update
sudo apt-get install nodejs
sudo apt-get install npm

Y una vez tengamos npm instalado vamos a instalar NBA-Go:

sudo npm install -g nba-go

El programa principalmente ofrece dos comandos: game y player (abreviados g y p). Dentro de la opción game hay dos opciones posibles: ver qué partidos hay disponibles y ver uno en concreto.

A la hora de ver los posibles tenemos las opciones date, yesterday, today y tomorrow. La primera para una fecha concreta, la segunda para los partidos de ayer, la tercera para los de hoy y la cuarta para los de mañana. Vemos ejemplos:

//fecha formato año/mes/día
$ nba-go game -d 2017/11/02
$ nba-go game --date 2017/11/02
//ayer
$ nba-go game -y
$ nba-go game --yesterday
//hoy
$ nba-go game -t
$ nba-go game --today
//mañana
$ nba-go game -T
$ nba-go game --tomorrow

Una vez listado podéis navegar con el partido, ver la información previa al juego o seguir el desarrollo del mismo con las estadísticas en directo. También podéis ver el boxscore del partido ya finalizado con toda la estadística.

Aquí os dejo un gif de la página oficial que ilustra esto:

Seleccionar partido

Partido en directo

Con el comando player tenemos tres opciones: información general, estadísticas de la temporada regular o de los playoff. Voy a usar el mismo ejemplo que en la página oficial para que cuadre con el gif que voy a usar de la misma.

//info de Curry
$ nba-go player Curry -i
$ nba-go player Curry --info
//temporada regular
$ nba-go player Curry -r
$ nba-go player Curry --regular
//eliminatorias
$ nba-go player Curry -p
$ nba-go player Curry --playoffs
//todo junto
$ nba-go player Curry -i -r -p

Información de jugadores

Os dejo por aquí el enlace al proyecto en GitHub por si queréis ver el código o si queréis clonarlo.

SQL-Server: Select * vs Select 1 en una cláusula EXISTS()

Una duda que me asaltó hoy ¿Es más eficiente usar Select * o usar Select 1 en una subconsulta de una cláusula EXISTS?

En principio podríamos pensar que Select 1, al no tener que devolver una lista de columnas,sería mucho más eficiente. Pero si analizamos el plan de ejecución de la consulta vemos que no, que el rendimiento es similar. ¿Por qué?

La propia Microsoft lo explica en esta entrada de sus manuales: SQL-Server no produce ningún dato cuando una consulta está dentro de EXISTS, simplemente evalúa el WHERE de dicha consulta para comprobar si devolvería algo y, según esto se cumpla, devuelve TRUE o FALSE. Por tanto, usar Select * en este caso no empeoraría el rendimiento de la consulta.