2009-06-19 3 views
721

Для простоты предположим, что все соответствующие поля не являются NULL.INNER JOIN ON vs WHERE статья

Вы можете сделать:

SELECT 
    table1.this, table2.that, table2.somethingelse 
FROM 
    table1, table2 
WHERE 
    table1.foreignkey = table2.primarykey 
    AND (some other conditions) 

Или еще:

SELECT 
    table1.this, table2.that, table2.somethingelse 
FROM 
    table1 INNER JOIN table2 
    ON table1.foreignkey = table2.primarykey 
WHERE 
    (some other conditions) 

ли эти две работы по тому же пути в MySQL?

+2

@bendin: FYI ссылка Предоставлена сломана! –

+0

@Marco: [здесь это] (http://www.se-radio.net/2009/06/episode-137-sql-with-jim-melton/) –

+0

Возможный дубликат [SQL left join vs multiple tables на строке FROM?] (http://stackoverflow.com/questions/894490/sql-left-join-vs-multiple-tables-on-from-line) –

ответ

591

INNER JOIN - синтаксис ANSI, который вы должны использовать.

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

Его также можно легко заменить OUTER JOIN всякий раз, когда возникнет такая необходимость.

WHERE синтаксис более реляционная модель ориентирована.

Результат из двух таблиц JOIN ed является декартовым произведением таблиц, к которым применяется фильтр, который выбирает только те строки с совпадающими столбцами.

Проще видеть это с помощью синтаксиса WHERE.

Что касается вашего примера, в MySQL (и в SQL вообще) эти два запроса являются синонимами.

Также обратите внимание, что MySQL также содержит предложение STRAIGHT_JOIN.

Используя этот раздел, вы можете управлять заказом JOIN: какая таблица сканируется во внешнем цикле и какая из них находится во внутреннем цикле.

Вы не можете контролировать это в MySQL, используя синтаксис WHERE.

+3

Спасибо, Кваснуи. У вас много деталей в ваших анах; справедливо ли говорить, что «да, эти запросы эквивалентны, но вы должны использовать внутреннее соединение, потому что оно более читаемо и проще изменить»? – allyourcode

+4

@allyourcode: для 'Oracle',' SQL Server', 'MySQL' и' PostgreSQL' - да. Для других систем, вероятно, тоже, но лучше проверить. – Quassnoi

+9

FWIW, используя запятые с условиями соединения в предложении WHERE, также находится в стандарте ANSI. –

24

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

Использование явного соединения (ваш второй пример) гораздо читабельнее и прост в обслуживании.

+38

Я не мог больше не согласиться. Синтаксис JOIN чрезвычайно многословен и сложен для организации. У меня есть много запросов, соединяющих 5, 10 и даже 15 таблиц с помощью предложений WHERE clause, и они прекрасно читаемы. Перезапись такого запроса с использованием синтаксиса JOIN приводит к искажению беспорядка. Это просто показывает, что нет правильного ответа на этот вопрос, и это зависит от того, с чем вам удобно. –

+27

Ной, я думаю, ты можешь быть здесь в меньшинстве. –

+2

Я получаю +1 к мату и Ною. Мне нравится разнообразие :). Я вижу, откуда приходит Ной; внутреннее соединение не добавляет ничего нового в язык и, безусловно, более многословно. С другой стороны, это может сделать ваше условие «где» намного короче, что обычно означает, что его легче читать. – allyourcode

10

У них есть другое понятное для человека значение.

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

Вы всегда должны указывать код для чтения.

То есть, если это встроенные отношения, используйте явное соединение. если вы сопоставляете слабосвязанные данные, используйте предложение where.

54

Неявный синтаксис ANSI старше, менее очевидный и не рекомендуется.

Кроме того, реляционная алгебра позволяет взаимозаменяемость сказуемого в предложении WHERE и INNER JOIN, поэтому даже INNER JOIN запросов с WHERE пунктов могут иметь предикаты rearrranged оптимизатора.

Я рекомендую вам написать запросы наиболее читаемым способом.

Иногда это относится к тому, чтобы сделать INNER JOIN относительно «неполным» и поставить некоторые из критериев в WHERE просто, чтобы сделать списки критериев фильтрации более удобными для обслуживания.

Например, вместо:

SELECT * 
FROM Customers c 
INNER JOIN CustomerAccounts ca 
    ON ca.CustomerID = c.CustomerID 
    AND c.State = 'NY' 
INNER JOIN Accounts a 
    ON ca.AccountID = a.AccountID 
    AND a.Status = 1 

Написать:

SELECT * 
FROM Customers c 
INNER JOIN CustomerAccounts ca 
    ON ca.CustomerID = c.CustomerID 
INNER JOIN Accounts a 
    ON ca.AccountID = a.AccountID 
WHERE c.State = 'NY' 
    AND a.Status = 1 

Но это зависит, конечно.

+12

Ваш первый фрагмент определенно больно больно моему мозгу. Кто-нибудь на самом деле это делает? Если я встречу кого-то, кто это сделает, нормально ли мне бить его по голове? – allyourcode

+3

Я определяю критерии, в которых это имеет смысл. Если я присоединяюсь к временной согласованной таблице поиска моментальных снимков (и у меня нет представления или UDF, который обеспечивает выбор допустимой даты), я буду включать дату вступления в действие, а не в WHERE, потому что это меньше вероятно, случайно удалятся. –

+12

@allyourcode: хотя редко бывает видно, что этот тип синтаксиса соединения в INNER JOINs, он довольно распространен для RIGHT JOINs и LEFT JOINS - указание более подробных сведений в предикате соединения устраняет необходимость в подзапросе и предотвращает непреднамеренное вторжение внешних соединений превратился в INNER JOINs. (Хотя я согласен, что для INNER JOINs я почти всегда ставил c.State = 'NY' в предложение WHERE) –

136

Другие указали, что INNER JOIN помогает человеку читать, и это главный приоритет; Согласен. Позвольте мне попытаться объяснить , почему синтаксис соединения более читабельен.

Основной ВЫБРАТЬ запрос следующим образом:

SELECT stuff 
FROM tables 
WHERE conditions 

SELECT, пункт говорит нам, что мы получаем обратно; предложение FROM сообщает нам , где мы получаем его, а предложение WHERE сообщает нам , которые мы получаем.

JOIN - это утверждение о таблицах, как они связаны вместе (концептуально, фактически, в одну таблицу). Любые элементы запроса, которые управляют таблицами - откуда мы получаем материал - семантически, относятся к предложению FROM (и, конечно же, здесь идут элементы JOIN). Ввод соединительных элементов в предложение WHERE объединяет , который и где-от; поэтому предпочтительным является синтаксис JOIN.

+6

Спасибо за разъяснение, почему внутреннее соединение предпочтительнее Carl. Я думаю, что ваш ans был скрыт в других, но явный обычно лучше (да, я фанат Python). – allyourcode

+2

Семантика ON и WHERE означает, что для JOINs после последнего ВНЕШНЕГО СОЕДИНЕНИЯ * это не имеет значения *, который вы используете. Хотя вы охарактеризовываете ON как часть JOIN, это * также * фильтрация после декартового произведения. * Оба * ON и WHERE фильтруют декартово произведение. Но либо ON, либо подзаголовок с WHERE должны использоваться * до * последнего ВНЕШНЕГО СОЕДИНЕНИЯ. (JOINs не входят в «пары столбцов»). Любые две таблицы могут быть подключены к любому условию. Это всего лишь способ интерпретировать JOINs ON на равенстве столбцов.) – philipxy

19

Я также укажу, что использование более старого синтаксиса более подвержено ошибкам. Если вы используете внутренние соединения без предложения ON, вы получите синтаксическую ошибку. Если вы используете старый синтаксис и забыли одно из условий соединения в предложении where, вы получите кросс-соединение. Разработчики часто исправляют это, добавляя отдельное ключевое слово (вместо того, чтобы фиксировать соединение, потому что они до сих пор не понимают, что само соединение не работает), что может показаться, что вылечило проблему, но значительно замедлит запрос.

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

Позвольте мне указать вам на этот вопрос, чтобы понять, почему неявный синтаксис является плохим, если вы используете левые соединения. Sybase *= to Ansi Standard with 2 different outer tables for same inner table

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

+2

@HLGEM: Хотя я полностью согласен с тем, что явные JOINs лучше, это случаи, когда вам просто нужно использовать старый синтаксис. Пример в реальном мире: ANSI JOIN попал в Oracle только в версии 9i, выпущенной в 2001 году, и только год назад (16 лет с момента публикации стандарта) мне пришлось поддерживать кучу установки 8i, для которой мы для выпуска критических обновлений. Я не хотел поддерживать два набора обновлений, поэтому мы разработали и протестировали обновления для всех баз данных, включая 8i, а это значит, что мы не смогли использовать ANSI JOIN. – Quassnoi

+0

+1 Интересный момент, когда вы указываете, что синтакс без INNER JOIN более подвержен ошибкам. Я смущен вашим последним предложением, когда вы говорите: «... стандарту, использующему явные объединения, 17 лет». так вы предлагаете использовать ключевое слово INNER JOIN или нет? –

+1

@Marco Demaio, да всегда используйте INNER JOIN или JOIN (эти два одинаковые) или LEFT JOIN или RIGHT JOIN или CROSS JOIN и никогда не используйте неявные комбинации запятой. – HLGEM

1

ANSI join синтаксис определенно более портативный.

Я собираюсь обновить Microsoft SQL Server, и я бы также упомянул, что синтаксис = * и * = для внешних соединений в SQL Server не поддерживается (без режима совместимости) для SQL Server 2005 года и позже.

+2

Даже в SQL Server 2000 * = и = * могут давать неправильные результаты и никогда не должны использоваться. – HLGEM

+2

'* =' и '= *' никогда не были ANSI и никогда не были хорошей нотацией. Вот почему ON был необходим - для OUTER JOINs в отсутствие подзапросов (которые были добавлены одновременно, поэтому они фактически не нужны в CROSS & INNER JOINs.) – philipxy

4

Я знаю, что вы говорите о MySQL, но в любом случае: В Oracle 9 явные объединения и неявные объединения будут генерировать различные планы выполнения. AFAIK, который был решен в Oracle 10+: больше нет такой разницы.

10

Стандарт SQL: 2003 изменил некоторые правила приоритета, поэтому оператор JOIN имеет преимущество перед соединением «запятая». Это может фактически изменить результаты вашего запроса в зависимости от того, как он настроен. Это вызывает некоторые проблемы для некоторых людей, когда MySQL 5.0.12 переключился на соблюдение стандарта.

Так что в вашем примере ваши запросы будут работать одинаково. Но если вы добавили третью таблицу: SELECT ... FROM table1, table2 JOIN table3 ON ... WHERE ...

До MySQL 5.0.12 сначала будут объединены table1 и table2, а затем table3. Теперь (5.0.12 и далее), table2 и table3 соединяются сначала, а затем table1. Это не всегда меняет результаты, но может, и вы даже не можете этого понять.

Я больше не использую синтаксис «запятая», предпочитая ваш второй пример. В любом случае, это намного более читаемо, условия JOIN с JOINs, а не разделенные на отдельный раздел запроса.

104

Применение условных операторов в положение ON/WHERE

Здесь я рассказал о логических шагов обработки запросов.


Ссылка: Внутрь Microsoft® SQL Server ™ 2005 T-SQL Запросы
Издатель: Microsoft Press
Pub Дата: 7 марта 2006
Печать ISBN-10: 0-7356-2313-9
Печать ISBN-13: 978-0-7356-2313-2
Страницы: 640

Inside Microsoft® SQL Server™ 2005 T-SQL Querying

(8) SELECT (9) DISTINCT (11) TOP <top_specification> <select_list> 
(1) FROM <left_table> 
(3)  <join_type> JOIN <right_table> 
(2)  ON <join_condition> 
(4) WHERE <where_condition> 
(5) GROUP BY <group_by_list> 
(6) WITH {CUBE | ROLLUP} 
(7) HAVING <having_condition> 
(10) ORDER BY <order_by_list> 

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

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

Краткое описание логических Этапы обработки запроса

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

  1. ОТ: декартово произведение (перекрестное соединение) выполняются между первыми двумя столами в ЕКЕ, и, как следствие, виртуальная таблица VT1 генерируется.

  2. ON: Фильтр ON применяется к VT1. Только строки, для которых <join_condition> TRUE, вставлены в VT2.

  3. OUTER (join): Если указан OUTER JOIN (в отличие от CROSS JOIN или INNER JOIN), строки из сохраненной таблицы или таблиц, для которых совпадение не было найдено, добавляются в строки из VT2 как внешние строки, генерирующие VT3. Если в предложении FROM указано более двух таблиц, шаги 1 - 3 применяются повторно между результатом последнего соединения и следующей таблицей в предложении FROM до тех пор, пока не будут обработаны все таблицы.

  4. ГДЕ: Фильтр WHERE применяется к VT3. Только строки, для которых значение <where_condition> TRUE, вставляются в VT4.

  5. GROUP BY: Строки из VT4 расположены группами в зависимости от списка столбцов, указанного в предложении GROUP BY. VT5 генерируется.

  6. CUBE | ROLLUP: Супергруппы (группы групп) добавляются к строкам из VT5, генерируя VT6.

  7. HAVING: Фильтр HAVING применяется к VT6. В VT7 вставляются только группы, для которых значение <having_condition> TRUE.

  8. SELECT: список SELECT обрабатывается, генерируя VT8.

  9. DISTINCT: Дублированные строки удаляются из VT8. Генерируется VT9.

  10. ORDER BY: Строки из VT9 сортируются в соответствии с списком столбцов, указанным в предложении ORDER BY. Создается курсор (VC10).

  11. TOP: указанное число или процент строк выбирается с начала VC10. Таблица VT11 генерируется и возвращается вызывающему абоненту.



Таким образом, (INNER JOIN) ПО будет фильтровать данные (данные счета ВТ будет снижена здесь сам по себе) перед применением ИНЕКЕ. Последующие условия соединения будут выполняться с отфильтрованными данными, что улучшает производительность. После этого условие WHERE будет применяться только к условиям фильтра.

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

+8

* «Поэтому (INNER JOIN) ON будет фильтровать данные (количество данных VT будет здесь уменьшено) перед применением предложения WHERE. * * Не обязательно. Статья посвящена логическому порядку обработки. Когда вы говорите, что конкретная реализация будет делать одну вещь перед другой, вы говорите о * реализованном порядке обработки. Реализациям разрешено делать любые оптимизации, которые им нравятся, до тех пор, пока результат будет таким же, как если бы реализация соответствовала логическому порядку. Джо Селко много написал об этом на Usenet. –

+0

@rafidheen "(INNER JOIN) ON будет фильтровать данные ... перед применением предложения WHERE ... что улучшает производительность". Хорошая точка зрения. «После этого условие WHERE будет применяться только к условиям фильтра». Как насчет предложения HAVING? – James

Смежные вопросы