SQL - Buscar registros con dos campos coincidentes en la misma tabla (SQL)

 
Vista:
Imágen de perfil de Javier
Val: 7
Ha aumentado su posición en 7 puestos en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por Javier (3 intervenciones) el 16/05/2018 13:01:43
Buenos días, después de revisar el foro no he encontrado solución a un problema que me parecía sencillo. Seguro que alguien puede hacerme ver la luz...

Necesito seleccionar los registros de una única tabla 'Trabajos' que hayan realizado un tipo de trabajo concreto en dos periodos de tiempo diferentes: Por ejemplo ¿Qué clientes han vacunado a su mascota el año 2016 y también el año 2017? Necesito obtener los clientes que han vacunado los dos años.

La tabla Trabajos utiliza para este fin los campos IdCliente, IdTipoTrabajo y FechaTrabajo

El tipo de trabajo se debe obtener de una segunda tabla 'Tareas' relacionada con la tabla 'Trabajos' mediante el campo común 'IdTipoTrabajo' ( en concreto los trabajos se definen con el campo Tareas.IdListado)

Los intervalos de fecha no son anuales, sino definidos mediante las variables tipo fecha: Fecha1, Fecha2 y Fecha3

He probado de mil maneras y siempre obtengo algún error...

RegistroSelect = "SELECT DISTINCT Trabajos.IdCliente" & _
"FROM ( " & _
" SELECT Trabajos.IdCliente" & _
"FROM Trabajos INNER JOIN Tareas ON Trabajos.IdTipoTrabajo = Tareas.IdTipoTrabajo " & _
"WHERE ( ((Tareas.IdListado)= 6 ) AND ((Trabajos.FechaTrabajo) BETWEEN # " & Fecha2 & " # AND # " & Fecha3 & " # ) ) " & _
" ) AS T1 " & _
"INNER JOIN ( " & _
"SELECT Trabajos.IdCliente" & _
"FROM Trabajos INNER JOIN Tareas ON Trabajos.IdTipoTrabajo = Tareas.IdTipoTrabajo " & _
"WHERE ( ((Tareas.IdListado)= 6 ) AND ((Trabajos.FechaTrabajo) BETWEEN # " & Fecha1 & " # AND # " & Fecha2 & " # ) ) " & _
" ) AS T2 " & _
"ON T1.IdCliente= T2.IdCliente"

En este código me da el error: el campo Trabajos.IdCliente puede que haga referencia a más de una tabla de las especificadas en la clausula FROM

¿Alguna idea?

Gracias a todos por adelantado
Valora esta pregunta
Me gusta: Está pregunta es útil y esta claraNo me gusta: Está pregunta no esta clara o no es útil
0
Responder
sin imagen de perfil
Val: 22
Ha aumentado su posición en 3 puestos en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por Ana (11 intervenciones) el 16/05/2018 15:30:33
Hola Javier,

No entiendo muy bien porque utilizas la misma select.

Si lo que buscas solo son años, prueba a poner:

1
2
3
4
5
6
7
8
9
SELECT Trabajos.IdCliente
FROM Trabajos INNER JOIN Tareas ON Trabajos.IdTipoTrabajo = Tareas.IdTipoTrabajo
WHERE ((Tareas.IdListado)= 6 ) AND (Trabajos.FechaTrabajo = YEAR(Fecha2) AND Trabajos.FechaTrabajo = YEAR(Fecha3))
 
UNION
 
SELECT Trabajos.IdCliente
FROM Trabajos INNER JOIN Tareas ON Trabajos.IdTipoTrabajo = Tareas.IdTipoTrabajo
WHERE ((Tareas.IdListado)= 6 ) AND (Trabajos.FechaTrabajo = YEAR(Fecha1) AND Trabajos.FechaTrabajo = YEAR(Fecha2))

Solo coges el año de la fecha que te dan.

No se si lo que buscas es eso.

Sino es así, pon algún dato de las tablas.

Un saludo
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por Javier (1 intervención) el 16/05/2018 16:37:02
Hola Ana, gracias por tu ayuda.

Se trata de realizar un análisis por años, pero no naturales, por eso utilizo Fecha1, Fecha2 y Fecha3 que son variables previamente calculadas en base a una fecha de referencia que puede ser cualquiera.

Este código forma parte de un programa de análisis de fidelidad de clientes en una clínica veterinaria.

Por ejemplo si el usuario desea analizar las vacunaciones realizadas en base a la fecha 01/05/2018 los cálculos se realizarán para saber qué clientes han vacunado entre el Fecha1 = 01/05/2016 y Fecha2 = 01/05/2017 (un año) y también han vacunado entre el Fecha2 = 01/05/2017 y el Fecha3 = 01/05/2018 (otro año). Por eso utilizo BETWEEN y no YEAR

Ya he probado usando UNION y no obtuve los resultados que deseaba, pues la cifra de clientes que vacunaban los dos años era superior a la de uno cualquiera de los años individualmente, creo que se suman los resultados de cada SELECT (aunque sin repetirse los clientes de cada año). Yo estoy buscando la intersección de los clientes comunes que vacunan cada año.

Por ejemplo:

El año Fecha1 - Fecha 2 vacunan 456 clientes
El año Fecha2 - Fecha 3 vacunan 345 clientes

Los clientes que vacunan los dos años serían como máximo 345 (y usando UNION obtengo resultados superiores a 456)

Necesitaría la manera de implementar un operador tipo 'INTERSECTION' y no encuentro la manera, ya he probado usando IN, EXITS... pero no lo consigo.

Espero haberme explicado un poco mejor
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil
Val: 806
Bronce
Ha mantenido su posición en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por leonardo_josue (1173 intervenciones) el 16/05/2018 17:45:20
Hola Javier:

No nos dices con qué BD's estás trabajando, pero te voy a plantear un ejemplo en MySQL para ver si puedes implementarlo en tu DBMS.

Supongamos que tenemos esta tabla:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT * FROM tabla;
+---------+------------+
| cliente | anio       |
+---------+------------+
| uno     | 2016-01-01 |
| uno     | 2017-01-01 |
| dos     | 2016-01-01 |
| dos     | 2016-01-01 |
| tres    | 2016-01-01 |
| cuatro  | 2018-01-01 |
+---------+------------+
6 rows in set (0.00 sec)

de esta tabla te interesan los clientes que tienen un registro tanto en el anio 2016 como en el año 2017... Con los datos de ejemplo SÓLO EL CLIENTE UNO cumple con la condición ya que el cliente dos tiene DOS REGISTROS, pero ambos son del año 2016, el cliente tres tiene sólo un registro en el año 2016 y el cliente cuatro no tienen ningún registro para los años que te interesan. Entonces, para lo que quieres, lo puedes hacer de varias formas:

1. AGRUPANDO POR AÑOS DISTINTOS. en primer lugar filtras sólo los años que te interesan aplicando un DISTINTC, ya de esta manera sólo cuentas una cita por año y filtras aquellos años que no te interesan:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT DISTINCT cliente, YEAR(ANIO) anio
    -> FROM tabla
    -> WHERE YEAR(ANIO) in (2016, 2017);
+---------+------+
| cliente | anio |
+---------+------+
| uno     | 2016 |
| uno     | 2017 |
| dos     | 2016 |
| tres    | 2016 |
+---------+------+
4 rows in set (0.06 sec)

Observa que el cliente cuatro YA NO APARECE y que el cliente DOS sólo aparece una vez, ya que ambas "citas" fueron el mismo año... sobre esta tabla simplemente AGRUPAS por CLIENTE y cuentas cuántos registros tiene cada uno:

1
2
3
4
5
6
7
8
9
10
11
12
mysql> SELECT cliente, COUNT(DISTINCT anio) total
    -> FROM tabla
    -> WHERE YEAR(anio) in (2016, 2017)
    -> GROUP BY cliente;
+---------+-------+
| cliente | total |
+---------+-------+
| uno     |     2 |
| dos     |     1 |
| tres    |     1 |
+---------+-------+
3 rows in set (0.06 sec)

Finalmente, puedes filtrar con HAVING aquellos registros que cumplan con ambos años:

1
2
3
4
5
6
7
8
9
10
11
mysql> SELECT cliente, COUNT(DISTINCT anio) total
    -> FROM tabla
    -> WHERE YEAR(ANIO) in (2016, 2017)
    -> GROUP BY cliente
    -> HAVING COUNT(DISTINCT anio) = 2;
+---------+-------+
| cliente | total |
+---------+-------+
| uno     |     2 |
+---------+-------+
1 row in set (0.00 sec)

2. CON SUBCONSULTAS: En este caso puedes usar la condición EXISTS para verificar que existan registros para el cliente para ambos años:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mysql> SELECT DISTINCT cliente
    -> FROM tabla T1
    -> WHERE
    -> EXISTS ( SELECT cliente
    ->          FROM tabla T2
    ->          WHERE T1.cliente = T2.cliente
    ->          AND YEAR(T2.anio) = 2016)
    -> AND
    -> EXISTS ( SELECT cliente
    ->          FROM tabla T2
    ->          WHERE T1.cliente = T2.cliente
    ->          AND YEAR(T2.anio) = 2017);
+---------+
| cliente |
+---------+
| uno     |
+---------+
1 row in set (0.04 sec)

En tu caso, en lugar de poner una condición como YEAR(ANIO), puedes seguir utilizando la lógica con el BETWEEN, el chiste es que simplemente cumplan con ambas condiciones.

Trata de aplicar esto al DBMS con el que estás trabajando y nos comentas.

Saludos
Leo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
2
Comentar
Imágen de perfil de Javier
Val: 7
Ha aumentado su posición en 7 puestos en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por Javier (3 intervenciones) el 17/05/2018 11:32:26
Buenos días y mil gracias Leo

Acabo de probar la segunda de tus opciones (con subconsultas) y funciona perfectamente.

El procedimiento es para usarlo en una BD Access 2016, utiliza Microsoft Access SQL y creo que una de las diferencias con ANSI SQL es que no permite referencias de la función de agregado DISTINCT, no creo que pueda ejecutar tu primera opción por incluir COUNT(DISTINCT anio) en el código.


Desgraciadamente las tablas que utilizo son demasiado grandes, la principal pasa de los 100.000 registros, y el proceso es demasiado lento, tengo que buscar otras opciones. Probaré con tu primera opción agrupando por años distintos (por si funciona COUNT(DISTINCT anio) aunque no tengo muy claro como adaptar las fechas.


Si no funciona recurriré a la creación de dos tablas temporales tmpT1 para un año y tmpT2 para el otro año, luego seleccionaré las coincidencias entre ellas. El proceso para una año solamente dura segundos. supongo que funcionará.


Gracias de nuevo y si se te ocurre alguna solución más elegante bienvenida será.

Javier
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar
sin imagen de perfil
Val: 806
Bronce
Ha mantenido su posición en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por leonardo_josue (1173 intervenciones) el 17/05/2018 22:41:18
Hola de nuevo Javier:

Puedes "Simular" el COUNT(DISTINCT) utilizando también una subconsulta, sería más o menos así:

1
2
3
4
5
6
7
SELECT cliente, count(anio) FROM
( SELECT distinct tabla.[cliente], YEAR(tabla.[anio]) as anio
  FROM tabla
  WHERE YEAR(tabla.[anio]) IN (2016, 2017)
) as T
GROUP BY T.cliente
HAVING count(anio) = 2;

Esto es equivalente a lo que puse con MySQL, pero utilizando solo SQL Estandar. Haz la prueba para ver si tienes un mejor rendimiento.

ahora bien, si la consulta sigue siendo muy lenta, puede ser que las otras opciones que planteas sean mejores.

Saludos
Leo.
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
2
Comentar
Imágen de perfil de Javier
Val: 7
Ha aumentado su posición en 7 puestos en SQL (en relación al último mes)
Gráfica de SQL

Buscar registros con dos campos coincidentes en la misma tabla (SQL)

Publicado por Javier (3 intervenciones) el 19/05/2018 12:17:49
Buenos días de nuevo Leo

El código 'adaptado' funciona correctamente y con la misma eficiencia que usando dos tablas temporales



He tenido que cambiar la clausula DISTINCT, ponerla en el SELECT inicial y no en el FROM (esto ya me había pasado anteriormente, no sé por qué pero de la otra forma solamente selecciona un registro)


Queda así:


1
2
3
4
5
6
7
8
RegistroSelect = "SELECT DISTINCT IdCliente, COUNT(Año) " & _
"FROM ( " & _
"SELECT Trabajos.IdCliente, YEAR(Trabajos.FechaTrabajo) as Anio " & _
"FROM Trabajos INNER JOIN Tareas ON Trabajos.IdTarea = Tareas.IdTarea " & _
"WHERE (  ((Tareas.IdTipoTarea)= 6 ) AND YEAR(Trabajos.FechaTrabajo) IN (2016, 2017) ) " & _
") AS T " & _
"GROUP BY T.IdCliente" & _
"HAVING COUNT(Anio ) = 2 "

Ahora estoy buscando la manera de usar los intervalos anuales basados en una fecha cualquiera:

Calculo las fechas Fecha1, Fecha2 en base a FechaReferencia

Año1 = entre Fecha1 y FechaReferencia
Año2 = entre Fecha2 y Fecha1

El problema es que no se puede usar IN para esos periodos anuales, o no se me ocurre la manera. Tampoco serviría directamente la actual clausula HAVING, probaré con EXISTS pero a lo mejor vuelve a ser un proceso lento.

Ya te contaré, gracias de nuevo

Javier
Valora esta respuesta
Me gusta: Está respuesta es útil y esta claraNo me gusta: Está respuesta no esta clara o no es útil
0
Comentar