Что такое LEFT JOIN, в чем его отличие от INNER JOIN? Приведите пример использования в Go.

Ответ

LEFT JOIN (или LEFT OUTER JOIN) — это операция в SQL, которая возвращает все строки из левой таблицы (первой в запросе) и совпадающие строки из правой таблицы. Если совпадения в правой таблице для строки из левой не найдено, то столбцы правой таблицы в результирующей строке будут заполнены значениями NULL.

Ключевое отличие от INNER JOIN:

  • INNER JOIN возвращает только те строки, для которых есть совпадение в обеих таблицах.
  • LEFT JOIN возвращает все строки из левой таблицы, независимо от того, есть ли для них совпадение в правой.

Когда используется?

LEFT JOIN идеально подходит для ситуаций, когда нужно получить все данные из основной таблицы и дополнить их связанными данными из другой, если они существуют. Классический пример: «получить всех пользователей и их заказы, включая тех пользователей, у которых заказов нет».

Пример использования в Go (database/sql)

При работе с LEFT JOIN в Go важно правильно обрабатывать NULL значения. Для этого в пакете database/sql есть специальные типы, такие как sql.NullString, sql.NullInt64 и т.д.

import (
    "database/sql"
    "fmt"
    "log"
)

// ... (db - это ваше *sql.DB соединение)

// Получаем всех пользователей и ID их последнего заказа (если он есть)
rows, err := db.Query(`
    SELECT u.name, o.id 
    FROM users AS u 
    LEFT JOIN orders AS o ON u.id = o.user_id
`)
if err != nil {
    log.Fatal(err)
}
defer rows.Close()

for rows.Next() {
    var userName string
    // Используем sql.NullInt64, так как o.id может быть NULL
    var orderID sql.NullInt64 

    if err := rows.Scan(&userName, &orderID); err != nil {
        log.Fatal(err)
    }

    if orderID.Valid { // Проверяем, было ли значение (не NULL)
        fmt.Printf("Пользователь: %s, ID Заказа: %dn", userName, orderID.Int64)
    } else {
        fmt.Printf("Пользователь: %s, Заказов нетn", userName)
    }
}

Важный аспект производительности: для эффективной работы LEFT JOIN поля, по которым происходит объединение (u.id и o.user_id), должны быть проиндексированы.

Ответ 18+ 🔞

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

Представь себе бар. Левая таблица — это все посетители за вечер. Правая таблица — это заказы на алкоголь. INNER JOIN — это как выписать нахуй всех трезвенников и тех, кто просто пришёл потусить: он покажет только тех, кто и пришёл, и заказал выпивку. А LEFT JOIN — это полный список гостей, и для каждого, кто бухал, мы видим, что он заказывал, а для тех, кто пил сок — в графе «заказ» будет пустота, NULL, нихуя.

Когда это нужно? Ну, например, когда начальник-придурок требует отчёт: «Хочу список всех сотрудников и их проекты!». А у половины людей проектов нет, они просто в телеграме сидят. Вот LEFT JOIN и выведет этих лодырей тоже, просто с пустой колонкой проекта. Справедливо, блядь.

А теперь смотри, как в Go с этим не обосраться. Главная засада — эти самые NULL значения. Если в правой таблице для строки нихуя нет, то вместо данных прилетает NULL. А Go NULL не любит, он от него паникует, как кот от огурца. Поэтому нужно использовать специальные обёртки — sql.NullString, sql.NullInt64 и прочую хуйню.

Вот тебе живой пример, смотри:

// ... подключение к базе и прочая муть

// Хотим всех юзеров и их аватарки (если есть)
rows, err := db.Query(`
    SELECT u.login, a.avatar_url
    FROM users u
    LEFT JOIN avatars a ON u.id = a.user_id -- Берём ВСЕХ юзеров, даже без аватарки
`)
if err != nil {
    log.Fatalf("Запрос сломался: %v", err) // Красиво падаем
}
defer rows.Close() // Не забудь закрыть, а то память потечёт

for rows.Next() {
    var login string
    var avatarURL sql.NullString // Вот она, волшебная обёртка для возможного NULL!

    err := rows.Scan(&login, &avatarURL)
    if err != nil {
        log.Fatal(err)
    }

    if avatarURL.Valid { // Проверяем, а не NULL ли нам подсунули?
        fmt.Printf("Юзер %s, аватарка: %sn", login, avatarURL.String)
    } else {
        fmt.Printf("Юзер %s — без авы, нищеброд.n", login) // Вот они, наши NULL'ы
    }
}

Видишь? Мы всех достали. И тех, у кого аватарка есть (avatarURL.Valid == true), и тех, у кого вместо авы — дырка от бублика. Без этой проверки на Valid можно было бы схватить панику, пытаясь вытащить строку из ничего.

И последнее, про скорость. Если у тебя таблицы на миллионы записей, а индексов на полях для джойна (u.id и a.user_id) нет — то запрос будет выполняться до второго пришествия. База будет тупо перебирать всё подряд, как дурак. Так что индексируй, блядь, ключевые поля, это не шутки.

Вот и вся магия. LEFT JOIN — это «дай мне всё отсюда, и по возможности прицепь то, что найдётся там». Главное — не забывай про NULL-проверки, и всё будет ебать как по маслу.