# [26ai] JOIN_TO_ME

*El artículo también se encuentra publicado en Ingles* [***English version***](https://dbaintheshadow.com/26ai-join-to-me)*.*

En el artículo de hoy vamos a ver la nueva sintaxis introducida para simplificar aún más los **joins** evitando errores comunes, haciendo cada *sql* más legible y explicita, este tipo de consultas son ***Row Widening Only Queries*** (**RWOQs**) garantizando que cada join solo añade columnas y no crea filas adicionales.

Este nuevo tipo de **join** esta disponible desde la última versión que ha lanzado Oracle, **23.26.2**.

```sql
SQL> Select banner from v$version;

BANNER
--------------------------------------------------------------
Oracle AI Database 26ai Enterprise 
 Edition Release 23.26.2.0.0 - Production
```

Antes de meternos en faena con este laboratorio, además de estar con última versión de Oracle vamos a cargar en nuestra PDB el schema **HR**. Los scripts de este schema se encuentra en el *github* de **Oracle Sample Projects**.

Dejo aquí el enlace:

[Github Oracle Sample Projects](https://github.com/oracle-samples/db-sample-schemas)

Este el modelo que hemos cargado:

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/f4ddc664-d84c-4964-9211-a3bfc7dcd04a.png align="center")

Fijémonos cual son las tablas principales y cual son las tablas hijas ya que Oracle va usar las propias FK para resolver este **join**.

El tipo de join que aplica por defecto es **LEFT OUTER JOIN**.

Algunos beneficios son:

*   Simplicidad SQL para complejas consultas.
    
*   Mantiene filtros explícitos a nivel de where o inner.
    
*   Confirma en tiempo de ejecución la unicidad de los join en tiempo de ejecución.
    

Sin más, veamos algún ejemplo:

### Consulta simple

```sql
SQL> 
select FIRST_NAME, LAST_NAME, EMAIL, JOB_TITLE  
  from hr.employees aa 
 inner join hr.jobs bb 
    on aa.job_id = bb.job_id
 fetch first 10 rows only
```

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/fcf60c4f-42b6-4b00-a86e-921dc5036222.png align="center")

Veamos también el *explain plan*, para compararlo con el nuevo join:

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/97c82fec-9ede-4214-852e-607f20781068.png align="center")

Ahora realizamos la misma consulta pero usando la nueva sintaxis.

```sql
SQL>
select FIRST_NAME, LAST_NAME, EMAIL, JOB_TITLE  
  from hr.employees JOIN TO ONE ( hr.jobs )
 fetch first 10 rows only
```

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/9aca33e4-f5f7-4db1-959e-f4bbb02c9a59.png align="center")

El explain plan:

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/901ac589-75a2-4df0-bf76-24731fd2a2d1.png align="center")

Pues los planes son diferentes, en el primer caso resuelve por **NESTED LOOP** pero el nuevo join resuelve por **MERGE JOIN OUTER** ¿motivo? Pues porque el nuevo **JOIN** resuelve por **LEFT OUTER JOIN**.

Si ejecutamos la misma query, pero por **LEFT OUTER JOIN** en vez de **INNER**, los planes son idénticos:

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/1018291a-bb7c-45d8-8314-23abea992a4c.png align="center")

### Consulta compleja

```sql
SQL> r
Select STREET_ADDRESS, 
       POSTAL_CODE, 
       CITY,  
       COUNTRY_NAME, 
       REGION_NAME
  from locations aa
 inner join countries cc
    on aa.country_id  = cc.country_id
 inner join regions dd
    on cc.region_id   = dd.region_id
fetch first 10 rows only
/
```

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/39df7264-be40-4131-b846-725e25eca738.png align="center")

Ahora realizamos la misma consulta pero usando la nueva sintaxis.

```sql
SQL> 
Select STREET_ADDRESS, POSTAL_CODE, CITY,  COUNTRY_NAME, REGION_NAME
FROM
    locations aa
    JOIN TO ONE
        (INNER join countries cc
            on aa.country_id  = cc.country_id
         inner join regions dd
            on cc.region_id   = dd.region_id
)
fetch first 10 rows only
```

![](https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/9837cf1f-44a8-4223-8679-a2ab343eae42.png align="center")

Si nos fijamos, en ambos ejemplos, siempre la tabla de la izquierda es la que hace de tabla principal, y la derecha la hija. Pero ¿Qué pasaría si la tabla principal no tiene FK directa con la tabla hija?.

```sql
SQL> 
    Select STREET_ADDRESS, POSTAL_CODE, CITY
          from locations JOIN TO ONE (departments)
      fetch first 10 rows only
      from locations JOIN TO ONE (departments)
                                  *
ERROR at line 2:
ORA-18641: No join key found for "DEPARTMENTS"
Help: https://docs.oracle.com/error-help/db/ora-18641/
```

Si nos fijamos en el modelo, la tabla LOCATIONS no tiene FK hacía DEPARTMENTS. La FK está definida en DEPARTMENTS, no en LOCATIONS.

Espero que os guste. ¡Nos vemos en el próximo artículo!
