Программы
Вредные привычки в SQL: старомодные JOINы

Вредные привычки в SQL: старомодные JOINы

Как лучше писать много-табличные запросы: просто перечисляя таблицы в SELECT через запятую, или же использовать JOIN?

Перевод заметки Bad Habits to Kick : Using old-style JOINs.

Уверен, многие ветераны SQL знают, что есть способ лучше, чем соединять таблицы в стиле ANSI-89 (через запятую):

SELECT o.OrderID, od.ProductID
  FROM dbo.Orders AS o, dbo.OrderDetails AS od
  WHERE o.OrderDate >= '20091001'
  AND o.OrderID = od.ProductID;

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

SELECT o.OrderID, od.ProductID
  FROM dbo.Orders AS o
  INNER JOIN dbo.OrderDetails AS od
    ON o.OrderID = od.ProductID
  WHERE o.OrderDate >= '20091001';

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

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

Версия данного синтаксиса с OUTER JOIN (через *= и =*) во многих SQL серверах упразднена, так что это ещё одна причина избегать использования данного синтаксиса (через перечисление таблиц в SELECT). Многие помнят, как работают данные условия, но пометка "устаревшее" или отсутствие синтаксиса *= и =* – всё же важный фактор. Опять же, лучше ключевыми словами описывать условия соединения во избежание двусмысленности:

SELECT p.ProductName, p.ProductID
  FROM dbo.Products AS p
  LEFT OUTER JOIN dbo.OrderDetails AS od
    ON p.ProductID = od.ProductID
  WHERE od.ProductID IS NULL;

Также есть задокументированные случаи, когда старомодные внешние соединения (аналоги * OUTER JOIN) выдавали неверные результаты в зависимости от порядка исполнения фильтрующих условий. Да, они сейчас объявлены устаревшими, где-то упразднены, но данный факт всё же показывает, что лучше не использовать старомодные соединения.

Если вам ещё не достаточно причин отказаться от старых соединений... Вот ещё большая проблема в использовании неявного синтаксиса, когда условия соединения описываются там же, где и условия фильтрации. А что если вы просто забыли в списке прочих критериев указать критерий объединения? Вы неумышленно получаете CROSS JOIN (Декартово Произведение нескольких таблиц). Рассмотрим эту ситуацию на первом примере:

SELECT o.OrderID, od.ProductID
  FROM dbo.Orders AS o, dbo.OrderDetails AS od
  WHERE o.OrderDate >= '20091001';

Для каждого заказа из Orders с 1-го октября 2009-го года вы получите копию каждой строки из таблицы OrderDetails за всё время. Вы потеряли критерий соединения, поэтому для каждой строки из левой таблицы вы присоедините все строки из правой таблицы. Да, БД оптимизирует по критерию фильтрации, поэтому из левой таблицы вы возьмёте только лишь все заказы с октября 2009-го...

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

С другой стороны, вы могли узнать об этой ошибке ещё на моменте первого написания / тестирования данного кода. Если использовать явное указание соединения таблиц через INNER JOIN, база данных от вас потребует условие, так как синтаксис INNER JOIN требует указания условия соединения ON.

Конечно, вы всё ещё можете "выстрелить себе в ногу", указав, например ON o.OrderID = o.OrderID. Но согласитесь, хорошо, когда синтаксис уменьшает количество вариантов ошибиться!

Моя основная цель – помочь людям избавиться от вредных привычек, которые Майкрософт им прививает, публикуя примеры кода, использующие данный синтаксис. Это не проблема для ветеранов SQL (кроме тех, что не желают развиваться), тех, кто понимает, как соединения работают, и знает историю ANSI-89 и ANSI-92. Однако, новички в SQL могут встретить этот синтаксис в книжках или заметках в интернете.

Также может быть вам интересно:

Трюки терминала Bash

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

Читать »

Вредные привычки в SQL: не создавайте ID колонку в каждой таблице

Многие начинающие или просто ленивые программисты бездумно лепят поле ID в каждую таблицу: нужно это или нет. Вот небольшое объяснение: когда это полезно, а когда нет.

Читать »
Фото Python: Встроенные типы данных (list, set, dict, etc)

Python: Встроенные типы данных (list, set, dict, etc)

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

Фото Python: типы данных, переменные, логическое ветвление и циклы

Python: типы данных, переменные, логическое ветвление и циклы

Первая часть заметок о Python. О базовых типах, переменных, ветвлении и циклах.

Фото Как установить PostgreSQL на Linux и создать базу и пользователя

Как установить PostgreSQL на Linux и создать базу и пользователя

PostgreSQL - система управления базой данных общего назначения. Одна из самых распространённых баз данных, используемая на многих коммерческих и некоммерческих проектах.

Фото Как сделать свою middleware в Django (с примерами)

Как сделать свою middleware в Django (с примерами)

Middleware или "промежуточное программное обеспечение" - элегантный способ установить общие правила обработки запросов и ответов приложения. Давайте напишем парочку middleware, чтобы понять, как они работают.

Фото Добавляем постраничную пагинацию на Django сайт

Добавляем постраничную пагинацию на Django сайт

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

Фото Microsoft открывает исходники, а её IDE супер-популярна

Microsoft открывает исходники, а её IDE супер-популярна

Решил сложить пару фактов и немного над этим поразмыслить. Реально ли Microsoft "переобулись"?

Фото Пример своей консольной команды в Django проекте

Пример своей консольной команды в Django проекте

Если вы работали с Django проектом, то, скорее всего, запускали команды из консоли (manage.py). В Django есть простой способ писать свои команды для управления проектом.

Фото Разграничение прав доступа на Django сайте

Разграничение прав доступа на Django сайте

Почти на любом веб-сайте необходимо разделять пользователей на группы и предоставлять им разные возможности. В Django есть довольно серьёзная система прав доступа для пользователей - давайте её рассмотрим!