Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   MS SQL Server (https://www.clubdelphi.com/foros/forumdisplay.php?f=23)
-   -   Duplicados con LEFT OUTER JOIN (https://www.clubdelphi.com/foros/showthread.php?t=27263)

Cabanyaler 16-11-2005 09:10:44

Duplicados con LEFT OUTER JOIN
 
Hola compañeros.

Quisiera consultar el siguiente tema.

Asunto:
Tuplas duplicadas en consulta de varias tablas unidas por left outer join.

Problema:
Cuando realizo esta consulta, se obtienen tuplas de la tabla principal CONTRATOS duplicadas. Además he observado que se producen según el número de tuplas de las tablas esclavas que se encuentran y cumplen su WHERE. Así pues, si existe CONTRATO que cumple la WHERE, se retorna una tupla, pero si además para ese mismo CONTRATO su cliente asociado también cumple su propia clausula WHERE se retorna otra tupla, idéntica a la anterior, y si además el COMERCIAL también cumple la WHERE se obtiene otra tupla igual, de ese modo obtengo tres tuplas del mismo CONTRATO iguales en la misma consulta.

Soluciones estudiadas:
He buscado como eliminar una vez realizada la consulta incluyendo duplicados, dichas tuplas duplicadas, ya que en la consulta incluyo la clave principal de los CONTRATOS, pero no puedo realizar esta limpieza de ese modo ya que no quiero eliminar nada en el servidor, simplemente los duplicados en la consulta en el lado cliente y si ejecuto un p.e. ConsultaConDuplicados.delete y posteriormente por cuestiones de operatividad del programa se realiza un ConsultaConDuplicados.UpdateBatch quizá elimine algo por algún efecto lateral.

Tampoco puedo incluir un SELECT DISTINCT ya que hago búsqueda por un campo Memo y con este tipo de campos no se puede incluir dicha clausula.

Incluyo código (abreviado) de la consulta por si tengo algún fallo en su propio diseño.

Muchas gracias por vuestro tiempo y conocimiento. Un saludo.

Código SQL [-]

/* Retorna tots els Contratos que pasen els filtres establist amb les diferents clausules del WHERE
   Amb Rel.lació amb  
             CLIENTES y COMERCIALES
   amb telèfon o sense'll */
       
CREATE PROCEDURE ADOQ_Contratos_Clientes_Comerciales

/* Parametres del Contrato */
@NumCon_sp varchar(7), ....AS

SELECT 
   Contratos.*
FROM
   Contratos

   /* Unisc per C.Aj amb Clientes i estos amb TelefonosCli*/
   LEFT OUTER JOIN
   Clientes LEFT OUTER JOIN TelefonosCli     
                         ON TelefonosCli.CodCli_TelCli = Clientes.CodAut_Cli
   ON Clientes.CodAut_Cli = Contratos.CodCli_Con

   /* Unisc amb Comerciales */
   LEFT OUTER JOIN Comerciales
      ON Comerciales.CodAut_Com = Contratos.CodCom_Con      

WHERE  
( 
   (   (Contratos.NumCon_Con LIKE '%' + @NumCon_sp + '%' )
   AND (Contratos.FecCon_Con >= @FecIniCon_sp  AND Contratos.FecCon_Con <= @FecFinCon_sp)
   AND ((Contratos.ObsCon_Con  LIKE '%' + @ObsCon_sp + '%') OR (Contratos.ObsCon_Con IS NULL))
   )
 AND
    (    ((Clientes.TipVia_Cli  LIKE '%' + @TipVia_sp + '%')
      AND (Clientes.DirCli_Cli  LIKE '%' + @DirCli_sp + '%') 
      AND ((TelefonosCli.NumTel_telCli LIKE '%' + @NumTel_sp + '%') OR(TelefonosCli.NumTel_telCli IS NULL))
    )
 AND
    (Comerciales.NomCom_Com LIKE '%' + @NomCom_sp + '%')
)

ORDER BY Contratos.TipCon_Con
GO


:confused:

Emilio 16-11-2005 11:05:51

Si aplicas algún que otro max a tu consulta seguramente te de el resultado deseado.

Código SQL [-]
SELECT max(tabla1.campo1), max(tabla1.campo2), tabla2.campo1
FROM tabla1 LEFT OUTER JOIN tabla2 bla bla bla... 
...
...

Por cierto, me parece muy bien que seas del Barça, yo soy del Madrid, y me parece muy bonito tu avatar, el mio también reflefa mis aficiones, pero joer, hasta en los comentarios vaís a meternos el catalán? ;)

Nelet 16-11-2005 11:41:31

Según deduzco del comentario del SP quieres relacionar todos los contratos con los comerciales y los clientes y de los clientes los quieres todos tengan teléfono o no. No entiendo entonces los LEFT OUTER JOIN a las tablas de clientes y comerciales.

Solución propuesta:
Código SQL [-]
SELECT 
   Contratos.*
FROM
   Contratos

   /* Unisc per C.Aj amb Clientes i estos amb TelefonosCli*/
   INNER JOIN
   Clientes LEFT OUTER JOIN TelefonosCli     
                         ON TelefonosCli.CodCli_TelCli = Clientes.CodAut_Cli
   ON Clientes.CodAut_Cli = Contratos.CodCli_Con

   /* Unisc amb Comerciales */
   INNER JOIN Comerciales
      ON Comerciales.CodAut_Com = Contratos.CodCom_Con      

WHERE  
( 
   (   (Contratos.NumCon_Con LIKE '%' + @NumCon_sp + '%' )
   AND (Contratos.FecCon_Con >= @FecIniCon_sp  AND Contratos.FecCon_Con <= @FecFinCon_sp)
   AND ((Contratos.ObsCon_Con  LIKE '%' + @ObsCon_sp + '%') OR (Contratos.ObsCon_Con IS NULL))
   )
 AND
    (    ((Clientes.TipVia_Cli  LIKE '%' + @TipVia_sp + '%')
      AND (Clientes.DirCli_Cli  LIKE '%' + @DirCli_sp + '%') 
      AND ((TelefonosCli.NumTel_telCli LIKE '%' + @NumTel_sp + '%') OR(TelefonosCli.NumTel_telCli IS NULL))
    )
 AND
    (Comerciales.NomCom_Com LIKE '%' + @NomCom_sp + '%')
)

ORDER BY Contratos.TipCon_Con

Así te trae todos los contratos relacionados con los comerciales y con los clientes que tengan o no teléfono.

Por cierto....los dos lo tenéis mal....Amunt Valencia!..

Cabanyaler 16-11-2005 12:10:09

Gracias por vuestra ayuda.

Cita:

Si aplicas algún que otro max a tu consulta seguramente te de el resultado deseado.
Voy a probar lo del max.

Cita:

No entiendo entonces los LEFT OUTER JOIN a las tablas de clientes y comerciales.
Porque la consulta está resumida, y también necesito consultar o filtrar por campos de estas tablas relacionadas con la maestra CONTRATOS.

Cita:

Hasta en los comentarios vaís a meternos el catalán?
Es un Copy/Paste de mi código, por eso están en mi lengua.

Ah!!, y suerte para todos, pero al final ganará el mejor, y mientras disfrutad del espectáculo. ;)

Nelet 16-11-2005 12:29:16

Y si pruebas con subconsultas?

Mas lento pero mas fácil

Cabanyaler 16-11-2005 12:42:47

Con subconsultas, si, pero es que tengo alrededor de 50 Store procedures que debería reescribirlos todos, por lo que soy capaz de inventar algo.

Por otro lado, como pongo el MAX en esta consulta??, ya que me da error de sintaxis si hago esto
Código SQL [-]
SELECT 
   MAX(Contratos.CodAut_Con),Contratos.*
FROM
   Contratos......

Muchas gracias.

Nelet 16-11-2005 12:52:14

Para poner el max de un campo debes agrupar por todos los demás campos que queden fuera del agregado. Por ejemplo si para la misma combinación de CAMPO1...CAMPON tienes un CAMPOX y quieres saber el máximo, quedaría así:

Código SQL [-]
SELECT CAMPO1, CAMPO2, ...CAMPON, MAX(CAMPOX)
FROM TABLA
WHERE ......BLABLABLA
GROUP BY CAMPO1, CAMPO2,....CAMPON

Por lo que veo de tu procedimiento.....creo que es bastante dificil implementarlo con agregados. Te sigo recomendando las subconsultas.

Emilio 16-11-2005 13:18:54

Cita:

Empezado por Cabanyaler
Por otro lado, como pongo el MAX en esta consulta??, ya que me da error de sintaxis si hago esto
Código SQL [-]
SELECT 
MAX(Contratos.CodAut_Con),Contratos.*
FROM
Contratos......

Lógico, en la select o le dices campo a campo los que quieres o le pones un asterisco para todos, pero nunca ambas cosas.

Por otra parte decir que hacer uso de max, no te obliga hacer un group by por lo menos en DB2 que es la que yo uso, bueno sería que nos dijeses también el motor que estás usando.

En este hilo por lo visto hay uno de cada equipo, en cualquier caso el sábado seguro que disfrutamos todos.

Nelet 16-11-2005 15:15:44

Cita:

Empezado por Emilio
Lógico, en la select o le dices campo a campo los que quieres o le pones un asterisco para todos, pero nunca ambas cosas.

Por otra parte decir que hacer uso de max, no te obliga hacer un group by por lo menos en DB2 que es la que yo uso, bueno sería que nos dijeses también el motor que estás usando.

En este hilo por lo visto hay uno de cada equipo, en cualquier caso el sábado seguro que disfrutamos todos.

En SQLServer si puedes poner lo siguiente
Código SQL [-]
SELECT CAMPO1, CAMPO3, * FROM TABLA

Lo digo por curiosidad, como curioso me resulta no tener que poner el cognazo de GROUP BY

Emilio 16-11-2005 16:44:22

Cita:

Empezado por Nelet
En SQLServer si puedes poner lo siguiente
Código SQL [-]
SELECT CAMPO1, CAMPO3, * FROM TABLA

Todo un puntazo para SQLServer

Cita:

Lo digo por curiosidad, como curioso me resulta no tener que poner el cognazo de GROUP BY
Pues la verdad es que no soy amante del GROUP BY precisamente tiende a duplicar cuando enlazas varias tablas si no andas con cuidado.

Eso demuestra lo distintos que son los motores y lo mucho que en mi caso (DB2) hecho de menos el LIMIT de MySQL o el ROWS de otros motores, snifff.

Cabanyaler 16-11-2005 18:56:34

Gracias a todos.
Creo que ya lo tengo, lo que ocurre es que debo irme ya, pero mañana acabaré de compilar y arreglar la consulta ya que lleva un campo Memo y no se puede agrupar como sabréis.
Pero me está dando una risa de no poder aguantarme al tener que hacer el GROUP BY por todos los campos.... ufff, y la verdad no es que tenga cuatro.

Al final lo he ehecho así
Código SQL [-]
SELECT CAMPO1, CAMPO2, ...CAMPON, MAX(CAMPOX)
FROM TABLA
WHERE ......BLABLABLA
GROUP BY CAMPO1, CAMPO2,....CAMPON
:)
Mañana comento el resultado.

Cabanyaler 17-11-2005 08:46:23

Ya está, por lo visto el resultado es el deseado con la anterior solución, pero como no puedo prescindir del campo Memo que debo incluir incluso para que cumpla condición WHERE, he tenido que decantarme por la solución anunciada al principio del Hilo.
He de añadir, que esta misma solución creo que hubiese sido equivalente a incluir una clausula DISCTINT, pero por el mismo motivo del campo Memo no se tomó en su momento dicha solución.
Cita:

He buscado como eliminar una vez realizada la consulta incluyendo duplicados, dichas tuplas duplicadas, ya que en la consulta incluyo la clave principal de los CONTRATOS, pero no puedo realizar esta limpieza de ese modo ya que no quiero eliminar nada en el servidor, simplemente los duplicados en la consulta en el lado cliente y si ejecuto un p.e. ConsultaConDuplicados.delete y posteriormente por cuestiones de operatividad del programa se realiza un ConsultaConDuplicados.UpdateBatch quizá elimine algo por algún efecto lateral.
No obstante muchísimas gracias a todos.
;)


La franja horaria es GMT +2. Ahora son las 11:11:08.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi