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.
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.
DELETE ... USINGpermite borrar filas de una tabla filtrandolas contra otra, al estilo de unJOIN, 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 deorders. La tabla de filtrado va enUSINGy la condicion de enlace enWHERE.DELETE FROM orders o USING users u WHERE o.user_id = u.id AND u.country = 'US';Aqui
orderses la tabla destino y de ella salen filas.usersse incorpora solo para filtrar y nunca se modifica. La clausulao.user_id = u.idcumple el papel deONen unJOINnormal, yu.country = 'US'es un filtro adicional. Se borran exactamente las filas deordersque encuentran pareja enusers.USINGva la tabla por la que filtras, no aquella de la que borras.WHERE, no unONaparte.o,u) mantienen la consulta legible y son obligatorios si los nombres de columna coinciden.Varias tablas en USING
Puedes listar varias tablas en
USINGseparadas por comas; se unen entre si y con la tabla destino mediante las condiciones delWHERE. 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
JOINde tres tablas: PostgreSQL construye la unionordersxusersxemployeesy luego borra cada fila deordersque sobrevive. Si una fila deorderscoincide 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:
USINGexpone las columnas de la segunda tabla dentro delWHERE, asi que filtrar por varios campos queda plano, sin anidamiento.INdevuelve una sola columna; para comparar un par de columnas tienes que pasar aEXISTSo a constructores de fila, y la legibilidad empeora.USINGes mas claro cuando ademas necesitas una condicion de la segunda tabla, no solo una lista de claves.Seguridad del WHERE: el riesgo principal
El error mas caro con
DELETE ... USINGes omitir o estropear la condicion de enlace. Si quitaso.user_id = u.idobtienes un producto cartesiano, que borra la tabla destino entera: cada fila deordersencuentra al menos una pareja enusers.-- DANGER: no join predicate -> deletes every row in orders DELETE FROM orders o USING users u WHERE u.country = 'US';Salvaguardas sencillas:
DELETEen unSELECTcon el mismoFROM/USING/WHEREe inspecciona que se borraria.BEGIN; ... ROLLBACK;) hasta confiar en el numero de filas.COMMIT.DELETE multitabla en MySQL
MySQL no tiene
USINGen este sentido; en su lugar ofrece unDELETEmultitabla con unJOINexplicito. El detalle clave: entreDELETEyFROMindicas 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 unDELETEligero en versiones recientes) y no hay semantica de joinUSING. En resumen: en PostgreSQL usaDELETE ... USING, en MySQLDELETE ... JOIN, y verifica siempre el predicado de enlace antes de ejecutar.