Присоединения для ленивых людей?


169

Недавно у меня была дискуссия с другим разработчиком, который заявил мне, что JOIN (SQL) бесполезны. Технически это верно, но он добавил, что использование объединений менее эффективно, чем выполнение нескольких запросов и таблиц ссылок в коде (C # или Java).

Для него объединения - для ленивых людей, которым нет дела до производительности. Это правда? Должны ли мы избегать использования объединений?


114
Нет. Базы данных оптимизированы для выполнения объединений, они чрезвычайно быстры, особенно для больших наборов данных. Вы не хотите, чтобы ваше приложение загружало десятки тысяч строк и объединяло их вместе вручную.
Халфдан

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

76
Как зовут разработчика? Я хочу убедиться, что никогда не найму его.
Джо

39
@ Майкл Мех, настоящие программисты используют бабочек ...
Марк Грэвелл

14
Re ваше «это правда» - нет, это не так. Базы данных работают через теорию множеств; соединения на съемочной площадке работают очень хорошо и с пользой ...
Марк Гравелл

Ответы:


188

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

Во многих случаях соединение с базой данных на несколько порядков быстрее, чем все, что выполняется через клиента, поскольку оно избегает циклических обращений к БД, и БД может использовать индексы для выполнения соединения.

Вдобавок ко всему, я даже не могу представить ни одного сценария, в котором правильно использованное соединение будет медленнее, чем эквивалентная операция на стороне клиента.

Редактировать: Есть несколько редких случаев, когда пользовательский код клиента может делать вещи более эффективно, чем простое соединение с БД (см. Комментарий meriton). Но это очень большое исключение.


1
А как насчет трехсторонних соединений? Разве не было случаев, когда вам было бы лучше делать их «в коде»?
julien_c

56
Присоединение к серверу приложений может быть более эффективным, если присоединение к базе данных вызывает серьезную избыточность в наборе результатов, отправляемом по сети. Рассмотрим таблицы A и B, где каждая строка в A связана с 20 строками в B, B имеет только 100 строк, и мы хотим извлечь первые 1000 строк из A со связанными строками из B. В результате объединения в базу данных будет получено 20 * 1000 кортежей отправлено по сети. Если соединение выполняется на сервере приложений (сначала извлекается вся таблица B в память), по сети отправляется всего 100 + 1000 строк.
меритон

7
Тем не менее, вы, безусловно, правы в том, что объединения в базе данных в большинстве случаев выполняются намного быстрее, и, следовательно, это не просто вопрос удобства, а необходимость.
меритон

13
Мне посчастливилось поговорить с некоторыми разработчиками, работающими над SQL Server в Microsoft. Вы почувствуете головокружение, услышав оптимизацию по запросам. Любого, кто думает, что они умнее этого, нужно шлепнуть.
riwalk

2
@meriton Я немного удивлен; Я ожидаю, что клиентская библиотека оптимизирует перекрестные соединения.
Фил Лелло

83

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

Однако реляционная база данных сильно оптимизирована для работы с наборами. Существует множество способов запроса данных, основанных на объединениях, которые гораздо более эффективны, чем множество циклов. Отсюда и универсальность rdbms. Вы можете достичь того же самого в магазине nosql, но вы часто заканчиваете тем, что строили отдельную структуру, подходящую для каждой различной природы запроса.

Короче говоря: я не согласен. В РСУБД объединения являются фундаментальными . Если вы их не используете, вы не используете его как RDBMS.


46

Ну, он не прав в общем случае.

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


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

1
@ LegendLength Я бы сказал, что это даже правда, если они не такие умные. Не нужно полагаться на сообразительность, потому что они совершают те же ошибки, которые мы помним совершаем (на самом деле, для меня это может означать, что они не такие умные ...) Это проще: редко помогает быть пренебрежительным. Это нормально, быть неправым, время от времени!
сэх

24

Нет, ты не должен

Базы данных специально предназначены для манипулирования наборами данных (очевидно ....). Поэтому они невероятно эффективны в этом. Делая то, что по сути является ручным соединением в его собственном коде, он пытается взять на себя роль чего-то, специально предназначенного для этой работы. Вероятность того, что его код будет столь же эффективным, как и в базе данных, очень мала.

Кроме того, без объединений, какой смысл в использовании базы данных? он может также просто использовать текстовые файлы.


2
Даже без присоединений? Автоматическое отображение в памяти, автоматическое кэширование запросов, множество других автоматических вещей, которых вообще не происходит с большинством файловых систем. О, я упомянул точно контролируемые транзакции?
Писквор покинул здание

19

Если «ленивый» определяется как люди, которые хотят писать меньше кода, тогда я согласен. Если «ленивый» определяется как люди, которые хотят, чтобы инструменты делали то, что у них хорошо получается, я согласен. Так что, если он просто соглашается с Ларри Уоллом (в отношении атрибутов хороших программистов), то я согласен с ним.


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

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

16

Хм, соединения - это то, как реляционные базы данных связывают таблицы друг с другом. Я не уверен, к чему он клонит.

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

Возможно, ваш коллега слишком ленив для изучения SQL.


12

Да, ты должен.

И вы должны использовать C ++ вместо C # из-за производительности. C # для ленивых людей.

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

Нет нет нет. Вы должны использовать ассемблер вместо C из-за производительности. С для ленивых людей.

Да, я шучу. Вы можете создавать более быстрые программы без объединений и создавать программы, используя меньше памяти без объединений. НО во многих случаях ваше время разработки важнее, чем процессорное время и память. Откажитесь от небольшого выступления и наслаждайтесь жизнью. Не тратьте свое время на небольшую производительность. И скажи ему: «Почему ты не идешь по прямой дороге от своего места до офиса?»


1
Я посмотрел все ваши ответы до сих пор, и они очень смешные. Пожалуйста, продолжайте приходить. Или то, или где я могу подписаться на ваш блог?
Джерри

11

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

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


2
+1 Фраза «... технически верная» сработала бы лучше, если бы ОП употреблял слова unnecessaryскорее uselessв предыдущем предложении. Заявление о том, что объединения бесполезны, явно не соответствует действительности, не требуя рассмотрения технических деталей. В любом случае, неправильное понимание ОП и коллегой сути СУРБД не является чем-то необычным: stackoverflow.com/q/5575682/47550
Пол Сасик,

7

Последняя компания, в которой я работал, тоже не использовала SQL-соединения. Вместо этого они перенесли эту работу на прикладной уровень, который предназначен для горизонтального масштабирования. Обоснование такой конструкции - избегать работы на уровне базы данных. Обычно база данных становится узким местом. Легче копировать прикладной уровень, чем базу данных. Могут быть и другие причины. Но это тот, который я могу вспомнить сейчас.

Да, я согласен, что объединения, выполненные на уровне приложений, неэффективны по сравнению с объединениями, выполняемыми базой данных. Больше сетевого общения тоже.

Пожалуйста, обратите внимание, что я не стану твердо стоять на том, чтобы избегать соединений SQL.


Ну, это звучит как рациональный аргумент против JOINs в вашем конкретном случае. Я помню, что FB Engineering опубликовала нечто похожее в своем блоге - масштабирование также было их ключевым приоритетом. Увы, только небольшой процент программистов когда-либо должен будет делать это, но многие думают, что они делают «потому что OMG Facebook также делает это»;)
Писквор покинул здание

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

@Jodrell: Да, они хороши в объединениях; Опять же, есть угловые случаи, когда вам нужно отказаться от элегантности соединений, чтобы получить больше власти. Я встречал одну такую ​​ситуацию; Мы испробовали все возможные решения, и действительно, решение без присоединения было самым быстрым в этой очень специфической ситуации . И нет, на этом конкретном сервере больше ничего не работало; хранимые процедуры не могут замедлить вас, если у вас их нет;)
Писквор вышел из здания

5

Без объединений, как вы собираетесь связать элементы заказа с заказами? В этом весь смысл системы управления реляционными базами данных. Без объединений нет реляционных данных, и вы можете использовать текстовые файлы для обработки данных.

Похоже, он не понимает концепцию, поэтому он пытается заставить их казаться, что они бесполезны. Он такой же человек, который считает, что Excel - это приложение для работы с базами данных. Хлестать его глупо и сказать ему, чтобы прочитать больше о базах данных. Создание нескольких соединений и извлечение данных и объединение данных через C # - неправильный способ сделать что-то.


5

Я не понимаю логику утверждения "объединения в SQL бесполезны". Полезно ли фильтровать и ограничивать данные перед началом работы с ним? Поскольку вы, как и другие респонденты, заявили, что это то, что делают движки баз данных, это должно быть то, в чем они хороши.

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

Я оставляю вам решать.


5

Давайте рассмотрим пример: таблица с записями счетов-фактур и связанная таблица с записями отдельных позиций счетов-фактур. Рассмотрим псевдокод клиента:

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

Если у вас есть 100 000 счетов-фактур по 10 строк в каждой, этот код будет искать 10 строк счетов-фактур из таблицы 1 миллион, и он будет делать это 100 000 раз. По мере увеличения размера таблицы количество операций выбора увеличивается, а стоимость каждой операции выбора увеличивается.

Поскольку компьютеры работают быстро, вы можете не заметить разницу в производительности между двумя подходами, если у вас несколько тысяч записей или меньше. Поскольку увеличение стоимости является более чем линейным, так как количество записей увеличивается (например, до миллионов), вы начнете замечать разницу, и эта разница станет менее терпимой с ростом размера набора данных.

Соединение, однако. будет использовать индексы таблицы и объединить два набора данных. Это означает, что вы эффективно сканируете вторую таблицу один раз, а не получаете случайный доступ к ней 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

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


3
В любом случае большинство движков баз данных сделают это для вас за кулисами; и, например, в MySQL вы можете создать таблицу в памяти ( MEMORYдвижок). Повторное внедрение функциональности базы данных без базы данных обычно является признаком серьезного случая со стороны NIH;)
Писквор покинул здание

@phoog: не изобретено здесь - другими словами, «я не думал об этом, поэтому он не существует». Многие квадратные колеса были заново изобретены из-за этого. (и да, иногда полезно заново изобретать колесо, например, если вы создаете гоночные автомобили; изобретать «только потому, что» вряд ли
даст

Другими словами, «я не сделал это, так что это, должно быть, мусор». В этом есть доля правды только в том смысле, что «я не проверял его, поэтому он может не подходить для моих целей», поэтому проверьте его, прежде чем судить.
Питер Лоури,

@Piskvor: необязательно, база данных может использовать только память системы, в которой она работает, тогда как приложение может использовать память сервера приложений. Другими словами: если база данных находится на выделенном хосте, доступ к этому кешу все еще требует пропускной способности сети и подвержен задержке в сети, но любой кеш, который хранится в приложении, может запрашиваться со скоростью, низкой задержкой доступа к памяти.
меритон

2

Нет, соединения не только лучше оптимизированы в коде базы данных, чем ad-hoc C # / Java; но обычно можно применять несколько методов фильтрации, что дает еще лучшую производительность.


2

Он неправ, присоединения - это то, что используют компетентные программисты. Может быть несколько ограниченных случаев, когда предложенный им метод более эффективен (и я бы, вероятно, использовал базу данных Documant), но я не вижу его, если у вас есть какой-то заведомо недостающий объем данных. Например, возьмите этот запрос:

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

Предположим, у вас есть 10 миллионов записей в таблице 1 и 1 миллион записей в таблице 2. Предположим, что 9 миллионов записей в таблице 1 соответствуют предложению where. Предположим, что только 15 из них находятся в таблице2. Вы можете запустить этот оператор SQL, который при правильной индексации займет миллисекунды и вернет 15 записей по сети только с одним столбцом данных. Или вы можете отправить десять миллионов записей с двумя столбцами данных и отдельно отправить еще 1 миллион записей с одним столбцом данных по сети и объединить их на веб-сервере.

Или, конечно, вы всегда можете хранить все содержимое базы данных на веб-сервере, что просто глупо, если у вас есть более чем тривиальный объем данных и данных, которые постоянно меняются. Если вам не нужны качества реляционной базы данных, не используйте ее. Но если вы делаете, то используйте его правильно.


2

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

Да, при неправильном использовании объединения кажутся бесполезными или даже опасными. Но при правильном использовании для реализации базы данных существует большой потенциал для оптимизации и «помощи» разработчику, получающему правильный результат наиболее эффективно.

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

Таким образом, ответ определенно: нет, JOINSне бесполезны вообще!


0

Это «технически верно» только в одном случае, который не часто используется в приложениях (когда все строки всех таблиц в объединении (ах) возвращаются запросом). В большинстве запросов возвращается только часть строк каждой таблицы. Механизм базы данных часто использует индексы для удаления нежелательных строк, иногда даже без чтения фактической строки, поскольку он может использовать значения, хранящиеся в индексах. Сам механизм базы данных написан на C, C ++ и т. Д. И по крайней мере так же эффективен, как и код, написанный разработчиком.


0

Если я серьезно не понял, логика в вопросе очень ошибочна

Если для каждого A имеется 20 строк в B, то 1000 строк в A означают 20 000 строк в B. В B не может быть только 100 строк, если не существует таблицы много-много "AB" с 20 000 строк с отображением. ,

Таким образом, чтобы получить всю информацию о том, какие 20 из 100 строк B соответствуют каждой строке A, которую вы также включили в таблицу AB. Так что это будет либо:

  • 3 набора результатов по 100, 1000 и 20 тыс. Строк и клиентское соединение
  • один результирующий набор JOINed A-AB-B с 20 тыс. строк

Таким образом, «JOIN» в клиенте добавляет значение при проверке данных. Не то чтобы это не плохая идея. Если бы я извлекал один объект из базы данных, то, возможно, более разумно было бы разбить его на отдельные наборы результатов. Для вызова с типом отчета я бы почти всегда сводил его в один.

В любом случае, я бы сказал, что перекрестное соединение такого масштаба практически бесполезно. Это плохой пример.

Вы должны где-то ПРИСОЕДИНЯТЬСЯ, и в этом СУБД хороши. Я не хотел бы работать с любой обезьяной кода клиента, которая думает, что они могут добиться большего успеха.

Запоздалая мысль:

Для присоединения к клиенту требуются постоянные объекты, такие как DataTables (в .net). Если у вас есть один плоский набор результатов, его можно использовать с помощью чего-то более легкого, например DataReader. Большой объем = много клиентских ресурсов, используемых для обхода базы данных.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.