Menú de navegaciónMenú
Categorías

La mejor forma de Aprender Programación online y en español www.campusmvp.es

TUTORIAL SQL #4: Consultas SELECT multi-tabla - Tipos de JOIN

Imagen ornamental

Continuando en nuestro camino para aprender SQL desde cero, recordemos primero que en la anterior entrega de esta serie vimos cómo funcionan las consultas multi-tabla  basadas en JOIN. Ahora vamos a aprender más formas de realizar la unión de tablas que nos permitirán controlar mejor los conjuntos de resultados que obtenemos.

Nota: Si esta serie de artículos te está pareciendo interesante, entonces ni te imaginas lo que puedes aprender con este curso de fundamentos de SQL.

Combinaciones internas - INNER JOIN

Las combinaciones internas se realizan mediante la instrucción INNER JOIN. Devuelven únicamente aquellos registros/filas que tienen valores idénticos en los dos campos que se comparan para unir ambas tablas. Es decir aquellas que tienen elementos en las dos tablas, identificados éstos por el campo de relación.

La mejor forma de verlo es con un diagrama de Venn que ilustre en qué parte de la relación deben existir registros:

INNER-JOIN

En este caso se devuelven los registros que tienen nexo de unión en ambas tablas. Por ejemplo, en la relación entre las tablas de clientes y pedidos en Northwind, se devolverán los registros de todos los clientes que tengan al menos un pedido, relacionándolos por el ID de cliente.

Esto puede ocasionar la desaparición del resultado de filas de alguna de las dos tablas, por tener valores nulos, o por tener un valor que no exista en la otra tabla entre los campos/columnas que se están comparando.

Su sintaxis es:

FROM Tabla1 [INNER] JOIN Tabla2 ON Condiciones_Vinculos_Tablas

Así, para seleccionar los registros comunes entre la Tabla1 y la Tabla2 que tengan correspondencia entre ambas tablas por el campo Col1, escribiríamos:

SELECT T1.Col1, T1.Col2, T1.Col3, T2.Col7
FROM Tabla1 T1 INNER JOIN Tabla2 T2 ON T1.Col1 = T2.Col1

Por ejemplo, para obtener en Northwind los clientes que tengan algún pedido, bastaría con escribir:

SELECT OrderID, C.CustomerID, CompanyName, OrderDate
FROM Customers C INNER JOIN Orders O ON C.CustomerID = O.CustomerID 

Que nos devolverá 830 registros. Hay dos pedidos en la tabla de Orders sin cliente asociado, como puedes comprobar, y éstos no se devuelven por no existir la relación entre ambas tablas.

En realidad esto ya lo conocíamos puesto que en las combinaciones internas, el uso de la palabra INNER es opcional (por eso lo hemos puesto entre corchetes). Si simplemente indicamos la palabra JOIN y la combinación de columnas (como ya hemos visto en el artículo anterior) el sistema sobreentiende que estamos haciendo una combinación interna. Lo hemos incluido por ampliar la explicación y por completitud.

Combinaciones externas (OUTER JOIN)

Las combinaciones externas se realizan mediante la instrucción OUTER JOIN. Como enseguida veremos, devuelven todos los valores de la tabla que hemos puesto a la derecha, los de la tabla que hemos puesto a la izquierda o los de ambas tablas según el caso, devolviendo además valores nulos en las columnas de las tablas que no tengan el valor existente en la otra tabla.

Es decir, que nos permite seleccionar algunas filas de una tabla aunque éstas no tengan correspondencia con las filas de la otra tabla con la que se combina. Ahora lo veremos mejor en cada caso concreto, ilustrándolo con un diagrama para una mejor comprensión.

La sintaxis general de las combinaciones externas es:

FROM Tabla1 [LEFT/RIGHT/FULL] [OUTER] JOIN Tabla2 ON Condiciones_Vinculos_Tablas

Como vemos existen tres variantes de las combinaciones externas.

En todas estas combinaciones externas el uso de la palabra OUTER es opcional. Si utilizamos LEFT, RIGHT o FULL y la combinación de columnas, el sistema sobreentiende que estamos haciendo una combinación externa.

Variante LEFT JOIN

Se obtienen todas las filas de la tabla colocada a la izquierda, aunque no tengan correspondencia en la tabla de la derecha.

Así, para seleccionar todas las filas de la Tabla1, aunque no tengan correspondencia con las filas de la Tabla2, suponiendo que se combinan por la columna Col1 de ambas tablas escribiríamos:

SELECT T1.Col1, T1.Col2, T1.Col3, T2.Col7
FROM Tabla1 T1 LEFT [OUTER] JOIN Tabla2 T2 ON T1.Col1 = T2.Col1

Esto se ilustra gráficamente de la siguiente manera:

LEFT-JOIN

De este modo, volviendo a Northwind, si escribimos la siguiente consulta:

SELECT OrderID, C.CustomerID, CompanyName, OrderDate
FROM Customers C LEFT JOIN Orders O ON C.CustomerID = O.CustomerID 

Obtendremos 832 registros ya que se incluyen todos los clientes y sus pedidos, incluso aunque no tengan pedido alguno. Los que no tienen pedidos carecen de la relación apropiada entre las dos tablas a partir del campo CustomerID. Sin embargo se añaden al resultado final dejando la parte correspondiente a los datos de la tabla de pedidos con valores nulos, como se puede ver en esta captura de SQL Server:

LEFT-JOIN-Resultados

Variante RIGHT JOIN

Análogamente, usando RIGHT JOIN se obtienen todas las filas de la tabla de la derecha, aunque no tengan correspondencia en la tabla de la izquierda.

Así, para seleccionar todas las filas de la Tabla2, aunque no tengan correspondencia con las filas de la Tabla1 podemos utilizar la cláusula RIGHT:

SELECT T1.Col1, T1.Col2, T1.Col3, T2.Col7
FROM Tabla1 T1 RIGHT [OUTER] JOIN Tabla2 T2 ON T1.Col1 = T2.Col1

El diagrama en este caso es complementario al anterior:

RIGHT-JOIN

Si en nuestra base de datos de ejemplo queremos obtener todos los pedidos aunque no tengan cliente asociado, junto a los datos de dichos clientes, escribiríamos:

SELECT OrderID, C.CustomerID, CompanyName, OrderDate
FROM Customers C RIGHT JOIN Orders O ON C.CustomerID = O.CustomerID

En este caso se devuelven 830 registros que son todos los pedidos. Si hubiese algún pedido con el CustomerID vacío (nulo) se devolvería también en esta consulta (es decir, órdenes sin clientes), aunque en la base de datos de ejemplo no se da el caso.

Variante FULL JOIN

Se obtienen todas las filas en ambas tablas, aunque no tengan correspondencia en la otra tabla. Es decir, todos los registros de A y de B aunque no haya correspondencia entre ellos, rellenando con nulos los campos que falten:

FULL-JOIN

Es equivalente a obtener los registros comunes (con un INNER) y luego añadirle los de la tabla A que no tienen correspondencia en la tabla B, con los campos de la tabla vacíos, y los registros de la tabla B que no tienen correspondencia en la tabla A, con los campos de la tabla A vacíos.

Su sintaxis es:

SELECT T1.Col1, T1.Col2, T1.Col3, T2.Col7
FROM Tabla1 T1 FULL [OUTER] JOIN Tabla2 T2 ON T1.Col1 = T2.Col1

Por ejemplo, en Northwind esta consulta:

SELECT OrderID, C.CustomerID, CompanyName, OrderDate
FROM Customers C FULL JOIN Orders O ON C.CustomerID = O.CustomerID

nos devuelve nuevamente 832 registros: los clientes y sus pedidos, los clientes sin pedido (hay 2) y los pedidos sin cliente (que en este caso son 0).

En una próxima entrega, vamos a aprender a hacer subconsultas, que te darán un nuevo nivel de posibilidades, operaciones de conjuntos, uniendo, intersecando y restando resultados.

José M. Alarcón Aguín Fundador de campusMVP, es ingeniero industrial y especialista en consultoría de empresa. Ha escrito diversos libros, habiendo publicado hasta la fecha cientos de artículos sobre informática e ingeniería en publicaciones especializadas. Microsoft lo ha reconocido como MVP (Most Valuable Professional) en desarrollo web desde el año 2004 hasta la actualidad. Puedes seguirlo en Twitter en @jm_alarcon o leer sus blog técnico o personal. Ver todos los posts de José M. Alarcón Aguín
Archivado en: Acceso a Datos

Boletín campusMVP.es

Solo cosas útiles. Una vez al mes.

🚀 Únete a miles de desarrolladores

DATE DE ALTA

x No me interesa | x Ya soy suscriptor

La mejor formación online para desarrolladores como tú

Comentarios (21) -

EfrainMejiasC
Bolivarian Republic of Venezuela EfrainMejiasC

Muy Bueno todos los tutoriales ...los estoy check 1 a 1

como se hace en el caso de que quiera mostrar los elementos de una tabla que NO esten en otra..

demasiado tarde pero si aun les sirve pueden utilizar el LETF JOIN
con esto los datos de la tabla derecha siempre tendran valor y la tabla izquierda aveces si y otras no, por ejemplo.

tengo esta consulta en visual 6.0
w = numero.text

SELECT  tablaIzquierda.numero, tablaDerecha.nombre  FROM tablaIzquierda LEFT JOIN tablaDerecha ON tablaIzquierda.numero = tablaDerecha.numero where tablaIzquierda.numero =  " + w + " ")

bueno el curso...

Aural Lanuza
Aural Lanuza

Hola.. Necesito su ayuda.
No se mucho de MySQL, soy nuevo en el tema.

Pero como le hago para relacionar 5 tablas donde mi "Lógica de Relación" es:

a).- la tabla PLANES tiene o relaciona con: 1 o + filas del campo "producto" en la tabla PRODUCTOS.
b).- La tabla PRODUCTOS tiene o relaciona con: 1 o + filas del campo "titulo" de la tabla TITULOS.
c).-  La tabla TITULOS tiene o se relaciona con: 1 o + filas del campo "actividad"  en la tabla de ACTIVIDADES
d).- La tabla ACTIVIDADES contendra en su campo "responsable" : 1 o + "empleados" de la tabla EMPLEADOS.

siendo las tablas de la base de datos:
1.- EMPLEADOS
2.- PLANES
3.- PRODUCTOS
4.- TITULOS
5.-ACTIVIDADES

José Manuel Alarcón
José Manuel Alarcón

Hola Aural:

Con la información que tienes en el artículo y en el libro gratuito tienes información más que suficiente para poder conseguir enlazar esas tablas por ti mismo.

Esa es una de las operaciones más básicas que se pueden hacer en SQL, así que debes aprender a hacerlo por ti mismo o no te servirá de nada.

La única forma de aprender es practicar, intentarlo, confundirse y volver a intentarlo, así que ¡ánimo! :-)

hola
para hacer una union de tablas o varias la sintaxis deberia ser asi:
from tabla1 join tabla2 => aqui llamas las tablas que quieres unir solo tablas que estan relacionadas entre si , me refiero a que no puedes unir una tabla1 con la tabla3 , debieses unir la tabla1 con la tabla2 y asi unirla con la tabla3, despues de eso va "on" :
on tabla2.llaveForanea = tabla1.llaveForanea => en esta parte debes colocar las llaves foraneas o sea debiese ir el atributo que los une anteponiendo la tabla (aunque debiese ir un alias de la tabla pero no te quiero confundir) con un punto y el atributo que los une si quieres unir la tabla 3 seria asi
join tabla3
on tabla2.llaveforanea = tabla3llaveforanea;
asi quedaria la union de tres tablas por ejemplo
o sea la sintaxis seria asi (sin comentarios):

from tabla1 join tabla2
on tabla2.llaveForanea = tabla1.llaveForanea
join tabla3
on tabla2.llaveforanea = tabla3llaveforanea;

NELSON QUIROZ
NELSON QUIROZ

HOLA BUENOS DIAS, MI PROBLEMA ES CON LA ACTUALIZACION DE UN CAMPO DE UNA TABLA DESDE UNA BASE DE DATOS A OTRA DIFERENTE:

BASE DE DATOS A: PRUEBADB
BASE DE DATSO B: PROBLEMADB
TABLA: CUENTASXCOBRAR
CAMPO EN PRUEBADB: NUMEROD
CAMPO EN PROBLEMADB: DOCUMD

POR FAVOR AYUDAME CON ESTO, GRACIAS

excelente informacion, mmuy bien explicado,
los graficos ayudan mucho en la forma de enseñar.
gracias

francis arias
francis arias

buen post, eso lo entiendo muy bien, pero si n quiero q m muestre un null sino 0 cero, que tendria q hacer? saludos.....

José Manuel Alarcón
José Manuel Alarcón

Hola Francis:

Pues si es en SQL Server es muy sencillo. Basta con que en lugar de devolver directamente el campo, lo sustituyas por una llamada a esta función:

ISNULL(nombreCampo, 0 )

de modo que si el campo es nulo, se sustituirá por el valor que desees, en este caso un 0.

En Oracle la función se llama NVL pero se usa igual, sería:

NVL(nombreCampo,0)

y en MySQL el equivalente sería IFNULL:

IFNULL(nombreCampo,0)

o también te valdría COALESCE:

COALESCE(nombreCampo,0)

Saludos.

francis arias
francis arias

hola si funciona en la consulta en este caso  function

getRetencion($conn,$id,$nrodoc)
  
{
            $q = "SELECT COALESCE( (c.mntret) , 0) as mntret, a.nrodoc,a.nrocontrato,a.descripcion,a.montodoc, c.mntret, d.descri  ";
            $q.= "FROM finanzas.orden_pago AS a ";
            $q.= "INNER JOIN finanzas.relacion_retenciones_orden AS c ON (c.nrodoc=a.nrodoc) ";
            $q.= "INNER JOIN finanzas.retenciones_adiciones AS d ON (d.id::varchar=c.codret) ";
            $q.= "WHERE 1=1 AND d.id=19";
            $q.= "AND a.nrodoc NOT IN (SELECT nroref FROM finanzas.orden_pago  WHERE status='1' ) ";
            $q.= "ORDER BY 1 ";
       //die($q);
        $r = $conn->Execute($q);



        $this->mntrett = $r->fields['mntret'];
      

        return $this;
    }
pro en este caso este getRetencion m arrogar el dato en otra consulta:


function getOrdenPago($conn, $nrodoc,$nrocontrato, $descripcion, $proveedor, $dependencia, $from=0, $max=0, $orden="a.id"){
        try {
            $q = "SELECT a.nrodoc,a.nrocontrato,a.descripcion,b.nombre AS proveedor,a.montodoc,b.rif,b.id AS id_proveedor  ";
            $q.= "FROM finanzas.orden_pago AS a ";
            $q.= "INNER JOIN puser.proveedores AS b ON (b.id=a.id_proveedor) ";
          //  $q.= "INNER JOIN finanzas.relacion_retenciones_orden AS c ON (c.nrodoc=a.nrodoc) ";
          //  $q.= "INNER JOIN finanzas.retenciones_adiciones AS d ON (d.id::varchar=c.codret) ";
            $q.= "WHERE 1=1  ";
            $q.= !empty($proveedor) ? "AND (b.nombre ILIKE '%$proveedor%') ": "";
            $q.= !empty($nrodoc) ? "AND a.nrodoc ILIKE '%$nrodoc%' ": "";
            $q.= !empty($nrocontrato) ? "AND a.nrodoc ILIKE '%$nrocontrato%' ": "";
            $q.= !empty($descripcion) ? "AND a.descripcion ILIKE '%$descripcion%' ": "";
            $q.= "AND a.nrodoc NOT IN (SELECT nroref FROM finanzas.orden_pago  WHERE status='1' ) ";
            $q.= "ORDER BY 1 ";

          //die($q);
            $r = $conn->Execute($q);
            $this->total = $r->RecordCount();
            $r = ($max!=0) ? $conn->SelectLimit($q, $max, $from) : $conn->Execute($q);
            $collection=array();
            while(!$r->EOF){
                $ue = new responsabilidad_social;
                $ue->nroref = $r->fields['nrodoc'];
                $ue->nrocontrato = $r->fields['nrocontrato'];
                $ue->descripcion = $r->fields['descripcion'];
                $ue->id_proveedor = $r->fields['id_proveedor'];
                $ue->proveedor = $r->fields['proveedor'];
                $ue->rif = $r->fields['rif'];
                $ue->montodoc = $r->fields['montodoc'];
                $ue->montoRespon = $r->fields['montodoc']*0.03;
          $ue->getRetenciones($conn, $id,$nrodoc);
          $ue->getRetencion($conn, $id,$nrodoc);
                $coleccion[] = $ue;


                $r->movenext();
            }
          
            return $coleccion;
        }
        catch( ADODB_Exception $e ){
            if($e->getCode()==-1)
                return ERROR_CATCH_VFK;
            elseif($e->getCode()==-5)
                return ERROR_CATCH_VUK;
            else
                return ERROR_CATCH_GENERICO;
        }
    }

si con ese nrodoc mo existe el registro  mntret me arroja como resultado un null y quisiera q fuera un 0 cero me dicen que con una condicion en getretenciones:
/* if (!empty ($r->fields['mntret'])){
                    
                ISNULL($mntret)  or InStr($mntret=" ")= 0
      
                  } else {

              $this->mntrett = $r->fields['mntret'];
pro me arroja error

francis arias
francis arias

saludos y gracias de antemano cualquier respuesta q m pueda ayudar (-:

Rosa Carmen Navarro
Rosa Carmen Navarro

Estoy utilizando esta formula

Select KC_EVALUA from KARCURSO INNER JOIN COLABORA ON KARCURSO.CB_CODIGO = COLABORA.CB_CODIGO where KARCURSO.CU_CODIGO='170001' AND KARCURSO.CB_CODIGO = COLABORA.CB_CODIGO and  KC_FEC_TOM = (SELECT max(KC_FEC_TOM) FROM KARCURSO WHERE KARCURSO.CU_CODIGO='170001' AND KARCURSO.CB_CODIGO=COLABORA.CB_CODIGO)

y me trae las calificaciones de todos los alumnos que tomaron el curso, pero si yo quiero que unicamente me traiga primero la calificacion del alumno 1 y en otro renglon la calificacion del alumno 2 y asi sucesivamente, me puedes decir como puedo hacerlo, no se si ya me cicle porque no puedo dar con la formula

Hola, tengo un problema y no encuentro solucion. Ejemplo, tengo una tabla de clientes y una tabla de telefonos, que 1 cliente puede tener varios registros de telefonos, pero cuando muestro el listado de clientes quiero que me salga un solo registro por cliente con telefono, ahora me pasa que si un cliente tiene 2 telefonos me sale el registro repetido 1 por cada telefono.
Saludos
Gracias

campusMVP
campusMVP

Hola Sebastian:

Si lo que quieres es que te salgan todos concatenados en un solo campo devuelto por la consulta tendrás que hacer una subconsulta que haga justamente eso. Es decir, uno de los campos devueltos por tu consulta es a su vez otra consulta que se encarga de concatenar todos los teléfonos que tenga el cliente actual.

Saludos.

Gracias, esa esta buena, seria una solución.  Usaria en la subconsulta top 1 para que me muestre un solo telefono..

Muchisimas gracias
Saludos

Fernando Rilo
Fernando Rilo

Buenas, yo tengo dos tablas en las que uní con un UNION ALL para crear una sola consulta y mi pregunta es, las tablas son casi iguales y lo que quiero es que me junte el campo nombre y me cree dos columnas una con la suma de los nombres iguales de la primera tabla y en otra columna la suma de los nombres iguales de la segunda tabla. No se si me explico. ejemplo

Nombre         Tabla1         Tabla2
Fernando            5                    2

Buenas tengo una situación tengo 5 reportes de empresas en excel cada reporte tiene información diferente estos en ocasiones solo coinciden en 2 campos el código y nombre de la empresa, en ocasiones una empresa puede solo aparecer en un, en dos o en todos los reportes, cuando deseo realizar una la consulta de algún dato debo ingresar al reporte donde viene la información solicitada.
Pensé  en crear una base de datos con todos estos reportes por lo cual primero importe dichos reportes en excel a mi base de datos paso siguiente fue el combinar toda la información de los reportes en una sola tabla para que en ella contenga toda la información, este paso lo resolví de una manera bastante tediosa fue combinar la información de 2 tablas y el resultado de esta combinarlo con una tabla que también había sido resultado de una combinación de 2 tablas, para que al final el resultado de esto hacer una última combinación con una tabla final en el proceso combine 5 tablas en una sola. Realice 4 consultas SQL utilizando JOIN y por cada consulta generaba una tabla más. Es obvio es debe existir una manera de simplificar esto para lo cual recuro a ustedes por asesoramiento sobre cómo realizarlo. Espero puedan ayudarme en mi situación. Gracias

Ejemplo de tablas
https://imgur.com/lu2SMU3

Objetivo:
https://imgur.com/a/85Eif

leonel Velasco Morales
leonel Velasco Morales

tengo problemas para esta consulta
Listar los libros que han sido vendidos por más de un vendedor, visualizando los nombres de los vendedores y el título de los libros para corroborar que así sea,

Hola un favor mira tengo una base de datos en postgres y hasta ahora todo bien tengo un sistema, estoy haciendo un reporte sobre el total de presupuesto de una entidad bajo un esquema con INNER JOIN me da 32 502 879.62 (campo db_subtotal) pero utilizando otra consulta me da menos monto (cuando muestro al detalle otros campos), he probado haciéndolo con FULL JOIN, RIGHT JOIN pero nada me sigue dando un monto menor, me da más filas pero no coinciden las cantidades, pensé que había quizá algunos registros que están errados (valor null o similares a pesar que por código y tabla depuré eso para que al momento de grabar un nuevo registro no lo haga si no tiene los valores completos) pero no ubico el error, te agradeceré tu apoyo.

Esta es la consulta

SELECT
  -- SUM(tb_det_act_temp.db_subtotal)  -- PARA EL TOTAL
  tx_cod_uni_org||'-'||tx_nom_uni_org AS "UNIDAD",
  tb_obj_ope.tx_cod_obj_ope||'-'||
  tb_obj_ope.tx_nom_obj_ope AS "OBJETIVO",
  tb_act_ope.tx_cod_act||'-'||
  tb_act_ope.tx_nom_act AS "ACTIVIDAD",
  tb_det_act_temp.db_tipo AS "TIPO",  
  tb_det_act_temp.cod_prod AS "CODIGO",
  tb_det_act_temp.db_desc_prod AS "PRODUCTO",
  tb_det_act_temp.db_und AS "UND",
  tb_det_act_temp.db_cant_prod AS "CANT",  
  tb_det_act_temp.db_prec_prod AS "PRECIO",  
  tb_det_act_temp.db_subtotal AS "MONTO",
  (tb_det_act_temp.db_cant_1_prod+
  tb_det_act_temp.db_cant_2_prod+
  tb_det_act_temp.db_cant_3_prod) AS "1T",
  (tb_det_act_temp.db_cant_4_prod+
  tb_det_act_temp.db_cant_5_prod+
  tb_det_act_temp.db_cant_6_prod) AS "2T",
  (tb_det_act_temp.db_cant_7_prod+
  tb_det_act_temp.db_cant_8_prod+
  tb_det_act_temp.db_cant_9_prod) AS "3T",
  (tb_det_act_temp.db_cant_10_prod+
  tb_det_act_temp.db_cant_11_prod+
  tb_det_act_temp.db_cant_12_prod) AS "4T",
  (SELECT
CASE WHEN (CASE WHEN id_obj_est_1>0 THEN id_obj_est_1 END) ISNULL THEN '' ELSE id_obj_est_1||'.' END ||
CASE WHEN (CASE WHEN id_obj_est_2>0 THEN id_obj_est_2 END) ISNULL THEN '' ELSE id_obj_est_2||'.' END ||
CASE WHEN (CASE WHEN id_obj_est_3>0 THEN id_obj_est_3 END) ISNULL THEN '' ELSE id_obj_est_3||'.' END ||
CASE WHEN (CASE WHEN id_obj_est_4>0 THEN id_obj_est_4 END) ISNULL THEN '' ELSE id_obj_est_4||'.' END ||
CASE WHEN (CASE WHEN id_obj_est_5>0 THEN id_obj_est_5 END) ISNULL THEN '' ELSE id_obj_est_5||'.' END ||
CASE WHEN (CASE WHEN id_obj_est_6>0 THEN id_obj_est_6 END) ISNULL THEN '' ELSE id_obj_est_6||'' END
FROM public.tb_obj_ope WHERE tb_obj_ope."Id_obj_ope" = tb_act_ope.int_cod_obj)||'-'||
tb_obj_ope.tx_cod_obj_ope
AS "OBES"
FROM
  tb_uni_org FULL JOIN tb_obj_ope
  ON tb_uni_org."Id_uni_org" = tb_obj_ope."int_Id_uni_org"
  FULL JOIN tb_act_ope
  ON tb_obj_ope."Id_obj_ope" = tb_act_ope.int_cod_obj
  FULL JOIN tb_det_act_temp
  ON tb_act_ope.id_act = tb_det_act_temp.cod_act
WHERE
  tb_obj_ope.int_anio_obj_ope=2018 AND
  tb_act_ope.flag_act = 'S'
ORDER BY
  tx_cod_uni_org||'-'||tx_nom_uni_org ASC,
  tb_obj_ope.tx_cod_obj_ope||'-'||
  tb_obj_ope.tx_nom_obj_ope ASC,
  tb_act_ope.tx_cod_act||'-'||
  tb_act_ope.tx_nom_act ASC

Pingbacks and trackbacks (1)+

No se aceptan más comentarios