Який найкращий спосіб приєднатися до одного столу двічі?


108

Це трохи складніше, але у мене є 2 таблиці. Скажімо, структура виглядає приблизно так:

*Table1*
ID
PhoneNumber1
PhoneNumber2

*Table2*
PhoneNumber
SomeOtherField

Таблиці можна з'єднати на основі Table1.PhoneNumber1 -> Table2.PhoneNumber або Table1.PhoneNumber2 -> Table2.PhoneNumber.

Тепер я хочу отримати набір результатів, що містить PhoneNumber1, SomeOtherField, що відповідає PhoneNumber1, PhoneNumber2, і SomeOtherField, що відповідає PhoneNumber2.

Я подумав два способи зробити це - або приєднавшись до столу двічі, або з'єднавшись один раз із АБО у пункті 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

Що я зробив:

  • Вказувати ІННЕР не потрібно - це мається на увазі через те, що ви не вказуєте Ліворуч або Право
  • Не n-суфікс вашої основної таблиці пошуку
  • N-Суфіксуйте псевдоніми таблиці, які ви будете використовувати кілька разів, щоб зробити це очевидним

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


Я просто використав це рішення для власної проблеми. Це дуже допомогло. Однак перед тим, як побачити це, я застосував Первинні та Іноземні Ключі там, де я міг бачити таблиці, які потребують приєднання один до одного. Чому це погана ідея?
том 1

6
@volumeone - Я думаю, ви, можливо, неправильно зрозуміли останню частину моєї відповіді. Первинний та зовнішній ключі - хороша ідея. Уникати їх - це погана практика, поганий дизайн і просто поганий.
Пол Сасик

Ідеально .. але чому псевдоніми в цій ситуації обов'язкові?
Raiden Core

Чи є спосіб це зробити, не приєднуючись до тієї ж таблиці двічі? Можливо, використовуйте умову в пункті де ...
JohnOsborne

5

Перше добре, якщо або Phone1, або (швидше за все, phone2) телефон може бути недійсним. У такому випадку ви хочете використовувати ліве з'єднання замість внутрішнього з'єднання.

Зазвичай це поганий знак, коли у вас є таблиця з двома полями телефонних номерів. Зазвичай це означає, що дизайн вашої бази даних є помилковим.


Чудова точка! Це спричинило б мені великі головні болі пізніше ... дякую!
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;

2

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

Другий метод може мати проблему: що станеться, якщо Table2є і те PhoneNumber1і PhoneNumber2? Який рядок буде обраний? Залежно від ваших даних, сторонніх ключів тощо, це може бути або не бути проблемою.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.