Ana içeriğe geç
Kesit SQL
Orta · Küme ve Alt Sorgular

Satır başına alt sorgu: LATERAL ve APPLY

Her satır için ayrı çalışan, birden çok sütun ve satır döndürebilen alt sorgular; “her müşterinin son N siparişi”.

Uygulamada aç, 6 alıştırmayı çözÜcretsiz, hesap gerekmez, kurulum yok.

Bu sayfa dersin okuma sürümüdür. Metin bir kutuyu çalıştırmanı ya da adıma geçmeni söylüyorsa dersi uygulamada aç.

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şimDış birleşim (eşi olmayan da kalsın)
PostgreSQL 9.3+, MySQL 8.0.14+CROSS JOIN LATERAL (...) AS sLEFT JOIN LATERAL (...) AS s ON TRUE
SQL ServerCROSS APPLY (...) AS sOUTER APPLY (...) AS s
Oracle 12c+CROSS APPLY veya CROSS JOIN LATERALOUTER APPLY
BigQueryFROM 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_NUMBERLATERAL / APPLY
TaşınabilirlikHer SQL türündeBigQuery'de yok
Az sayıda dış satır, büyük iç tabloTü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ğırmakUygun değilTablo döndüren fonksiyonları (STRING_SPLIT, OPENJSON, jsonb_array_elements) satır başına çağırmak için doğal yol
Bu uygulamadaTarayıcıdaki motor LATERAL ve APPLY alt sorgularını eşdeğer bir birleşime çevirerek çalıştırır. Alt sorgu dış tabloya WHERE içindeki eşitlik koşullarıyla bağlanmalıdır (yukarıdaki örneklerdeki gibi).
Alıştırmalara geç (6)