Как лучше всего сесть за один стол дважды?

108

Это немного сложно, но у меня есть 2 таблицы. Допустим, структура выглядит примерно так:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Таблицы можно объединять на основе Table1.PhoneNumber1 -> Table2.PhoneNumber или Table1.PhoneNumber2 -> Table2.PhoneNumber.

Теперь я хочу получить набор результатов, содержащий PhoneNumber1, SomeOtherField, который соответствует PhoneNumber1, PhoneNumber2 и SomeOtherField, который соответствует PhoneNumber2.

Я подумал о двух способах сделать это - либо дважды присоединиться к таблице, либо один раз присоединиться с помощью OR в предложении ON.

Способ 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

Кажется, это работает.

Способ 2 :

Чтобы каким-то образом получить запрос, который выглядит примерно так -

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

У меня это еще не работает, и я не уверен, есть ли способ сделать это.

Как лучше всего этого добиться? Ни один из способов не кажется простым или интуитивно понятным ... Есть ли более простой способ сделать это? Как обычно выполняется это требование?

фроди
источник

Ответы:

151

Во-первых, я бы попытался провести рефакторинг этих таблиц, чтобы не использовать телефонные номера в качестве естественных ключей. Я не поклонник естественных ключей, и это отличный пример того, почему. Естественные клавиши, особенно такие вещи, как номера телефонов, могут часто меняться. Обновление вашей базы данных, когда это изменение произойдет, будет ОГРОМНОЙ головной болью, подверженной ошибкам. *

Однако способ 1, как вы его описываете, - ваш лучший выбор. Это выглядит немного лаконично из-за схемы именования и коротких псевдонимов, но ... псевдонимы - ваш друг, когда дело доходит до объединения одной и той же таблицы несколько раз или использования подзапросов и т. Д.

Я бы просто немного поправил:

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

Что я сделал:

  • Не нужно указывать INNER - это подразумевается тем, что вы не указываете LEFT или RIGHT
  • Не добавляйте суффикс к основной таблице поиска
  • N-суффикс псевдонимов таблиц, которые вы будете использовать несколько раз, чтобы сделать это очевидным

* Один из способов, которым администраторы баз данных могут избежать головной боли при обновлении естественных ключей, - это не указывать первичные ключи и ограничения внешнего ключа, что еще больше усугубляет проблемы с плохим дизайном базы данных. На самом деле я видел это чаще, чем нет.

Пол Сасик
источник
Я только что использовал это решение для своей проблемы. Это очень помогло. Однако, прежде чем увидеть это, я применил первичные ключи и внешние ключи везде, где я мог видеть таблицы, которым необходимо соединяться друг с другом. Почему это плохая идея?
Том первый
6
@volumeone - я думаю, вы неправильно поняли последнюю часть моего ответа. Первичный и внешний ключи - хорошая идея. Избегать их - плохая практика, плохой дизайн и просто плохой.
Пол Сасик
Отлично .. но почему псевдонимы необходимы в этой ситуации?
Raiden Core
Есть ли способ сделать это, не присоединяясь к одной и той же таблице дважды? Может быть, использовать условие в предложении where ...
Джон Осборн
5

Первый хорош, если только Phone1 или (что более вероятно) phone2 не могут иметь значение null. В этом случае вы хотите использовать левое соединение вместо внутреннего соединения.

Когда у вас есть таблица с двумя полями телефонных номеров, это обычно плохой знак. Обычно это означает, что ваша база данных ошибочна.

HLGEM
источник
Отличный момент! Позже это вызвало бы у меня большие головные боли ... спасибо!
froadie
4

Вы можете использовать UNIONдля объединения двух соединений:

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
Я подумал об этом, но мне нужно, чтобы он был возвращен как единственная денормализованная запись ...
froadie
О, ладно, я предположил прямо противоположное. В таком случае я бы сделал это, используя что-то вроде вашего первого метода. Отредактирую свой ответ.
Pointy
3

Моя проблема заключалась в том, чтобы отобразить запись, даже если номер телефона отсутствует или существует только один (полная адресная книга). Поэтому я использовал LEFT JOIN, который берет все записи слева, даже если справа нет соответствующего. Для меня это работает в Microsoft Access SQL (они требуют скобки!)

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
источник
2

Первый метод - это правильный подход, и он сделает то, что вам нужно. Однако с внутренними объединениями вы будете выбирать строки только в том Table1случае, если оба телефонных номера существуют в Table2. Вы можете сделать LEFT JOINтак, чтобы Table1были выбраны все строки из . Если номера телефонов не совпадают, SomeOtherFields будет нулевым. Если вы хотите убедиться, что у вас есть хотя бы один подходящий номер телефона, вы можете сделатьWHERE t2.PhoneNumber IS NOT NULL OR t3.PhoneNumber IS NOT NULL

У второго метода может быть проблема: что произойдет, если Table2есть и PhoneNumber1и PhoneNumber2? Какая строка будет выбрана? В зависимости от ваших данных, внешних ключей и т. Д. Это может быть или не быть проблемой.

Нельсон Ротермель
источник