Sorun
“Her müşterinin son iki siparişi” sorusunu düşün. İlişkili alt sorgu SELECT listesinde yalnızca tek bir değer döndürebilir; iki satır ve birkaç sütun döndüremez. FROM içindeki normal alt sorgu ise dışarıdaki tabloyu göremez.
LATERAL: FROM içinde ilişkili alt sorgu
LATERAL anahtar kelimesi, FROM içindeki alt sorgunun solundaki tablolara başvurmasına izin verir. Alt sorgu her dış satır için yeniden çalışır gibi düşünülebilir.
SELECT c.customer_id, s.order_id, s.order_date FROM customers c CROSS JOIN LATERAL ( SELECT o.order_id, o.order_date FROM orders o WHERE o.customer_id = c.customer_id ORDER BY o.order_date DESC LIMIT 2 ) AS s WHERE c.customer_id <= 3 ORDER BY c.customer_id, s.order_date DESC;
| SQL türü | İç birleşim | Dış birleşim (eşi olmayan da kalsın) |
|---|---|---|
| PostgreSQL 9.3+, MySQL 8.0.14+ | CROSS JOIN LATERAL (...) AS s | LEFT JOIN LATERAL (...) AS s ON TRUE |
| SQL Server | CROSS APPLY (...) AS s | OUTER APPLY (...) AS s |
| Oracle 12c+ | CROSS APPLY veya CROSS JOIN LATERAL | OUTER APPLY |
| BigQuery | FROM içinde ilişkili alt sorgu yoktur; aynı iş pencere fonksiyonuyla yapılır (aşağıda). | |
Eşi olmayanı korumak: LEFT JOIN LATERAL ... ON TRUE
Alt sorgu bir dış satır için hiç satır döndürmezse CROSS JOIN LATERAL o satırı düşürür. Dış satırın kalması için LEFT JOIN LATERAL kullanılır. Koşul zaten alt sorgunun içinde olduğundan ON kısmına TRUE yazılır.
SELECT d.name, s.calisan FROM departments d LEFT JOIN LATERAL ( SELECT COUNT(*) AS calisan FROM employees e WHERE e.dept_id = d.dept_id ) AS s ON TRUE;
Pencere fonksiyonuyla karşılaştırma
Aynı “her gruptan ilk N” sorusu ROW_NUMBER ile de çözülür ve her SQL türünde çalışır:
WITH s AS (
SELECT customer_id, order_id, order_date,
ROW_NUMBER() OVER (PARTITION BY customer_id ORDER BY order_date DESC) AS rn
FROM orders
)
SELECT customer_id, order_id, order_date
FROM s
WHERE rn <= 2 AND customer_id <= 3
ORDER BY customer_id, order_date DESC;
| ROW_NUMBER | LATERAL / APPLY | |
|---|---|---|
| Taşınabilirlik | Her SQL türünde | BigQuery'de yok |
| Az sayıda dış satır, büyük iç tablo | Tüm tabloyu numaralandırır | İndeks varsa her dış satır için birkaç satır okur; çok daha hızlı olabilir |
| Satır başına fonksiyon çağırmak | Uygun değil | Tablo döndüren fonksiyonları (STRING_SPLIT, OPENJSON, jsonb_array_elements) satır başına çağırmak için doğal yol |