sqlpostgresqldeletejoin

DELETE ... USING en SQL: borrados con JOIN sin subconsulta

Como DELETE ... USING filtra filas contra otra tabla con un join, por que supera a WHERE IN con subconsulta y la forma multitabla de MySQL.

3 min de lecturaReferencesql · postgresql · delete · join · mysql

DELETE ... USING permite borrar filas de una tabla filtrandolas contra otra, al estilo de un JOIN, sin subconsulta correlacionada. Es sintaxis nativa de PostgreSQL, y varios motores ofrecen la misma idea con pequenas variaciones.

Sintaxis basica

Supongamos que hay que borrar todos los pedidos de usuarios de EE. UU. El pais esta en la tabla users, pero queremos borrar de orders. La tabla de filtrado va en USING y la condicion de enlace en WHERE.

DELETE FROM orders o
USING users u
WHERE o.user_id = u.id
  AND u.country = 'US';

Aqui orders es la tabla destino y de ella salen filas. users se incorpora solo para filtrar y nunca se modifica. La clausula o.user_id = u.id cumple el papel de ON en un JOIN normal, y u.country = 'US' es un filtro adicional. Se borran exactamente las filas de orders que encuentran pareja en users.

  • En USING va la tabla por la que filtras, no aquella de la que borras.
  • El enlace entre tablas es un predicado normal en WHERE, no un ON aparte.
  • Los alias (o, u) mantienen la consulta legible y son obligatorios si los nombres de columna coinciden.

Varias tablas en USING

Puedes listar varias tablas en USING separadas por comas; se unen entre si y con la tabla destino mediante las condiciones del WHERE. Borremos pedidos ligados a gerentes del departamento de ventas.

DELETE FROM orders o
USING users u, employees e
WHERE o.user_id = u.id
  AND u.id = e.manager_id
  AND e.dept = 'sales';

La logica refleja un JOIN de tres tablas: PostgreSQL construye la union orders x users x employees y luego borra cada fila de orders que sobrevive. Si una fila de orders coincide con varias filas de la derecha, se borra una sola vez; los duplicados de la derecha no multiplican el borrado.

DELETE ... USING frente a una subconsulta WHERE IN

La forma clasica de expresar lo mismo es una subconsulta:

DELETE FROM orders
WHERE user_id IN (
  SELECT id FROM users WHERE country = 'US'
);

Ambas son correctas, pero hay una diferencia practica:

  • USING expone las columnas de la segunda tabla dentro del WHERE, asi que filtrar por varios campos queda plano, sin anidamiento.
  • Una subconsulta con IN devuelve una sola columna; para comparar un par de columnas tienes que pasar a EXISTS o a constructores de fila, y la legibilidad empeora.
  • El planificador de PostgreSQL suele convertir ambas formas en un semi-/hash-join, por lo que rinden parecido. Pero USING es mas claro cuando ademas necesitas una condicion de la segunda tabla, no solo una lista de claves.

Trampa: NOT IN con subconsulta se rompe en silencio con NULL: si la subconsulta produce aunque sea un NULL, devuelve vacio y no borra nada. Para "borrar pedidos de usuarios que no estan en una whitelist", prefiere NOT EXISTS o LEFT JOIN ... IS NULL antes que NOT IN.

Seguridad del WHERE: el riesgo principal

El error mas caro con DELETE ... USING es omitir o estropear la condicion de enlace. Si quitas o.user_id = u.id obtienes un producto cartesiano, que borra la tabla destino entera: cada fila de orders encuentra al menos una pareja en users.

-- DANGER: no join predicate -> deletes every row in orders
DELETE FROM orders o
USING users u
WHERE u.country = 'US';

Salvaguardas sencillas:

  • Convierte primero el DELETE en un SELECT con el mismo FROM/USING/WHERE e inspecciona que se borraria.
  • Envuelve la operacion en una transaccion (BEGIN; ... ROLLBACK;) hasta confiar en el numero de filas.
  • Revisa el recuento de filas informado: si parece sospechosamente alto, no hagas COMMIT.

DELETE multitabla en MySQL

MySQL no tiene USING en este sentido; en su lugar ofrece un DELETE multitabla con un JOIN explicito. El detalle clave: entre DELETE y FROM indicas de que tablas borras filas realmente.

DELETE o
FROM orders o
JOIN users u ON o.user_id = u.id
WHERE u.country = 'US';

Si escribes DELETE o, u, borras filas de ambas tablas a la vez: a veces comodo, pero mas arriesgado. En ClickHouse los borrados son asincronos (ALTER TABLE ... DELETE, o un DELETE ligero en versiones recientes) y no hay semantica de join USING. En resumen: en PostgreSQL usa DELETE ... USING, en MySQL DELETE ... JOIN, y verifica siempre el predicado de enlace antes de ejecutar.

Practica con ejercicios reales

Resuelve ejercicios en el entrenador de SQL con corrección instantánea y pistas.

Abrir el entrenador