FULL JOIN

d

Зачем нужен FULL JOIN: реальные задачи в Delphi

В коммерческой разработке на Delphi приходится отображать данные из двух таблиц, где каждая может содержать строки, не имеющие пары во второй. Обычные JOIN'ы (INNER, LEFT, RIGHT) отсекают часть информации. Например, в системе учёта клиентов — таблица клиентов (Clients с 340 записями) и таблица заказов (Orders с 190 заказами). Чтобы увидеть всех клиентов (даже тех, кто ничего не заказывал) И одновременно выявить заказы «без владельца», нужен FULL JOIN. Он не отбрасывает ни одну сторону.

Структура данных для примера

-- Таблица "Клиенты"
ClientID : Integer (PK)
ClientName : VarChar(50)
-- Всего 340 строк, 45 клиентов без заказов

-- Таблица "Заказы"
OrderID : Integer (PK)
ClientID : Integer (FK)
Amount : Currency
-- Всего 190 строк, 3 заказа с NULL в ClientID (ошибка или legacy)

Пошаговый отбор: как выглядит запрос

  1. Формулируем задачу: получить список всех ClientID и OrderID, включая те, где нет пары. Итоговое количество строк = 340 + 3 = 343.
  2. Пишем FULL JOIN:
    SELECT COALESCE(c.ClientID, o.ClientID) AS ClientRef,
    c.ClientName, o.OrderID, o.Amount
    FROM Clients c
    FULL JOIN Orders o ON c.ClientID = o.ClientID
  3. Фильтруем по ситуации:
    — Чтобы увидеть только клиентов без заказов — добавляем WHERE o.OrderID IS NULL (45 строк).
    — Чтобы найти «висящие» заказы — WHERE c.ClientID IS NULL (3 строки).
  4. Загружаем в Delphi: используем TFDQuery или TSQLQuery. Результат — 343 строки.

Конкретные цифры и типичные ошибки

Ошибка №1: Путают с INNER JOIN. Разработчики ждут, что FULL JOIN выдаст только те строки, где есть совпадение. В нашем примере — 295 строк вместо 343. Потеря 48 строк критична для аудита.

Ошибка №2: Забывают про COALESCE. Если в SELECT написать c.ClientID, то для сиротских заказов (где c.ClientID = NULL) поле будет пустым. Всегда подтягивайте ID из обеих таблиц через COALESCE.

Ошибка №3: Условия в WHERE отсекают NULL. Попытка отфильтровать запись: WHERE c.ClientID = NULL — синтаксически неверно. Пишите WHERE c.ClientID IS NULL.

Ошибка №4: Игнорирование индексов. При FULL JOIN на больших таблицах (например, 50 000 строк) без индекса по внешнему ключу запрос в FireDAC выполняется 8–12 сек. С индексом — 0.02 секунды.

Ошибка №5: Неправильный порядок таблиц. Для LEFT JOIN порядок важен, для FULL JOIN — нет. Но если поменять местами таблицы, количество строк не изменится — 343.

Где FULL JOIN применяется в реальных проектах

Пример кода на Delphi (FireDAC)

procedure LoadFullJoinData(ADBConnection: TFDConnection);
var
  Q: TFDQuery;
begin
  Q := TFDQuery.Create(nil);
  try
    Q.Connection := ADBConnection;
    Q.SQL.Text :=
      'SELECT COALESCE(c.ClientID, o.ClientID) AS ClientRef, ' +
      'c.ClientName, o.OrderID, o.Amount ' +
      'FROM Clients c FULL JOIN Orders o ON c.ClientID = o.ClientID';
    Q.Open;
    
    // Подсчёт общего числа строк
    ShowMessage('Всего строк: ' + IntToStr(Q.RecordCount));
    
    // Проверка наличия NULL-значений
    while not Q.Eof do
    begin
      if Q.FieldByName('ClientName').IsNull then
        Memo1.Lines.Add('Заказ без клиента: OrderID = ' +
          Q.FieldByName('OrderID').AsString);
      Q.Next;
    end;
  finally
    Q.Free;
  end;
end;

Советы по производительности

Добавлено: 27.04.2026