Qual é a melhor maneira de entrar na mesma mesa duas vezes?


108

Isso é um pouco complicado, mas tenho 2 tabelas. Digamos que a estrutura seja mais ou menos assim:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

As tabelas podem ser unidas com base em Table1.PhoneNumber1 -> Table2.PhoneNumber ou Tabela1.PhoneNumber2 -> Table2.PhoneNumber.

Agora, quero obter um conjunto de resultados que contém PhoneNumber1, SomeOtherField que corresponda a PhoneNumber1, PhoneNumber2 e SomeOtherField que corresponda a PhoneNumber2.

Pensei em 2 maneiras de fazer isso - juntando-se à mesa duas vezes ou juntando-se uma vez com OR na 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

Isso parece funcionar.

Método 2 :

Para de alguma forma ter uma consulta que se parece um pouco com isto -

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

Ainda não fiz isso funcionar e não tenho certeza se há uma maneira de fazer isso.

Qual é a melhor maneira de fazer isso? Nenhuma das formas parece simples ou intuitiva ... Existe uma maneira mais direta de fazer isso? Como esse requisito é geralmente implementado?

Respostas:


151

Primeiro, eu tentaria refatorar essas tabelas para evitar o uso de números de telefone como chaves naturais. Não sou um fã de chaves naturais e este é um grande exemplo do porquê. As teclas naturais, especialmente coisas como números de telefone, podem mudar com frequência. Atualizar seu banco de dados quando essa mudança acontecer será uma enorme dor de cabeça sujeita a erros. *

O método 1, conforme você descreve, é sua melhor aposta. Parece um pouco conciso devido ao esquema de nomenclatura e aos apelidos curtos, mas ... o apelido é seu amigo quando se trata de juntar-se à mesma tabela várias vezes ou usar subconsultas etc.

Eu apenas limparia as coisas um pouco:

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

O que eu fiz:

  • Não há necessidade de especificar INNER - está implícito no fato de que você não especifica LEFT ou RIGHT
  • Não sufixe com n sua tabela de pesquisa primária
  • N-sufixo os apelidos da tabela que você usará várias vezes para tornar óbvio

* Uma maneira dos DBAs evitarem as dores de cabeça de atualizar as chaves naturais é não especificar as chaves primárias e as restrições de chave estrangeira, o que piora ainda mais os problemas com um design de banco de dados ruim. Na verdade, eu já vi isso com mais frequência do que não.


Acabei de usar essa solução para meu próprio problema. Ele ajudou muito. No entanto, antes de ver isso, apliquei Chaves primárias e Chaves estrangeiras onde quer que pudesse ver as tabelas que precisavam ser unidas umas às outras. Por que isso é uma má ideia?
volume um de

6
@volumeone - acho que você entendeu mal a última parte da minha resposta. As chaves primárias e estrangeiras são uma boa ideia. Evitá-los é uma prática ruim, um design ruim e simplesmente ruim.
Paul Sasik

Perfeito ... mas por que apelidos são obrigatórios nessa situação?
Raiden Core

Existe uma maneira de fazer isso sem entrar na mesma mesa duas vezes? Talvez use uma condição na cláusula where ...
JohnOsborne

5

O primeiro é bom, a menos que Phone1 ou (mais provavelmente) phone2 possa ser nulo. Nesse caso, você deseja usar uma junção à esquerda em vez de uma junção interna.

Geralmente é um mau sinal quando você tem uma mesa com dois campos de número de telefone. Normalmente, isso significa que o design do seu banco de dados é falho.


Ótimo ponto! Isso teria me causado grandes dores de cabeça mais tarde ... obrigado!
froadie

4

Você pode usar UNIONpara combinar duas junções:

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

1
Pensei nisso, mas preciso que seja retornado como um único registro desnormalizado ...
froadie

Ok, presumi praticamente o oposto. Se for esse o caso, eu faria isso usando algo como seu primeiro método. Vou editar minha resposta.
Pointy

3

Meu problema era exibir o registro mesmo que não houvesse ou existisse apenas um número de telefone (catálogo de endereços completo). Portanto, usei um LEFT JOIN que pega todos os registros da esquerda, mesmo que nenhum correspondente exista à direita. Para mim, isso funciona no Microsoft Access SQL (eles exigem o parêntese!)

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;

2

O primeiro método é a abordagem adequada e fará o que você precisa. No entanto, com as junções internas, você só selecionará as linhas de Table1se ambos os números de telefone existirem em Table2. Você pode querer fazer um LEFT JOINpara que todas as linhas de Table1sejam selecionadas. Se os números de telefone não corresponderem, o SomeOtherFields será nulo. Se você quiser ter certeza de que tem pelo menos um número de telefone correspondente, você pode fazerWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

O segundo método pode ter um problema: o que acontece se Table2tiver PhoneNumber1e PhoneNumber2? Qual linha será selecionada? Dependendo de seus dados, chaves estrangeiras, etc., isso pode ou não ser um problema.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.