Ответ
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-проверки, и всё будет ебать как по маслу.