¿Cuál es la mejor manera de unirse dos veces a la misma mesa?

108

Esto es un poco complicado, pero tengo 2 tablas. Digamos que la estructura es algo como esto:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Las tablas se pueden unir basándose en Table1.PhoneNumber1 -> Table2.PhoneNumber, o Table1.PhoneNumber2 -> Table2.PhoneNumber.

Ahora, quiero obtener un conjunto de resultados que contenga PhoneNumber1, SomeOtherField que corresponda a PhoneNumber1, PhoneNumber2 y SomeOtherField que corresponda a PhoneNumber2.

Pensé en 2 formas de hacer esto: uniéndome a la mesa dos veces o uniéndome una vez con un OR en la cláusula ON.

Método 1 :

SELECT t1.PhoneNumber1, t1.PhoneNumber2, 
   t2.SomeOtherFieldForPhone1, t3.someOtherFieldForPhone2
FROM Table1 t1
INNER JOIN Table2 t2
   ON t2.PhoneNumber = t1.PhoneNumber1
INNER JOIN Table2 t3
   ON t3.PhoneNumber = t1.PhoneNumber2

Esto parece funcionar.

Método 2 :

Para tener de alguna manera una consulta que se parezca un poco a esto:

SELECT ...
FROM Table1
INNER JOIN Table2 
   ON Table1.PhoneNumber1 = Table2.PhoneNumber OR
      Table1.PhoneNumber2 = Table2.PhoneNumber

No he logrado que esto funcione todavía y no estoy seguro de si hay una manera de hacerlo.

¿Cuál es la mejor manera de lograrlo? Ninguna de las dos parece simple o intuitiva ... ¿Existe una forma más sencilla de hacer esto? ¿Cómo se implementa este requisito en general?

froadie
fuente

Respuestas:

151

Primero, trataría de refactorizar estas tablas para evitar el uso de números de teléfono como claves naturales. No soy un fanático de las teclas naturales y este es un gran ejemplo de por qué. Las teclas naturales, especialmente cosas como números de teléfono, pueden cambiar y con frecuencia. Actualizar su base de datos cuando ocurra ese cambio será un ENORME dolor de cabeza propenso a errores. *

Sin embargo, el método 1 como lo describe es su mejor opción. Parece un poco conciso debido al esquema de nombres y los alias cortos, pero ... el alias es su amigo cuando se trata de unirse a la misma mesa varias veces o usar subconsultas, etc.

Limpiaría un poco las cosas:

SELECT t.PhoneNumber1, t.PhoneNumber2, 
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2
FROM Table1 t
JOIN Table2 t1 ON t1.PhoneNumber = t.PhoneNumber1
JOIN Table2 t2 ON t2.PhoneNumber = t.PhoneNumber2

Lo que hice:

  • No es necesario especificar INNER; está implícito en el hecho de que no especifica LEFT o RIGHT
  • No agregue el sufijo n a su tabla de búsqueda principal
  • Sufijo con N los alias de la tabla que usará varias veces para que sea obvio

* Una forma en que los administradores de bases de datos evitan los dolores de cabeza de actualizar las claves naturales es no especificar claves primarias y restricciones de clave externa, lo que agrava aún más los problemas con un diseño de base de datos deficiente. De hecho, he visto esto la mayoría de las veces.

Paul Sasik
fuente
Acabo de usar esta solución para mi propio problema. Ayudó mucho. Sin embargo, antes de ver esto, apliqué las claves primarias y las claves externas siempre que pudiera ver que las tablas necesitaban unirse entre sí. ¿Por qué es esto una mala idea?
volumen uno
6
@volumeone: creo que puede haber entendido mal la última parte de mi respuesta. Las claves primarias y externas son una buena idea. Evitarlos es una mala práctica, un mal diseño y simplemente malo.
Paul Sasik
Perfecto ... pero ¿por qué los alias son imprescindibles en esta situación?
Raiden Core
¿Hay alguna forma de hacer esto sin unirse a la misma mesa dos veces? Tal vez use una condición en la cláusula where ...
JohnOsborne
5

La primera es buena a menos que Phone1 o (más probablemente) phone2 puedan ser nulos. En ese caso, desea utilizar una combinación izquierda en lugar de una combinación interna.

Por lo general, es una mala señal tener una tabla con dos campos de números de teléfono. Por lo general, esto significa que el diseño de su base de datos es defectuoso.

HLGEM
fuente
¡Gran punto! Esto me habría causado grandes dolores de cabeza más tarde ... ¡gracias!
froadie
4

Podría usar UNIONpara combinar dos combinaciones:

SELECT Table1.PhoneNumber1 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber1 = Table2.PhoneNumber
 UNION
SELECT Table1.PhoneNumber2 as PhoneNumber, Table2.SomeOtherField as OtherField
  FROM Table1
  JOIN Table2
    ON Table1.PhoneNumber2 = Table2.PhoneNumber
Puntiagudo
fuente
1
Pensé en esto, pero necesito que se devuelva como un solo registro desnormalizado ...
froadie
Oh, está bien, asumí todo lo contrario. Si ese es el caso, lo haría usando algo como su primer método. Editaré mi respuesta.
Puntiagudo
3

Mi problema fue mostrar el registro incluso si no existe o solo existe un número de teléfono (libreta de direcciones completa). Por lo tanto, utilicé un LEFT JOIN que toma todos los registros de la izquierda, incluso si no existe ningún correspondiente a la derecha. Para mí, esto funciona en Microsoft Access SQL (¡requieren el paréntesis!)

SELECT t.PhoneNumber1, t.PhoneNumber2, t.PhoneNumber3
   t1.SomeOtherFieldForPhone1, t2.someOtherFieldForPhone2, t3.someOtherFieldForPhone3
FROM 
(
 (
  Table1 AS t LEFT JOIN Table2 AS t3 ON t.PhoneNumber3 = t3.PhoneNumber
 )
 LEFT JOIN Table2 AS t2 ON t.PhoneNumber2 = t2.PhoneNumber
)
LEFT JOIN Table2 AS t1 ON t.PhoneNumber1 = t1.PhoneNumber;
F1iX
fuente
2

El primer método es el enfoque adecuado y hará lo que necesite. Sin embargo, con las combinaciones internas, solo seleccionará filas de Table1si ambos números de teléfono existen en Table2. Es posible que desee hacer una LEFT JOINpara que Table1se seleccionen todas las filas de . Si los números de teléfono no coinciden, la SomeOtherFields sería nula. Si desea asegurarse de tener al menos un número de teléfono coincidente, puede hacerloWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

El segundo método podría tener un problema: ¿qué pasa si Table2tiene ambos PhoneNumber1y PhoneNumber2? ¿Qué fila se seleccionará? Dependiendo de sus datos, claves externas, etc., esto puede ser un problema o no.

Nelson Rothermel
fuente