JOIN в Oracle SQL. Введение
Давай по‑честному: без JOIN жизнь в Oracle SQL была бы серой. Именно соединения позволяют связывать таблицы логикой предметной области — от заказов и клиентов до сложных аналитических витрин. Я покажу простым языком, как мыслить связями, не перегружая запросы и не теряя производительность.
Синтаксис
SELECT t1.col1,t2.col2
FROM t1
INNER JOIN t2 ON t2.fk=t1.pk
-- Варианты: LEFT/RIGHT/FULL OUTER,CROSS,NATURAL,USING(...)
-- Советы: ключи равенства держи в ON/USING, фильтры результата — в WHEREГде используют
- Операционная отчётность: связать факты продаж со справочниками клиентов и товаров.
- Аналитика: собрать витрину из фактов и измерений с аккуратной денормализацией.
- Качество данных: находить «висячие» ссылки и аномальные многие‑ко‑многим.
- Оптимизация: баланс условий между ON и WHERE, предагрегация и умные подсказки.
Коротко о лучших практиках
- Определи «драйвер» выборки — таблицу, задающую зерно.
- Связи равенства — в ON/USING, фильтры — в WHERE.
- Выводи только нужные столбцы — это делает план легче и быстрее.
- Предагрегируй с GROUP BY перед сложными связями «многие‑ко‑многим».
- Осторожнее с FULL OUTER: полезно, но затратно.
100 примеров
1. Связь фактов и справочника по ключу
SELECT o.order_id,o.amount,c.customer_name
FROM orders o
INNER JOIN customers c ON c.customer_id=o.customer_id2. Левое соединение когда не у всех есть профиль
SELECT u.user_id,u.email,p.phone
FROM users u
LEFT OUTER JOIN profiles p ON p.user_id=u.user_id3. Правое при акценте на справочнике
SELECT d.dept_id,d.dept_name,e.emp_name
FROM employees e
RIGHT OUTER JOIN departments d ON d.dept_id=e.dept_id4. Полное объединение для сверки остатка
SELECT a.sku,a.qty AS a_qty,b.qty AS b_qty
FROM stock_a a
FULL OUTER JOIN stock_b b ON b.sku=a.sku5. Сведение многих к одному через USING
SELECT s.sale_id,s.amount,d.dim_name
FROM sales s
INNER JOIN dims d USING(dim_id)6. Декартово произведение для генерации сетки
SELECT d.dt,h.hh
FROM calendar_days d
CROSS JOIN hours h7. Самосоединение для поиска менеджера
SELECT e.emp_name,m.emp_name AS manager
FROM employees e
LEFT OUTER JOIN employees m ON m.emp_id=e.manager_id8. Фильтр после соединения в WHERE
SELECT o.order_id,o.status,c.city
FROM orders o
INNER JOIN customers c ON c.customer_id=o.customer_id
WHERE o.status='PAID'9. Соединение с подзапросом предагрегации
SELECT c.customer_id,c.customer_name,t.total
FROM customers c
LEFT OUTER JOIN(
SELECT customer_id,SUM(amount) AS total
FROM orders
GROUP BY customer_id
) t ON t.customer_id=c.customer_id10. Соединение по нескольким условиям
SELECT f.id,f.region,d.rate
FROM fact f
INNER JOIN dict_rates d ON d.region=f.region AND d.year=f.year11. USING когда имена ключей совпадают
SELECT s.sale_id,s.amount,p.product_name
FROM sales s
INNER JOIN products p USING(product_id)12. Сопоставление дат по диапазону
SELECT b.booking_id,r.room_no
FROM bookings b
INNER JOIN rooms r ON r.hotel_id=b.hotel_id AND b.dt BETWEEN r.dt_from AND r.dt_to13. Слияние клиента с последним заказом
SELECT c.customer_id,c.customer_name,o.order_id,o.amount
FROM customers c
LEFT OUTER JOIN(
SELECT order_id,customer_id,amount,ROW_NUMBER() OVER(PARTITION BY customer_id ORDER BY order_date DESC) AS rn
FROM orders
) o ON o.customer_id=c.customer_id AND o.rn=114. Связь по составному ключу
SELECT l.country,l.city,w.temp
FROM locations l
INNER JOIN weather w ON w.country=l.country AND w.city=l.city15. Обогащение транзакций валютным курсом
SELECT t.id,t.amt,t.ccy,r.rate
FROM trx t
LEFT OUTER JOIN rates r ON r.ccy=t.ccy AND r.rate_date=t.trx_date16. Связь фактов и календаря по дате
SELECT f.id,f.amount,cal.week_of_year
FROM fact_sales f
LEFT OUTER JOIN dim_calendar cal ON cal.dt=f.sale_date17. Атрибутика заказа из нескольких таблиц
SELECT o.order_id,c.customer_name,p.product_name
FROM orders o
INNER JOIN customers c ON c.customer_id=o.customer_id
INNER JOIN products p ON p.product_id=o.product_id18. Сведение many‑to‑many через предагрегацию
SELECT t.tag,COUNT(*) AS posts
FROM(
SELECT pt.post_id,t.tag
FROM post_tags pt
INNER JOIN tags t ON t.tag_id=pt.tag_id
) t
GROUP BY t.tag19. Сопоставление адресов с регионами по префиксу
SELECT a.addr_id,a.zip,r.region
FROM addresses a
LEFT OUTER JOIN zip_regions r ON SUBSTR(a.zip,1,3)=r.zip_prefix20. Подтягиваем статус доставки
SELECT o.order_id,o.amount,sh.status
FROM orders o
LEFT OUTER JOIN shipments sh ON sh.order_id=o.order_idЕще 20 примеров
21. Слияние чеков с платежами за сутки
SELECT ch.id,ch.total,p.payment_method
FROM checks ch
LEFT OUTER JOIN payments p ON p.check_id=ch.id AND TRUNC(p.paid_at)=TRUNC(ch.created_at)22. Обогащение события юзер‑агентом
SELECT e.event_id,e.path,u.agent
FROM events e
LEFT OUTER JOIN user_agents u ON u.ua_id=e.ua_id23. Сопоставление сотрудников и городов
SELECT e.emp_id,e.emp_name,c.city_name
FROM employees e
LEFT OUTER JOIN cities c ON c.city_id=e.city_id24. Сверка справочников по полному объединению
SELECT NVL(a.code,b.code) AS code,a.name AS a_name,b.name AS b_name
FROM dict_a a
FULL OUTER JOIN dict_b b ON b.code=a.code25. Соединение с in‑line view для быстрого фильтра
SELECT o.order_id,o.amount
FROM orders o
INNER JOIN(SELECT order_id FROM fraud WHERE flag='Y') f ON f.order_id=o.order_id26. Поиск лишних связей через левое и NULL
SELECT p.product_id
FROM products p
LEFT OUTER JOIN product_images i ON i.product_id=p.product_id
WHERE i.product_id IS NULL27. Синхронизация остатков склада
SELECT s.sku,s.qty AS system_qty,r.qty AS real_qty
FROM stock_system s
LEFT OUTER JOIN stock_real r ON r.sku=s.sku28. Связь по ключу и дате актуальности
SELECT k.id,k.value,h.note
FROM keys k
LEFT OUTER JOIN key_history h ON h.id=k.id AND k.dt BETWEEN h.valid_from AND h.valid_to29. Разворот контактов клиента
SELECT c.customer_id,c.customer_name,ph.phone,em.email
FROM customers c
LEFT OUTER JOIN phones ph ON ph.customer_id=c.customer_id
LEFT OUTER JOIN emails em ON em.customer_id=c.customer_id30. Самосопоставление — коллеги из одного отдела
SELECT a.emp_name AS e1,b.emp_name AS e2
FROM employees a
INNER JOIN employees b ON b.dept_id=a.dept_id AND b.emp_id<>a.emp_id31. Справочник валют к транзакциям по дате начала
SELECT t.id,t.ccy,r.rate
FROM trx t
LEFT OUTER JOIN rates r ON r.ccy=t.ccy AND r.valid_from<=t.trx_date32. Связка склада и ассортимента
SELECT s.sku,p.product_name,s.qty
FROM stock s
INNER JOIN products p ON p.sku=s.sku33. Соотнесение заказов и счетов
SELECT o.order_id,inv.invoice_id,inv.total
FROM orders o
LEFT OUTER JOIN invoices inv ON inv.order_id=o.order_id34. Атрибутика платежа через две таблицы
SELECT p.payment_id,c.customer_name,m.method_name
FROM payments p
INNER JOIN customers c ON c.customer_id=p.customer_id
INNER JOIN methods m ON m.method_id=p.method_id35. Выбор последних статусов по пользователю
SELECT u.user_id,u.email,s.status
FROM users u
LEFT OUTER JOIN(
SELECT user_id,status,ROW_NUMBER() OVER(PARTITION BY user_id ORDER BY changed_at DESC) rn
FROM statuses
) s ON s.user_id=u.user_id AND s.rn=136. Соединение с подсчётом заказов в подзапросе
SELECT c.customer_id,c.customer_name,t.cnt
FROM customers c
LEFT OUTER JOIN(
SELECT customer_id,COUNT(*) cnt
FROM orders
GROUP BY customer_id
) t ON t.customer_id=c.customer_id37. Сопоставление позиций и чеков
SELECT i.item_id,i.price,ch.check_id
FROM items i
INNER JOIN checks ch ON ch.check_id=i.check_id38. Сведение по телефону или email
SELECT c.customer_id,c.customer_name,co.contact
FROM customers c
LEFT OUTER JOIN contacts co ON co.customer_id=c.customer_id AND co.primary_flag='Y'39. Сопоставление IP с геобазой по сети
SELECT l.ip,g.country
FROM logs l
LEFT OUTER JOIN geo g ON l.ip BETWEEN g.ip_from AND g.ip_to40. Аналитика заказов с типом клиента
SELECT o.order_id,o.amount,ct.type_name
FROM orders o
LEFT OUTER JOIN customer_types ct ON ct.type_id=o.type_idЕще 20 примеров
41. Детализация суммы по товарам
SELECT o.order_id,p.product_name,oi.qty,oi.qty*p.price AS line_total
FROM orders o
INNER JOIN order_items oi ON oi.order_id=o.order_id
INNER JOIN products p ON p.product_id=oi.product_id42. Связка событий и каналов
SELECT e.event_id,ch.channel_name
FROM events e
LEFT OUTER JOIN channels ch ON ch.channel_id=e.channel_id43. Сопоставление действий с реферальной кампанией
SELECT a.action_id,c.campaign_name
FROM actions a
LEFT OUTER JOIN campaigns c ON c.campaign_id=a.campaign_id44. Фокус на справочнике и возможном отсутствии фактов
SELECT p.product_id,p.product_name,oi.qty
FROM products p
LEFT OUTER JOIN order_items oi ON oi.product_id=p.product_id45. Выборка, где нет соответствия в справочнике
SELECT t.code
FROM trx_types t
LEFT OUTER JOIN dict_types d ON d.code=t.code
WHERE d.code IS NULL46. Проверка целостности внешних ключей
SELECT o.order_id
FROM orders o
LEFT OUTER JOIN customers c ON c.customer_id=o.customer_id
WHERE c.customer_id IS NULL47. Сопоставление версий документов
SELECT d.doc_id,v.version_no,v.author
FROM docs d
LEFT OUTER JOIN versions v ON v.doc_id=d.doc_id48. Сводка продаж по категориям
SELECT cat.category_name,SUM(oi.qty*p.price) AS revenue
FROM categories cat
LEFT OUTER JOIN products p ON p.category_id=cat.category_id
LEFT OUTER JOIN order_items oi ON oi.product_id=p.product_id
GROUP BY cat.category_name49. Прайс и актуальная скидка на дату
SELECT p.product_id,p.product_name,pr.price,dc.percent
FROM products p
LEFT OUTER JOIN prices pr ON pr.product_id=p.product_id AND pr.price_date=DATE '2025-09-01'
LEFT OUTER JOIN discounts dc ON dc.product_id=p.product_id AND dc.valid_from<=DATE '2025-09-01'50. Связка платежей и статей ДДС
SELECT pay.payment_id,gl.gl_name
FROM payments pay
LEFT OUTER JOIN gl_dict gl ON gl.gl_id=pay.gl_id51. Обогащение лога справочником ошибок
SELECT l.err_id,l.message,d.err_text
FROM error_log l
LEFT OUTER JOIN error_dict d ON d.err_id=l.err_id52. Клиент и его основной адрес
SELECT c.customer_id,c.customer_name,a.city,a.street
FROM customers c
LEFT OUTER JOIN addresses a ON a.customer_id=c.customer_id AND a.primary_flag='Y'53. Сопоставление транзакций и терминалов
SELECT t.trx_id,t.amount,term.terminal_name
FROM trx t
LEFT OUTER JOIN terminals term ON term.terminal_id=t.terminal_id54. Фокус на всех датах календаря даже без фактов
SELECT cal.dt,NVL(f.amount,0) AS amt
FROM dim_calendar cal
LEFT OUTER JOIN fact_daily f ON f.dt=cal.dt55. Связка пользователей и ролей
SELECT u.user_id,u.email,r.role_name
FROM users u
LEFT OUTER JOIN user_roles ur ON ur.user_id=u.user_id
LEFT OUTER JOIN roles r ON r.role_id=ur.role_id56. Обогащение колл‑логов маршрутом
SELECT c.call_id,r.route_name
FROM calls c
LEFT OUTER JOIN routes r ON r.route_id=c.route_id57. Соотнесение платежей и возмещений
SELECT pay.payment_id,ref.refund_id,ref.amount
FROM payments pay
LEFT OUTER JOIN refunds ref ON ref.payment_id=pay.payment_id58. Склейка доп. атрибутов товара
SELECT p.product_id,p.product_name,a.attr_name,a.attr_value
FROM products p
LEFT OUTER JOIN product_attrs a ON a.product_id=p.product_id59. Сопоставление тикетов и агентов
SELECT t.ticket_id,a.agent_name
FROM tickets t
LEFT OUTER JOIN agents a ON a.agent_id=t.agent_id60. Агрегация после присоединения справочников
SELECT c.city_name,COUNT(*) AS orders_cnt
FROM orders o
INNER JOIN customers cu ON cu.customer_id=o.customer_id
INNER JOIN cities c ON c.city_id=cu.city_id
GROUP BY c.city_nameЕще 20 примеров
61. Выборка сотрудников без отдела
SELECT e.emp_id,e.emp_name
FROM employees e
LEFT OUTER JOIN departments d ON d.dept_id=e.dept_id
WHERE d.dept_id IS NULL62. Сравнение прайсов двух поставщиков
SELECT NVL(a.sku,b.sku) AS sku,a.price AS price_a,b.price AS price_b
FROM price_a a
FULL OUTER JOIN price_b b ON b.sku=a.sku63. Связка заказов и статусов оплаты
SELECT o.order_id,o.amount,p.status
FROM orders o
LEFT OUTER JOIN pay_status p ON p.order_id=o.order_id64. Подтягиваем страну по IP с диапазонами
SELECT l.id,g.country
FROM logins l
LEFT OUTER JOIN geoip g ON l.ip_num BETWEEN g.ip_from AND g.ip_to65. Матчинг клиентов по email
SELECT a.id AS a_id,b.id AS b_id,a.email
FROM src_a a
INNER JOIN src_b b ON LOWER(b.email)=LOWER(a.email)66. Синхронизация каталога и остатков
SELECT p.sku,p.product_name,st.qty
FROM products p
LEFT OUTER JOIN stock st ON st.sku=p.sku67. Слияние KPI и целей
SELECT kpi.metric,kpi.value,goal.target
FROM kpi
LEFT OUTER JOIN goals goal ON goal.metric=kpi.metric68. Аналитика: корзина и купоны
SELECT cart.user_id,coupon.code
FROM carts cart
LEFT OUTER JOIN coupons coupon ON coupon.coupon_id=cart.coupon_id69. Сопоставление курса по ближайшей дате
SELECT t.id,t.ccy,r.rate
FROM trx t
LEFT OUTER JOIN(
SELECT ccy,rate_date,rate,ROW_NUMBER() OVER(PARTITION BY ccy ORDER BY rate_date DESC) rn
FROM rates
) r ON r.ccy=t.ccy AND r.rn=170. Связка задач и исполнителей
SELECT t.task_id,u.user_name
FROM tasks t
LEFT OUTER JOIN users u ON u.user_id=t.assignee_id71. Детализация заказов и позиций
SELECT o.order_id,oi.item_id,oi.qty
FROM orders o
INNER JOIN order_items oi ON oi.order_id=o.order_id72. Сопоставление городов и регионов
SELECT c.city_name,r.region_name
FROM cities c
LEFT OUTER JOIN regions r ON r.region_id=c.region_id73. Обогащение логов устройствами
SELECT l.event_id,d.device_type
FROM logs l
LEFT OUTER JOIN devices d ON d.device_id=l.device_id74. Разделение платёжных проводок по счетам
SELECT p.posting_id,acc.account_no
FROM postings p
LEFT OUTER JOIN accounts acc ON acc.account_id=p.account_id75. Достижения игрока и уровни
SELECT a.player_id,lvl.level_name
FROM achievements a
LEFT OUTER JOIN levels lvl ON lvl.level_id=a.level_id76. Связка просмотров и контента
SELECT v.view_id,c.title
FROM views v
LEFT OUTER JOIN content c ON c.content_id=v.content_id77. Присоединение группы лояльности
SELECT c.customer_id,c.customer_name,l.group_name
FROM customers c
LEFT OUTER JOIN loyalty l ON l.customer_id=c.customer_id78. Обогащение заявок статусом KYC
SELECT a.app_id,k.status
FROM applications a
LEFT OUTER JOIN kyc k ON k.app_id=a.app_id79. Соотнесение поездок и водителей
SELECT r.ride_id,d.driver_name
FROM rides r
LEFT OUTER JOIN drivers d ON d.driver_id=r.driver_id80. Гистограмма заказов по категории
SELECT cat.category_name,COUNT(*) cnt
FROM categories cat
LEFT OUTER JOIN products p ON p.category_id=cat.category_id
LEFT OUTER JOIN order_items oi ON oi.product_id=p.product_id
GROUP BY cat.category_nameЕще 20 примеров
81. Связка клиентов и последних визитов
SELECT c.customer_id,c.customer_name,v.visit_at
FROM customers c
LEFT OUTER JOIN(
SELECT customer_id,MAX(visit_at) AS visit_at
FROM visits
GROUP BY customer_id
) v ON v.customer_id=c.customer_id82. Сопоставление с ближайшим складом по городу
SELECT o.order_id,w.wh_name
FROM orders o
LEFT OUTER JOIN warehouses w ON w.city_id=o.city_id83. Связка транзакций и категорий расходов
SELECT t.trx_id,cat.cat_name
FROM trx t
LEFT OUTER JOIN categories cat ON cat.cat_id=t.cat_id84. Матчинг по нормализованному телефону
SELECT p.person_id,c.contact_id
FROM persons p
INNER JOIN contacts c ON REGEXP_REPLACE(c.phone,'\D')=REGEXP_REPLACE(p.phone,'\D')85. Разруливание дубликатов по ключу
SELECT s.id,src.source_name
FROM sources s
LEFT OUTER JOIN src_map src ON src.id=s.id86. Обогащение заказов менеджером
SELECT o.order_id,m.manager_name
FROM orders o
LEFT OUTER JOIN managers m ON m.manager_id=o.manager_id87. Сопоставление платежей и банков
SELECT p.payment_id,b.bank_name
FROM payments p
LEFT OUTER JOIN banks b ON b.bank_id=p.bank_id88. Связка визитов и каналов привлечения
SELECT v.visit_id,ch.channel_name
FROM visits v
LEFT OUTER JOIN channels ch ON ch.channel_id=v.channel_id89. Стримы и авторы
SELECT s.stream_id,a.author_name
FROM streams s
LEFT OUTER JOIN authors a ON a.author_id=s.author_id90. Профили и аватары
SELECT p.profile_id,a.url
FROM profiles p
LEFT OUTER JOIN avatars a ON a.profile_id=p.profile_id91. Материализация витрины с атрибутами
CREATE TABLE mart_orders AS
SELECT o.order_id,o.amount,c.customer_name,p.product_name
FROM orders o
INNER JOIN customers c ON c.customer_id=o.customer_id
INNER JOIN products p ON p.product_id=o.product_id92. Аналитика просмотров по типу контента
SELECT ct.type_name,COUNT(*) cnt
FROM content c
INNER JOIN content_types ct ON ct.type_id=c.type_id
GROUP BY ct.type_name93. Сопоставление сотрудников и должностей
SELECT e.emp_id,e.emp_name,j.job_name
FROM employees e
LEFT OUTER JOIN jobs j ON j.job_id=e.job_id94. Обогащение транзакций географией
SELECT t.id,geo.country,geo.city
FROM trx t
LEFT OUTER JOIN geo ON geo.geo_id=t.geo_id95. Проверка висячих ссылок на метод оплаты
SELECT o.order_id
FROM orders o
LEFT OUTER JOIN methods m ON m.method_id=o.method_id
WHERE m.method_id IS NULL96. Сверка остатков между системами
SELECT NVL(a.sku,b.sku) AS sku,a.qty AS a_qty,b.qty AS b_qty
FROM inv_a a
FULL OUTER JOIN inv_b b ON b.sku=a.sku97. Доставка и курьеры
SELECT d.delivery_id,c.courier_name
FROM deliveries d
LEFT OUTER JOIN couriers c ON c.courier_id=d.courier_id98. Кампании и объявления
SELECT ad.ad_id,cmp.campaign_name
FROM ads ad
LEFT OUTER JOIN campaigns cmp ON cmp.campaign_id=ad.campaign_id99. Клиент, заказ, товар — тройка
SELECT c.customer_name,o.order_id,p.product_name
FROM customers c
INNER JOIN orders o ON o.customer_id=c.customer_id
INNER JOIN products p ON p.product_id=o.product_id100. Финальный срез для отчёта
SELECT d.dt,SUM(oi.qty*p.price) AS revenue
FROM dim_calendar d
LEFT OUTER JOIN orders o ON TRUNC(o.order_date)=d.dt
LEFT OUTER JOIN order_items oi ON oi.order_id=o.order_id
LEFT OUTER JOIN products p ON p.product_id=oi.product_id
GROUP BY d.dtДокументация
Oracle SQL Language Reference — Joins
🧩 Заключение
JOIN — один из самых мощных инструментов SQL. Он позволяет соединять таблицы и строить сложные отчёты.
Понимание разных типов JOIN’ов — критически важно для аналитики и разработки сложных систем на Oracle SQL.
🔜 Следующая статья:
INNER в Oracle SQL — что такое INNER JOIN и как он работает под капотом