Приєднання - для ледачих людей?


169

Нещодавно у мене була дискусія з іншим розробником, який стверджував, що JOINs (SQL) марний. Це технічно вірно, але він додав, що використання приєднань є менш ефективним, ніж створення декількох запитів та таблиць посилань у коді (C # або Java).

Для нього приєднання - для ледачих людей, які не дбають про продуктивність. Це правда? Чи слід уникати використання з'єднань?


114
Ні. Бази даних оптимізовані для з'єднання, вони надзвичайно швидкі, особливо для великих наборів даних. Ви не хочете, щоб ваша програма завантажувала десятки тисяч рядків і об’єднувала їх разом вручну.
полудан

91
Мови програмування призначені для ледачих людей; вони менш ефективні, ніж кодування інструкцій процесора вручну. :)
Майкл МакГоуан

76
Як називається розробник? Я хочу переконатися, що я його ніколи не наймаю.
Джо

39
@Michael meh, реальні програмісти використовують метеликів ...
Marc Gravell

14
Повторіть своє "це правда" - ні, це не так. Бази даних працюють за допомогою теорії множин; приєднання на наборах працюють дуже красиво та корисно ...
Марк Гравелл

Відповіді:


188

Ні, нам слід уникати розробників, які дотримуються такої неймовірно неправильної думки.

У багатьох випадках з'єднання з базою даних на кілька порядків швидше, ніж все, що робиться через клієнта, оскільки це дозволяє уникнути туди переходу БД, а БД може використовувати індекси для виконання з'єднання.

Вгорі голови я навіть не можу уявити єдиного сценарію, коли правильно використане з'єднання було б повільніше, ніж еквівалентна операція на стороні клієнта.

Редагувати: Є деякі рідкісні випадки, коли користувацький код клієнта може робити ефекти ефективніше, ніж прямий приєднання БД (див. Коментар meriton). Але це дуже великий виняток.


1
А як щодо тристоронніх приєднань? Чи не буває випадків, коли вам краще було б робити їх "за кодом"?
julien_c

56
Приєднання до сервера додатків може бути більш ефективним, якщо приєднання до бази даних викликає сильне надмірність набору результатів, що надсилається по мережі. Розглянемо таблиці A і B, де кожен рядок у A асоціюється з 20 рядками в B, B має лише 100 рядків, і ми хочемо отримати перші 1000 рядків з A з пов'язаними рядками з B. Приєднання до бази даних призведе до 20 * 1000 кортежів, що надсилаються по мережі. Якщо об'єднання здійснюється на сервері додатків (спочатку отримуючи всю таблицю B в пам'ять), по мережі надсилається всього 100 + 1000 рядків.
meriton

7
Однак ви, безумовно, вірні в тому, що приєднання до бази даних в більшості випадків відбувається набагато швидше, а тому не лише питання зручності, а необхідності.
meriton

13
Мені пощастило поговорити з деякими розробниками, які працюють на SQL Server у Microsoft. Це дозволить вам запаморочити слух оптимізацій, які вони роблять за запитами. Кожен, хто думає, що вони розумніші, ніж це, треба хитрити.
riwalk

2
@meriton Я трохи здивований; Я очікую, що клієнтська бібліотека оптимізує перехресні з'єднання.
Філ Лелло

83

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

Однак реляційна база даних сильно оптимізована для роботи з наборами. Існує багато, багато способів запиту даних на основі приєднань, які набагато ефективніші, ніж безліч туди-назад. Звідси походить універсальність rdbms. Цього можна досягти і в магазині nosql, але ви часто в кінцевому підсумку будуєте окрему структуру, підходящу для кожного різного характеру запиту.

Коротше кажучи: я не згоден. У RDBMS приєднання є принциповими . Якщо ви не використовуєте їх, ви не використовуєте його як RDBMS.


46

Що ж, він помиляється в загальному випадку.

Бази даних здатні оптимізуватись за допомогою різноманітних методів, що допомагають підказками оптимізатора, індексами таблиць, зовнішніми ключовими зв’язками та, можливо, іншою інформацією щодо постачальника баз даних.


1
Я повинен визнати, коли я почав працювати з базами даних, я вважав, що можу перемогти продуктивність приєднання. Але це не зайняло багато часу, щоб зрозуміти, наскільки надзвичайно швидкі приєднання здійснюються БД. Насправді я б сказав, що в цій ситуації краще обговорити це з працівником відкрито, а не звільняти його як ідіот.
LegendLength

1
@LegendLength Я б сказав, що це навіть правда, якщо вони не такі розумні. Не потрібно припускати розумності, оскільки вони роблять ті самі помилки, що ми пам’ятаємо, що ми робимо (насправді, для мене це може означати, що вони не такі розумні…) Простіше: рідко допомагає бути зневажливим. Добре помилятися, раз у раз!
sehe

24

Ні, ти не повинен.

Бази даних спеціально розроблені для управління наборами даних (очевидно ....). Тому вони неймовірно ефективні в цьому. Роблячи те, що по суті є ручним приєднанням до власного коду, він намагається взяти на себе роль чогось, спеціально призначеного для роботи. Шанси його коду коли-небудь настільки ефективні, як у базі даних, дуже віддалені.

Як убік, без приєднання, який сенс у використанні бази даних? він також може просто використовувати текстові файли.


2
Навіть без приєднань? Автоматичне відображення в пам'яті, автоматичне кешування запитів, безліч інших автоматичних речей, які взагалі не трапляються у більшості файлових систем. О, я згадав про тонко контрольовані транзакції?
Пісквор вийшов з будинку

19

Якщо "ледачий" визначений як люди, які хочуть писати менше коду, то я згоден. Якщо "ледачий" визначається як люди, які хочуть мати інструменти, роблять те, що їм добре робити, я згоден. Тож якщо він просто погоджується з Ларрі Уоллом (щодо атрибутів хороших програмістів), то я згоден з ним.


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

3
@Dran Dane: Приєднання - для ледачих людей, так. Те, що вони, ймовірно, добре спрацюють, є ортогональним.
Пісквор вийшов з будівлі

16

Ummm, приєднується, як реляційні бази даних співвідносять таблиці між собою. Я не впевнений, у що він потрапляє.

Як зробити кілька дзвінків до бази даних більш ефективним, ніж один дзвінок? Плюс двигуни sql оптимізовані при виконанні подібних дій.

Можливо, ваш колега лінивий вивчати SQL.


12

Так, слід.

І вам слід використовувати C ++ замість C # через продуктивність. C # призначений для ледачих людей.

Ні-ні-ні. Ви повинні використовувати C замість C ++ через продуктивність. C ++ призначений для ледачих людей.

Ні-ні-ні. Ви повинні використовувати збірку замість C через продуктивність. C - для ледачих людей.

Так, я жартую. ви можете робити швидші програми без приєднання, і ви можете робити програми, використовуючи менше пам'яті без об'єднань. Але у багатьох випадках ваш час розробки важливіший, ніж час і пам'ять процесора. Відмовтеся від невеликої продуктивності та насолоджуйтесь своїм життям. Не витрачайте час на незначні показники. І скажи йому: "Чому ти не зробиш пряму дорогу зі свого місця до свого офісу?"


1
Я переглянув усі ваші відповіді до цих пір, і вони дуже смішні. Будь ласка, продовжуйте їх приходити. Або це, або де я можу підписатися на ваш блог?
Геррі

11

"Це технічно правда" - аналогічно, база даних SQL марна: який сенс використовувати один, коли ви можете отримати той самий результат, використовуючи купу файлів CSV та співвідносивши їх у коді? Чорт, будь-яка абстракція призначена для ледачих людей, повернемося до програмування в машинному коді прямо на апараті! ;)

Крім того, його твердження є неправдивим у всіх, крім найбільш суперечливих випадках: RDBMS сильно оптимізовані для швидкого приєднання . Системи управління реляційними базами даних, правда?


2
+1 Фраза "... технічно правдива" спрацювала б краще, якби ОП використовувала слово раніше, unnecessaryніж uselessу попередньому реченні. Сказати, що приєднання є марними, це явно неправда, оскільки технічні засоби не потребують розгляду. У будь-якому випадку, ФП - х і нерозуміння колеги з точки РСУБД є sandly не рідкість: stackoverflow.com/q/5575682/47550
Пол Sasik

7

Остання компанія, над якою я працював, також не використовувала приєднання SQL. Натомість вони перемістили цю роботу до рівня додатків, який призначений для масштабування по горизонталі. Обґрунтуванням цієї конструкції є уникнення роботи на рівні бази даних. Зазвичай база даних стає вузьким місцем. Його легше копіювати прикладний рівень, ніж база даних. Можуть бути й інші причини. Але це те, про що я можу зараз згадати.

Так, я погоджуюся, що приєднання, здійснені на рівні додатків, неефективні порівняно з об'єднаннями, виконаними в базі даних. Більше мережевого спілкування також.

Зауважте, що я не займаю твердої позиції щодо уникнення приєднання до SQL.


Ну, це звучить як раціональний аргумент проти JOIN у вашому конкретному випадку. Я пам’ятаю, що FB Engineering розмістило щось подібне у своєму блозі - масштабування також було їх ключовим пріоритетом. На жаль, лише невеликий% програмістів коли-небудь знадобиться це зробити, але багато хто думає, що це робиться, "оскільки OMG Facebook теж робить це";)
Пісквор вийшов з будівлі

Гаразд, у рішенні підприємства, де у вас достатньо трафіку для перевантаження сервера баз даних, це, можливо, варто врахувати, але, швидше за все, це процедура звітування, що зберігається, або запланована резервна копія, яка прибиває результативність. Бази даних добре поєднуються, особливо якщо є невдачі на допомогу
Jodrell

@Jodrell: Так, вони добре поєднуються; знову ж, є кутові випадки, коли вам потрібно скинути елегантність приєднання, щоб отримати більше сил. Я зустрів одну з таких ситуацій; ми намагалися будь-яке можливе рішення, і справді рішення про неприєднання було найшвидшим у цій дуже конкретній ситуації . І ні, на цьому конкретному сервері взагалі нічого іншого не було; збережені процедури не можуть сповільнити вас, якщо у вас немає;)
Piskvor вийшов з будинку

5

Без приєднання, як ви збираєтеся співвідносити елементи замовлення з замовленнями? У цьому вся суть реляційної системи управління базами даних. Без приєднання немає реляційних даних, і ви можете також використовувати текстові файли для обробки даних.

Здається, він не розуміє поняття, тому намагається зробити так, що вони здаються марними. Він той самий тип людини, який вважає, що excel є додатком до бази даних. Поцупи його по-дурному і скажи йому більше читати про бази даних. Здійснення декількох з'єднань, витягування даних та об'єднання даних через C # - це неправильний спосіб робити.


5

Я не розумію логіку твердження "приєднання до SQL марні". Чи корисно фільтрувати та обмежувати дані, перш ніж працювати над ними? Як ви інші респонденти заявили, що це те, що роблять двигуни бази даних, це має бути в чому вони хороші.

Можливо, ледачий програміст буде дотримуватися технологій, з якими вони були знайомі, і уникав інших можливостей з не технічних причин.

Я залишаю це вам вирішити.


5

Розглянемо приклад: таблиця із записами рахунків-фактур та пов’язана таблиця із записами позиції рахунка-фактури. Розглянемо псевдо код клієнта:

for each (invoice in invoices)
    let invoiceLines = FindLinesFor(invoice)
...

Якщо у вас є 100 000 рахунків-фактур з 10 рядками кожен, цей код знайде 10 рядків рахунків-фактур із таблиці в 1 мільйон, і це зробить це 100 000 разів. Зі збільшенням розміру таблиці збільшується кількість вибраних операцій, а вартість кожної вибраної операції збільшується.

Комп'ютери Becase швидко, ви можете не помітити різниці в продуктивності між двома підходами, якщо у вас є кілька тисяч записів або менше. Оскільки збільшення витрат є більш ніж лінійним, оскільки кількість записів збільшується (скажімо мільйони, скажімо), ви почнете помічати різницю, і різниця стане менш терпимою, оскільки розмір набору даних збільшуватиметься.

Однак приєднання. використовуватиме індекси таблиці та об'єднає два набори даних. Це означає, що ви ефективно скануєте другу таблицю один раз, а не випадково отримуєте доступ до неї N разів. Якщо визначений зовнішній ключ, у базі даних вже є зв’язки між відповідними записами, що зберігаються всередині країни.

Уявіть, що ви робите це самостійно. У вас є алфавітний список учнів та зошит із усіма звітами учнів про оцінку (одна сторінка на клас). Зошит сортується впорядковано за іменами учнів у тому ж порядку, що і список. Як би ви хотіли продовжити?

  1. Прочитайте ім’я зі списку.
  2. Відкрийте зошит.
  3. Знайдіть ім’я учня.
  4. Прочитайте оцінки учнів, перегортаючи сторінки, поки не досягнете наступного учня чи останньої сторінки.
  5. Закрийте зошит.
  6. Повторіть.

Або:

  1. Відкрийте зошит на першій сторінці.
  2. Прочитайте ім’я зі списку.
  3. Прочитайте будь-які оцінки цього імені з зошита.
  4. Повторіть кроки 2-3, поки не досягнете кінця
  5. Закрийте зошит.

5

Звучить класичний випадок " Я можу це краще написати ". Іншими словами, він бачить щось, що він сприймає як вид болю в шиї (написання купи приєднань до SQL) і каже: "Я впевнений, що можу написати це краще і отримаю кращу продуктивність". Ви повинні запитати, чи він а) розумніший і б) більш освічений, ніж типова людина, котра по колу в коді оптимізації Oracle або SQL Server. Шанси, що його немає.


3

Він, безумовно, помиляється. Хоча існують певні плюси в маніпулюванні даними на таких мовах, як C # або Java, приєднання є найшвидшим у базі даних через характер самого SQL.

SQL веде детальну інформацію про дані, і якщо ви правильно створили свої індекси, можна дуже швидко знайти один запис на пару мільйонів. Окрім того, що навіщо потрібно перетягувати всі свої дані в C #, щоб зробити приєднання, коли ви просто можете це зробити прямо на рівні бази даних?

Плюси використання C # вступають у гру, коли вам потрібно щось робити ітеративно. Якщо вам потрібно виконати якусь функцію для кожного рядка, швидше за все, це зробити швидше всередині C #, інакше приєднання даних оптимізується в БД.


3

Я скажу, що я зіткнувся з випадком, коли швидше було порушено запит і виконано з'єднання в коді. Це було зроблено лише з однією конкретною версією MySQL. Все інше, база даних, ймовірно, буде швидшою (зауважте, що вам, можливо, доведеться оптимізувати запити, але все одно буде швидше).


3

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


3
Більшість двигунів бази даних все одно зроблять це за вами; і, наприклад, в MySQL ви можете створити таблицю ( MEMORYдвижок), що суто в пам'яті . Повторна реалізація функціональності бази даних без бази даних зазвичай є ознакою важкого випадку НІГ;)
Пісквор покинув будівлю

@phoog: Тут не придумано - іншими словами, "я не думав про це, тому його не існує". Через це було винайдено багато квадратних коліс. (і так, інколи корисно переосмислити колесо, наприклад, якщо ви робите гоночні машини; повторно винайдіть "просто тому, що" навряд чи вийде вам краще колесо)
Пісквор покинув будівлю

Іншими словами, "я не зробив це так, це мусить бути сміттям". У цьому є зерно істини лише настільки, що "я не перевіряв її, щоб вона не підходила для моїх цілей", тому протестуйте її, перш ніж судити про це.
Пітер Лоурі

@Piskvor: Не обов’язково база даних може використовувати лише пам'ять системи, на якій вона працює, тоді як програма може використовувати пам'ять сервера додатків. Інакше кажучи: якщо база даних знаходиться на спеціалізованому хості, для доступу до цього кешу все ще потрібна пропускна здатність мережі та підлягає затримка в мережі, але будь-який кеш, який зберігає додаток, можна запитувати зі швидкістю низькою затримкою доступу до пам'яті.
meriton

2

Ні, не тільки приєднання краще оптимізовані в коді бази даних, який є спеціальним C # / Java; але зазвичай можна застосувати кілька методів фільтрації, що дає ще кращі показники.


2

Він помиляється, приєднується те, чим користуються компетентні програмісти. Можливо, є кілька обмежених випадків, коли запропонований ним метод є більш ефективним (і в тому, напевне, я б використовував базу даних Documant), але я не можу його побачити, якщо у вас є обманний обсяг даних. Наприклад, візьміть цей запит:

select t1.field1 
from table1 t1
join table2 t2 
    on t1.id = t2.id
where t1.field2 = 'test'

Припустимо, у вас є 10 мільйонів записів у table1 та 1 мільйон записів у table2. Припустимо, що 9 мільйонів записів у таблиці 1 відповідають умові де. Припустимо, що лише 15 з них є в таблиці2. Ви можете запустити цю операцію sql, яка, якщо її правильно індексувати, займе мілісекунди і поверне 15 записів у мережі лише з 1 стовпцем даних. Або ви можете надіслати десять мільйонів записів з 2 стовпцями даних і окремо надсилати ще 1 мільйон записів з одним стовпцем даних по всій мережі та комбінувати їх на веб-сервері.

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


2

Цей аргумент я чув досить часто під час своєї кар'єри як розробника програмного забезпечення. Практично кожен раз, коли це було заявлено, хлопець, який заявляв претензію, не мав багато знань про реляційні системи баз даних, те, як вони працюють та спосіб використання таких систем.

Так, при неправильному використанні з'єднання здаються марними або навіть небезпечними. Але при правильному використанні існує великий потенціал для впровадження бази даних, щоб зробити оптимізацію та "допомогти" розробникові отримати правильний результат найбільш ефективно.

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

Тож відповідь однозначно: Ні, JOINSзовсім не марні!


0

Це "технічно правдиво" лише в одному випадку, який не використовується часто в додатках (коли всі рядки всіх таблиць у з'єднанні (запитах) повертаються запитом). У більшості запитів повертається лише частина рядків кожної таблиці. Двигун бази даних часто використовує індекси для усунення небажаних рядків, іноді навіть без зчитування фактичного рядка, оскільки він може використовувати значення, збережені в індексах. Сам движок бази даних написаний на C, C ++ тощо і є принаймні настільки ж ефективним, як код, написаний розробником.


0

Якщо я серйозно не зрозумів, логіка у питанні дуже хибна

Якщо для кожного A є 20 рядків у B, 1000 рядків у A означає 20k рядків у B. Не може бути лише 100 рядків у B, якщо не існує багато-багато таблиць "AB" з 20k рядками, що містять відображення .

Отож, щоб отримати всю інформацію про те, які 20 зі 100 рядків B відображатимуться в кожному рядку A, ви також таблицю AB. Так це було б або:

  • 3 набори результатів з 100, 1000 та 20k рядків та клієнт ПРИЄДНАЙТЕСЬ
  • єдиний набір приєднаних результатів A-AB-B з 20k рядками

Тож "ПРИЄДНАЙТЕСЬ" у клієнта дійсно додає значення при вивченні даних. Не те, що це не погана ідея. Якщо я витягував один об’єкт із бази даних, ніж, можливо, є більш сенс розбити його на окремі набори результатів. Для дзвінка типу звіту я майже завжди вирівнював його в один.

У будь-якому випадку, я б сказав, що майже немає користі для перехресного з'єднання такої величини. Це поганий приклад.

Вам потрібно десь приєднатися, і саме в цьому RDBMS хороші. Я не хотів би працювати з жодною мавпою з кодом клієнта, яка вважає, що може зробити краще.

Задумливість:

Для приєднання до клієнта потрібні стійкі об'єкти, такі як DataTables (в .net). Якщо у вас є один сплющений набір результатів, його можна споживати через щось легше, як DataReader. Високий обсяг = багато клієнтських ресурсів, які використовуються для уникнення БД ПРИЄДНАЙТЕСЬ.

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