# Veri Analisti Mülakatları İçin İleri Düzey SQL: Alt Sorgular, Pivot Tablolar ve Sorgu Optimizasyonu 2026 > Veri analisti mülakatlarında başarı için kritik olan ileri düzey SQL konuları: korelasyonlu alt sorgular, pivot tablolar, EXPLAIN planları ve indeksleme stratejileri. - Published: 2026-05-15 - Updated: 2026-05-15 - Author: Anthony Fillion-Maillet - Tags: sql, data-analytics, interview, query-optimization, subqueries - Reading time: 10 min --- Veri analisti pozisyonlarına yapılan başvurularda SQL bilgisi artık temel seviyenin çok ötesine geçmiş durumda. 2026 yılında Meta, Stripe ve Airbnb gibi şirketler, adayları korelasyonlu alt sorgular, pivot dönüşümleri ve sorgu optimizasyonu konularında test ediyor — bu beceriler, junior analistleri senior olanlardan ayırıyor. Bu rehber, teknik mülakatlarda çıkan kesin kalıpları, PostgreSQL 17 üzerinde çalışan üretim düzeyinde örneklerle ele almakta. > **Mülakatçıların Gerçekte Test Ettikleri** > > 2026 yılında veri analisti rolleri için SQL mülakatları üç alana odaklanıyor: karmaşık mantığı okunabilir alt sorgulara veya CTE'lere ayırma, satır düzeyindeki verileri koşullu toplama kullanarak pivot raporlara dönüştürme ve EXPLAIN planları ile indeksleme yoluyla sorgu performansı farkındalığı gösterme. Adayların çoğu optimizasyon konusunda başarısız oluyor — bu konuyu bilmek önemli bir avantaj sağlıyor. ## SQL Mülakatlarında Korelasyonlu Alt Sorgular ve Normal Alt Sorgular Korelasyonlu alt sorgu, dış sorgudan bir sütuna referans verir ve veritabanı motorunu, dış sonuç kümesindeki her satır için alt sorguyu yeniden değerlendirmeye zorlar. Normal (korelasyonsuz) alt sorgu bir kez çalışır ve sabit bir sonuç döndürür. Mülakatçılar, adayların her yaklaşımın ne zaman uygun olduğunu — ve bir JOIN'in her ikisinden de ne zaman daha iyi performans gösterdiğini — anlayıp anlamadıklarını özellikle test eder. Normal alt sorgu, eşik karşılaştırmaları için iyi çalışır. İç sorgu bir kez yürütülür ve dış sorgu bu tek değere göre filtreler. ```sql -- regular_subquery_threshold.sql -- Find all orders above the average order value SELECT order_id, customer_id, total_amount FROM orders WHERE total_amount > ( -- Executes once, returns a single scalar value SELECT AVG(total_amount) FROM orders ); ``` Korelasyonlu alt sorgu, karşılaştırma her satırın bağlamına bağlı olduğunda gereklidir. Klasik mülakat sorusu: "Departmanlarındaki ortalama maaştan fazla kazanan çalışanları bulun." ```sql -- correlated_subquery_department.sql -- Employees earning above their department average SELECT e.employee_id, e.name, e.department, e.salary FROM employees e WHERE e.salary > ( -- Re-evaluated for each employee's department SELECT AVG(e2.salary) FROM employees e2 WHERE e2.department = e.department ); ``` Korelasyonlu versiyon, her satırın departmanı için AVG hesaplamasını yeniden çalıştırır. Büyük tablolarda bu pahalı hale gelir. Optimize edilmiş yaklaşım, departman ortalamalarını bir kez hesaplamak için CTE veya pencere fonksiyonu kullanır. ```sql -- optimized_with_cte.sql -- Same result, single pass over the data WITH dept_avg AS ( SELECT department, AVG(salary) AS avg_salary FROM employees GROUP BY department ) SELECT e.employee_id, e.name, e.department, e.salary FROM employees e JOIN dept_avg d ON e.department = d.department WHERE e.salary > d.avg_salary; ``` Mülakatçılar, korelasyonlu versiyonu yazabilen ve ardından onu yeniden yapılandırabilen adaylar arar. Her iki yaklaşımı da sergilemek — ve performans ödünleşimini açıklamak — senior düzey SQL yetkinliği sinyali verir. ## Veri Analisti Mülakatlarında Sık Sorulan Alt Sorgu Kalıpları Üç alt sorgu kalıbı, veri analisti mülakatlarında tekrar tekrar karşımıza çıkar: üyelik kontrolleri için EXISTS, SELECT ifadelerinde skaler alt sorgular ve FROM ifadelerinde türetilmiş tablolar. **EXISTS vs. IN** — EXISTS, bir eşleşme bulunur bulunmaz kısa devre yapar ve büyük sonuç kümeleri için IN'den daha hızlı olur. IN, karşılaştırmadan önce alt sorgunun tüm sonucunu materyalize eder. ```sql -- exists_vs_in.sql -- Customers who placed at least one order in 2026 (EXISTS — preferred) SELECT c.customer_id, c.name FROM customers c WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id AND o.order_date >= '2026-01-01' ); -- Equivalent with IN (slower on large datasets) SELECT customer_id, name FROM customers WHERE customer_id IN ( SELECT customer_id FROM orders WHERE order_date >= '2026-01-01' ); ``` **SELECT'te skaler alt sorgu** — tam bir JOIN olmadan hesaplanmış bir sütun eklemek için kullanışlıdır. ```sql -- scalar_subquery_select.sql -- Each product with its category's total revenue SELECT p.product_id, p.product_name, p.category, ( SELECT SUM(oi.quantity * oi.unit_price) FROM order_items oi JOIN products p2 ON oi.product_id = p2.product_id WHERE p2.category = p.category ) AS category_total_revenue FROM products p; ``` Bu kalıp, bir veya iki sütun eklemek için temizdir. Daha fazlası için, bir JOIN ile CTE daha okunabilir ve genellikle daha hızlıdır. ## SQL'de Koşullu Toplama ile Pivot Sorguları Pivot sorguları, satır düzeyindeki verileri sütunlu özetlere dönüştürür — satır olarak saklanan ayları `jan_revenue`, `feb_revenue` gibi sütunlara çevirir. Bazı veritabanları yerel bir [PIVOT operatörü](https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot) sunsa da, taşınabilir yaklaşım toplama fonksiyonları içinde CASE ifadelerini kullanır. Mülakatçıların beklediği versiyon budur. Pivot sorgusu, toplama fonksiyonları içindeki CASE ifadeleriyle koşullu toplamayı birleştirir. Her CASE ifadesi belirli bir kategori için filtre uygular ve toplama (SUM, COUNT, AVG) filtrelenmiş satırları grup başına tek bir değere indirger. ```sql -- pivot_monthly_revenue.sql -- Monthly revenue pivot: one row per product, one column per month SELECT product_id, product_name, SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 1 THEN quantity * unit_price ELSE 0 END) AS jan_revenue, SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 2 THEN quantity * unit_price ELSE 0 END) AS feb_revenue, SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 3 THEN quantity * unit_price ELSE 0 END) AS mar_revenue, SUM(CASE WHEN EXTRACT(MONTH FROM order_date) = 4 THEN quantity * unit_price ELSE 0 END) AS apr_revenue, -- Repeat for remaining months SUM(quantity * unit_price) AS total_revenue FROM order_items oi JOIN orders o ON oi.order_id = o.order_id JOIN products p ON oi.product_id = p.product_id WHERE o.order_date >= '2026-01-01' GROUP BY product_id, product_name ORDER BY total_revenue DESC; ``` Aynı koşullu toplama tekniği, çok boyutlu pivotları da yönetir. Yaygın bir mülakat sorusu, adaylardan haftanın gününe ve duruma göre kullanıcı etkinliğini gösteren bir rapor oluşturmalarını ister. ```sql -- pivot_user_activity.sql -- User activity: sessions by day of week and device type SELECT user_id, COUNT(CASE WHEN device_type = 'mobile' THEN 1 END) AS mobile_sessions, COUNT(CASE WHEN device_type = 'desktop' THEN 1 END) AS desktop_sessions, COUNT(CASE WHEN device_type = 'tablet' THEN 1 END) AS tablet_sessions, ROUND( COUNT(CASE WHEN device_type = 'mobile' THEN 1 END) * 100.0 / COUNT(*), 1 ) AS mobile_pct FROM user_sessions WHERE session_date >= CURRENT_DATE - INTERVAL '30 days' GROUP BY user_id HAVING COUNT(*) >= 5 -- Only users with meaningful activity ORDER BY mobile_pct DESC; ``` CASE ile koşullu toplama, temel bir veri analisti becerisidir. [PostgreSQL koşullu ifadeler belgeleri](https://www.postgresql.org/docs/current/functions-conditional.html), bu sözdizimini basitleştiren FILTER ifadeleri gibi ek kalıpları kapsar. ## PostgreSQL'de CROSSTAB ile Dinamik Pivot PostgreSQL'in `tablefunc` uzantısı, pivot kategorilerinin sorgu yazma zamanında bilinmediği durumlar için CROSSTAB sağlar. Mülakatlarda daha az yaygın olsa da, ev ödevi projelerinde karşımıza çıkar. ```sql -- crosstab_dynamic_pivot.sql -- Enable the extension (once per database) CREATE EXTENSION IF NOT EXISTS tablefunc; -- Revenue by product and quarter using CROSSTAB SELECT * FROM crosstab( $$ SELECT product_name, 'Q' || EXTRACT(QUARTER FROM order_date)::TEXT AS quarter, SUM(quantity * unit_price) AS revenue FROM order_items oi JOIN orders o ON oi.order_id = o.order_id JOIN products p ON oi.product_id = p.product_id WHERE EXTRACT(YEAR FROM o.order_date) = 2026 GROUP BY product_name, quarter ORDER BY product_name, quarter $$, $$ VALUES ('Q1'), ('Q2'), ('Q3'), ('Q4') $$ ) AS pivot_table( product_name TEXT, q1_revenue NUMERIC, q2_revenue NUMERIC, q3_revenue NUMERIC, q4_revenue NUMERIC ); ``` CROSSTAB, kaynak sorgunun tam olarak üç sütun döndürmesini gerektirir: satır tanımlayıcı, kategori ve değer. İkinci argüman, tüm olası kategori değerlerini listeler. Eksik kombinasyonlar 0 yerine NULL üretir. ## SQL Sorgu Optimizasyonu: EXPLAIN Planları ve Maliyet Analizi Sorgu optimizasyonu soruları, adayın yavaş sorguları teşhis edip edemediğini test eder — sadece doğru olanları yazıp yazamadığını değil. Birincil tanı aracı, sorguyu yürüten ve gerçek yürütme sürelerini, satır sayılarını ve planlayıcının seçtiği operasyonları raporlayan EXPLAIN ANALYZE'dir. ```sql -- explain_analyze_example.sql -- Analyze a slow query to identify bottlenecks EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) SELECT c.customer_id, c.name, COUNT(o.order_id) AS order_count, SUM(o.total_amount) AS lifetime_value FROM customers c JOIN orders o ON c.customer_id = o.customer_id WHERE o.order_date >= '2025-01-01' GROUP BY c.customer_id, c.name HAVING SUM(o.total_amount) > 1000 ORDER BY lifetime_value DESC; ``` Mülakatçıların sorduğu EXPLAIN çıktısındaki temel unsurlar: - **Seq Scan** — tam tablo taraması. Küçük tablolarda kabul edilebilir; milyonlarca satırda sorunludur. Eksik bir indeksin göstergesidir. - **Index Scan / Index Only Scan** — sorgu bir indeks kullanıyor. Index Only Scan daha hızlıdır çünkü tabloya dokunmadan doğrudan indeksten veri okur. - **Nested Loop / Hash Join / Merge Join** — birleştirme stratejisi. Hash Join büyük sırasız veri kümeleri için uygundur. Merge Join, her iki taraf önceden sıralı olduğunda optimaldir. Nested Loop, indeksli iç tablolarla birleştirilen küçük dış tablolar için çalışır. - **Rows** — tahmini ve gerçek satır sayıları. Büyük tutarsızlıklar, güncel olmayan istatistikleri (ANALYZE çalıştırın) veya zayıf seçiciliğe sahip koşulları gösterir. - **Buffers: shared hit / read** — önbellek isabet oranı. Yüksek read sayıları, verilerin bellekte olmadığını gösterir. [PostgreSQL EXPLAIN belgeleri](https://www.postgresql.org/docs/current/sql-explain.html) tüm mevcut çıktı formatlarını ve seçenekleri kapsar. ## Mülakat Düzeyinde SQL Optimizasyonu İçin İndeksleme Stratejileri İndeksleme, en etkili optimizasyon tekniğidir ve sık sorulan bir mülakat konusudur. Soru genellikle şu formu alır: "Bu yavaş sorgu verildiğinde, hangi indeksler onu iyileştirir?" Üç indeksleme kalıbı, mülakat senaryolarının çoğunu kapsar: **1. Yüksek seçiciliğe sahip WHERE ifadelerinde tek sütunlu indeks** ```sql -- indexing_strategies.sql -- Index on order_date: filters a small percentage of rows CREATE INDEX idx_orders_date ON orders (order_date); -- Composite index for queries filtering on both columns CREATE INDEX idx_orders_customer_date ON orders (customer_id, order_date); -- Covering index: includes columns needed in SELECT -- Enables Index Only Scan (no table access) CREATE INDEX idx_orders_covering ON orders (customer_id, order_date) INCLUDE (total_amount, status); ``` **2. Bileşik indeks sütun sırası önemlidir.** En soldaki sütun, sorgunun WHERE ifadesiyle eşleşmelidir. `(customer_id, order_date)` üzerindeki bir indeks, yalnızca `customer_id` veya `customer_id AND order_date` ile filtreleyen sorguları hızlandırır, ancak yalnızca `order_date` ile filtreleyen sorguları HIZLANDIRMAZ. **3. Kısmi indeksler**, yalnızca bir satır alt kümesi sıklıkla sorgulandığında indeks boyutunu ve bakım maliyetini azaltır. ```sql -- partial_index.sql -- Only index active orders (smaller, faster index) CREATE INDEX idx_orders_active ON orders (customer_id, order_date) WHERE status = 'active'; -- Only index recent data for dashboard queries CREATE INDEX idx_orders_recent ON orders (order_date) WHERE order_date >= '2026-01-01'; ``` > **İndeks Anti-Kalıpları** > > Her sütunu indekslemekten kaçının — her indeks INSERT, UPDATE ve DELETE işlemlerini yavaşlatır. Yoğun yazma iş yüklerinde 10'dan fazla indekse sahip bir tablo, belirgin performans düşüşü yaşar. Ayrıca, bileşik veya kısmi indeksin parçası olmadıkça düşük kardinaliteye sahip sütunları (boolean bayrakları gibi) indekslemekten kaçının. ## Performansı Öldüren Yaygın Sorgu Anti-Kalıpları Mülakatçılar, kasıtlı olarak yavaş sorgular sunar ve adaylardan sorunu tespit etmelerini ister. Bu beş anti-kalıp, gerçek dünya performans sorunlarının çoğunu oluşturur. **İndeksli sütunlar üzerinde fonksiyonlar** — indeksli bir sütuna fonksiyon uygulamak, indeks kullanımını engeller. ```sql -- anti_patterns.sql -- BAD: function on indexed column disables the index SELECT * FROM orders WHERE EXTRACT(YEAR FROM order_date) = 2026; -- GOOD: range comparison uses the index SELECT * FROM orders WHERE order_date >= '2026-01-01' AND order_date < '2027-01-01'; ``` **Üretim sorgularında SELECT *** — büyük TEXT veya BYTEA alanları dahil tüm sütunları getirir. Yalnızca gereken sütunları belirtin. **NULL'larla NOT IN** — alt sorgu herhangi bir NULL döndürürse, NOT IN hiçbir satır döndürmez. NOT EXISTS ile değiştirin. ```sql -- not_in_null_trap.sql -- BAD: returns empty if any customer_id is NULL in subquery SELECT * FROM customers WHERE customer_id NOT IN ( SELECT customer_id FROM orders ); -- GOOD: NOT EXISTS handles NULLs correctly SELECT * FROM customers c WHERE NOT EXISTS ( SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id ); ``` **Örtülü tür dönüşümü** — bir VARCHAR sütununu bir tamsayıyla karşılaştırmak, her satırda dönüşüm yapmaya zorlar ve indeksi atlar. Her zaman türleri açıkça eşleştirin. **Keşif sorgularında eksik LIMIT** — yalnızca ilk 10 satır gerektiğinde milyonlarca satırı taramak. Geliştirme sırasında ve sayfalanmış API sorgularında her zaman LIMIT ekleyin. > **Bu Kalıpları Pratik Edin** > > [SQL alt sorguları ve CTE'ler](/technologies/data-analytics/interview-questions/sql-subqueries-ctes) konusunda yetkinlik kazanmak için gerçek mülakat senaryoları üzerinde çalışın. Alt sorgu yeniden yapılandırmasını EXPLAIN analiziyle birleştirmek, mülakatçıların aradığı tanısal sezgiyi geliştirir. ## Sonuç - Korelasyonlu alt sorgular satır başına yeniden yürütülür — büyük veri kümelerinde daha iyi performans için CTE'lere veya pencere fonksiyonlarına dönüştürün - EXISTS, üyelik kontrolleri için IN'den daha iyi performans gösterir, özellikle alt sorgu çok sayıda satır döndürdüğünde veya NULL'lar içerdiğinde - CASE ile koşullu toplama, taşınabilir pivot tekniğidir — veritabanına özgü PIVOT sözdiziminden önce bunu öğrenin - EXPLAIN ANALYZE gerçek yürütme planını ortaya koyar; Seq Scan, satır tahmin doğruluğu ve birleştirme stratejisine odaklanın - Bileşik indeks sütun sırası, sorgu filtre kalıplarıyla eşleşmelidir — en sol önek kuralı geçerlidir - Kısmi ve kapsayıcı indeksler, tam tablo indekslerinin yükü olmadan hedeflenen sorgu kalıpları için I/O'yu azaltır - İndeksli sütunlarda fonksiyonlardan, NULL'larla NOT IN'den ve üretim sorgularında SELECT *'dan kaçının - Çok adımlı analitik soruları ele almak için [pencere fonksiyonlarını](/blog/data-analytics/sql-window-functions-ctes-advanced-queries) alt sorgu optimizasyonuyla birleştirmeyi pratik edin --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/tr/blog/data-analytics/advanced-sql-data-analyst-interviews