SQL ile Veri Analizi: Temel Sorgulardan Window Functions'a

SELECT'ten window functions'a uzanan SQL yapılarını, gerçek hayattan veri analizi örnekleriyle adım adım inceliyorum.

  • Araçlar

Veri analistliği söz konusu olduğunda SQL, en temel ve en sık kullanılan araçlardan biri. Çünkü analiz başlamadan önce çoğu zaman yapılması gereken ilk iş, veriyi bulunduğu kaynaktan doğru biçimde çekmek ve anlamlı bir yapıya dönüştürmek.

SQL öğrenirken yalnızca sorgu yazmayı değil, hangi soruyu hangi SQL yapısıyla çözebileceğimizi anlamak daha önemli. Bu yazıda en sık kullanılan SQL yapılarını gerçek hayattan örneklerle ele alıyorum.

“SQL'in gücü yalnızca veriyi getirmesinde değil, doğru soruyu verinin içinden çıkarabilmesindedir.”

SQL Veri Analizinde Neden Önemli?

Bir şirketin satış, müşteri, ürün, sipariş veya sosyal medya verilerinin büyük bölümünün veritabanlarında tutulduğunu düşünelim. Veri analistinin görevi çoğu zaman bu ham kayıtların içinden, sorulan iş sorusuna cevap verecek veriyi çıkarmaktır.

01 — Sorgu

Doğru veriyi seçmek

Binlerce veya milyonlarca kaydın içinden yalnızca ihtiyaç duyulan kısmı almak.

02 — Filtre

Koşulları uygulamak

Tarih, kategori, müşteri veya kanal gibi kriterlerle veriyi daraltmak.

03 — Özet

Veriyi toplulaştırmak

Satış, adet, müşteri sayısı gibi metrikleri gruplar bazında hesaplamak.

04 — İçgörü

Sonucu yorumlamak

SQL çıktısını, iş problemine cevap veren analitik bir sonuca dönüştürmek.

Sorgudan içgörüye
  1. SELECT
  2. WHERE
  3. GROUP BY
  4. JOIN
  5. Analiz

1. SELECT — Hangi Veriye İhtiyacımız Var?

SQL öğrenirken ilk yapı SELECT. Bu komut, sorgudan hangi sütunları görmek istediğimizi belirler.

Basit SELECT örneği
SELECT
    musteri_id,
    urun,
    satis
FROM siparisler;

Tek bir sütun seçebilir ya da veri setindeki tüm alanları alabiliriz:

Tek sütun ve tüm alanlar
SELECT urun
FROM siparisler;

-- Tüm sütunlar
SELECT *
FROM siparisler;
Pratik not: Analiz amacıyla yalnızca ihtiyaç duyduğunuz sütunları seçmek, sorguyu daha anlaşılır hale getirmenin yanında gereksiz veri taşınmasını da azaltır.

2. WHERE — Veriyi Filtrelemek

Belirli bir koşulu sağlayan kayıtları almak için WHERE kullanılır.

Tek ve çoklu koşul
SELECT *
FROM siparisler
WHERE satis > 5000;

-- Birden fazla koşul
SELECT *
FROM siparisler
WHERE satis > 5000
  AND kategori = 'Elektronik';

Bu yapı özellikle “Belirli bir dönemde belirli kategoride ne oldu?” gibi soruların ilk adımını oluşturur.

3. GROUP BY — Veriyi Gruplara Ayırmak

Analizde çoğu zaman tek tek kayıtları değil, grupları karşılaştırmak isteriz. Örneğin kategori bazında toplam satış:

Grup bazında metrikler
SELECT
    kategori,
    SUM(satis) AS toplam_satis
FROM siparisler
GROUP BY kategori;

-- Şehir bazında benzersiz müşteri sayısı
SELECT
    sehir,
    COUNT(DISTINCT musteri_id) AS musteri_sayisi
FROM siparisler
GROUP BY sehir;
FonksiyonNe için kullanılır?
SUM()Toplam değer
COUNT()Kayıt sayısı
AVG()Ortalama
MIN()Minimum değer
MAX()Maksimum değer

4. HAVING — Gruplanmış Veriyi Filtrelemek

WHERE kayıtları filtrelerken, HAVING grup sonuçlarını filtrelemek için kullanılır.

Grup sonuçlarını filtrelemek
SELECT
    kategori,
    SUM(satis) AS toplam_satis
FROM siparisler
GROUP BY kategori
HAVING SUM(satis) > 100000;
Akılda tutmak için: WHERE = satırları filtrele, HAVING = grupları filtrele.

5. JOIN — Farklı Tablolardan Veri Birleştirmek

Gerçek hayattaki veriler çoğu zaman tek tabloda bulunmaz. Müşteri bilgileri ayrı, siparişler ayrı, ürün bilgileri başka bir tabloda tutulabilir.

INNER JOIN örneği
SELECT
    s.siparis_id,
    m.musteri_adi,
    s.satis
FROM siparisler s
INNER JOIN musteriler m
    ON s.musteri_id = m.musteri_id;
INNER JOIN

Kesişim

Her iki tabloda eşleşen kayıtları getirir.

LEFT JOIN

Sol tablo öncelikli

Sol tablodaki tüm kayıtları ve eşleşen sağ kayıtları getirir.

RIGHT JOIN

Sağ tablo öncelikli

Sağ tablodaki tüm kayıtları ve eşleşen sol kayıtları getirir.

FULL JOIN

Tüm kayıtlar

Her iki tablodaki eşleşen ve eşleşmeyen kayıtları bir araya getirir.

6. CASE WHEN — Veriyi Sınıflandırmak

Analizde ham sayıları anlamlı kategorilere dönüştürmek sık karşılaşılan bir ihtiyaç. CASE WHEN bunun için çok kullanışlı.

Satış seviyesi segmentasyonu
SELECT
    musteri_id,
    satis,
    CASE
        WHEN satis >= 10000 THEN 'Yüksek'
        WHEN satis >= 5000 THEN 'Orta'
        ELSE 'Düşük'
    END AS satis_seviyesi
FROM siparisler;

Bu yaklaşım müşteri segmentasyonu, performans sınıflandırması, risk grupları veya kampanya segmentleri gibi birçok analizde kullanılabilir.

7. Birkaç Yapıyı Birlikte Kullanmak

Asıl analitik güç, tek tek komutlardan ziyade bu yapıların birlikte kullanılmasında ortaya çıkıyor.

Gerçek hayat satış analizi
SELECT
    kategori,
    COUNT(*) AS siparis_sayisi,
    SUM(satis) AS toplam_satis,
    AVG(satis) AS ortalama_satis
FROM siparisler
WHERE tarih >= '2025-01-01'
GROUP BY kategori
HAVING SUM(satis) > 50000
ORDER BY toplam_satis DESC;

Bu sorgu aslında tek bir komut değil, küçük bir analitik hikâye anlatıyor: belirli dönemi seç, kategorileri grupla, metrikleri hesapla, düşük hacimli grupları ele ve sonucu sırala.

8. Window Functions — Satırları Kaybetmeden Analiz

Window Functions, SQL'in veri analizi açısından en güçlü yapılarından biri. Toplulaştırma yaparken satırları kaybetmeden her satıra ek bilgi hesaplamamızı sağlar.

Kategori içinde sıralama
SELECT
    kategori,
    urun,
    satis,
    RANK() OVER (
        PARTITION BY kategori
        ORDER BY satis DESC
    ) AS kategori_sirasi
FROM urun_satislari;

Burada her kategori kendi içinde sıralanıyor. Böylece “Her kategorinin en çok satan ürünleri hangileri?” sorusuna cevap verebiliriz.

ROW_NUMBER()

Müşterinin son siparişi
ROW_NUMBER() OVER (
    PARTITION BY musteri_id
    ORDER BY tarih DESC
) AS son_siparis_sirasi

Bu yapı bir müşterinin siparişlerini tarihe göre sıralamak ve örneğin son siparişi bulmak için kullanılabilir.

LAG() ve LEAD()

Önceki satırın değeri
LAG(satis) OVER (
    ORDER BY tarih
) AS onceki_satis

LAG() önceki satırdaki değeri, LEAD() ise sonraki satırdaki değeri görmemizi sağlar. Zaman serilerinde dönemsel değişimleri analiz etmek için çok kullanışlıdır.

9. Gerçek Bir Analiz: Aylık Satış Değişimi

Örneğin aylık satışları hesaplayıp önceki ayla karşılaştırmak istediğimizi düşünelim.

CTE ve LAG ile aylık karşılaştırma
WITH aylik_satis AS (
    SELECT
        DATE_TRUNC('month', tarih) AS ay,
        SUM(satis) AS toplam_satis
    FROM siparisler
    GROUP BY DATE_TRUNC('month', tarih)
)

SELECT
    ay,
    toplam_satis,
    LAG(toplam_satis) OVER (
        ORDER BY ay
    ) AS onceki_ay_satis
FROM aylik_satis
ORDER BY ay;

Bu sonuç daha sonra değişim oranı, büyüme trendi veya dashboard KPI'ları için kullanılabilir.

10. SQL'de Analitik Düşünmek

Bir veri analistinin SQL kullanırken yalnızca “Bu sorguyu nasıl yazarım?” diye düşünmesi yeterli değil. Daha önemli olan, soruyu parçalara ayırmak.

1. Adım

Ne görmek istiyorum?

Hangi metriği veya değişkeni analiz edeceğini belirle.

2. Adım

Veri nerede?

Gerekli tabloları ve aralarındaki ilişkileri bul.

3. Adım

Kapsam ne?

Tarih, segment, kanal veya diğer filtreleri tanımla.

4. Adım

Nasıl gruplayacağım?

İş sorusuna göre kategori, müşteri, dönem veya kanal seç.

5. Adım

Hangi hesap?

SUM, COUNT, AVG veya window function gibi doğru yapıyı seç.

6. Adım

Sonuç mantıklı mı?

Çıktıyı kontrol et ve iş bağlamında yorumla.

SQL Öğrenirken Sık Yapılan Hatalar

HataNeden problem?Daha iyi yaklaşım
SELECT * her yerde kullanmakGereksiz sütunlar gelir.İhtiyaç duyulan alanları seçmek.
JOIN mantığını kontrol etmemekKayıt sayısı beklenmedik şekilde çoğalabilir.İlişkinin cardinality'sini kontrol etmek.
WHERE ve HAVING'i karıştırmakSorgu mantığı yanlış kurulur.Satır filtresi / grup filtresi ayrımını düşünmek.
NULL değerleri göz ardı etmekHesaplamalar beklenenden farklı çıkabilir.NULL davranışını ve COALESCE gibi yapıları kontrol etmek.
Sonucu doğrulamamakTeknik olarak çalışan ama yanlış bir analiz oluşabilir.Örnek kayıtlar ve bağımsız kontroller yapmak.

SQL'de En Çok Kullanılan Yapılar

Temel

SELECT + WHERE

Veriyi seç ve ihtiyacın olmayan kayıtları filtrele.

Özet

GROUP BY + HAVING

Veriyi gruplandır, metrikleri hesapla ve grupları filtrele.

Birleştirme

JOIN

Farklı tablolardan gelen bilgileri anlamlı biçimde birleştir.

Mantık

CASE WHEN

Kurallara göre yeni kategoriler ve segmentler oluştur.

İleri analiz

Window Functions

Sıralama, önceki/sonraki değer ve kümülatif analizler yap.

Sıralama

ORDER BY

Sonuçları analizin ihtiyacına göre düzenle.

Sonuç

SQL, veri analistliği için yalnızca teknik bir beceri değil; veriye soru sormanın temel yollarından biri. SELECT, WHERE, GROUP BY, HAVING, JOIN ve CASE WHEN gibi yapılar sağlam bir temel oluştururken, window functions daha ileri analizlerde önemli bir esneklik sağlıyor.

SQL öğrenirken hedef yüzlerce sorguyu ezberlemek olmamalı. Bunun yerine bir iş sorusunu alıp onu küçük parçalara ayırmayı ve her parçayı doğru SQL yapısıyla çözmeyi öğrenmek çok daha değerli.

“İyi SQL sadece çalışan sorgu değildir; doğru soruya, doğru veriden, güvenilir bir cevap üreten sorgudur.”

When it comes to working as a data analyst, SQL is one of the most fundamental and most frequently used tools. Because before any analysis starts, the first job is usually to pull the data correctly from where it lives and turn it into a meaningful shape.

While learning SQL, understanding which question can be solved with which SQL structure matters more than simply writing queries. In this post I go through the most commonly used SQL structures with real-life examples.

“The power of SQL is not only in fetching data, but in being able to pull the right question out of it.”

Why Does SQL Matter in Data Analysis?

Think of a company where most of the sales, customer, product, order or social media data is kept in databases. The data analyst's job is usually to extract, from those raw records, the data that answers the business question being asked.

01 — Query

Selecting the right data

Taking only the part you need out of thousands or millions of records.

02 — Filter

Applying conditions

Narrowing the data with criteria such as date, category, customer or channel.

03 — Summary

Aggregating the data

Calculating metrics such as sales, quantity or customer count by group.

04 — Insight

Interpreting the result

Turning the SQL output into an analytical answer to the business problem.

From query to insight
  1. SELECT
  2. WHERE
  3. GROUP BY
  4. JOIN
  5. Analysis

1. SELECT — Which Data Do We Need?

The first structure you meet in SQL is SELECT. It defines which columns you want to see in the result.

A simple SELECT example
SELECT
    customer_id,
    product,
    sales
FROM orders;

You can select a single column, or take every field in the data set:

One column and all fields
SELECT product
FROM orders;

-- All columns
SELECT *
FROM orders;
Practical note: Selecting only the columns you need makes the query easier to read and also reduces unnecessary data movement.

2. WHERE — Filtering the Data

WHERE is used to take only the records that satisfy a given condition.

Single and multiple conditions
SELECT *
FROM orders
WHERE sales > 5000;

-- More than one condition
SELECT *
FROM orders
WHERE sales > 5000
  AND category = 'Electronics';

This structure is the first step of questions such as “What happened in a given category during a given period?”.

3. GROUP BY — Splitting the Data into Groups

In analysis we usually want to compare groups rather than individual records. Total sales by category, for example:

Metrics by group
SELECT
    category,
    SUM(sales) AS total_sales
FROM orders
GROUP BY category;

-- Unique customers by city
SELECT
    city,
    COUNT(DISTINCT customer_id) AS customer_count
FROM orders
GROUP BY city;
FunctionWhat is it used for?
SUM()Total value
COUNT()Number of records
AVG()Average
MIN()Minimum value
MAX()Maximum value

4. HAVING — Filtering Grouped Data

While WHERE filters records, HAVING is used to filter group results.

Filtering group results
SELECT
    category,
    SUM(sales) AS total_sales
FROM orders
GROUP BY category
HAVING SUM(sales) > 100000;
An easy way to remember: WHERE = filter the rows, HAVING = filter the groups.

5. JOIN — Combining Data from Different Tables

Real-life data rarely sits in a single table. Customer information, orders and product details are often kept in separate tables.

An INNER JOIN example
SELECT
    o.order_id,
    c.customer_name,
    o.sales
FROM orders o
INNER JOIN customers c
    ON o.customer_id = c.customer_id;
INNER JOIN

Intersection

Returns the records that match in both tables.

LEFT JOIN

Left table first

Returns all records from the left table plus the matching right ones.

RIGHT JOIN

Right table first

Returns all records from the right table plus the matching left ones.

FULL JOIN

All records

Brings together both matching and non-matching records from both tables.

6. CASE WHEN — Classifying the Data

Turning raw numbers into meaningful categories is a common need in analysis, and CASE WHEN is very handy for it.

Sales level segmentation
SELECT
    customer_id,
    sales,
    CASE
        WHEN sales >= 10000 THEN 'High'
        WHEN sales >= 5000 THEN 'Medium'
        ELSE 'Low'
    END AS sales_level
FROM orders;

The same approach can be used in customer segmentation, performance classification, risk groups or campaign segments.

7. Using Several Structures Together

The real analytical power appears when these structures are used together rather than one by one.

A real-life sales analysis
SELECT
    category,
    COUNT(*) AS order_count,
    SUM(sales) AS total_sales,
    AVG(sales) AS average_sales
FROM orders
WHERE order_date >= '2025-01-01'
GROUP BY category
HAVING SUM(sales) > 50000
ORDER BY total_sales DESC;

This query is not really a single command; it tells a small analytical story: pick the period, group the categories, calculate the metrics, drop the low-volume groups and sort the result.

8. Window Functions — Analysis Without Losing Rows

Window functions are one of SQL's most powerful structures for data analysis. They let you calculate extra information for every row while aggregating, without losing the rows themselves.

Ranking inside a category
SELECT
    category,
    product,
    sales,
    RANK() OVER (
        PARTITION BY category
        ORDER BY sales DESC
    ) AS category_rank
FROM product_sales;

Here every category is ranked within itself, so we can answer “Which are the best-selling products of each category?”.

ROW_NUMBER()

A customer's latest order
ROW_NUMBER() OVER (
    PARTITION BY customer_id
    ORDER BY order_date DESC
) AS last_order_rank

This structure can be used to sort a customer's orders by date and find the most recent one, for example.

LAG() and LEAD()

The value of the previous row
LAG(sales) OVER (
    ORDER BY order_date
) AS previous_sales

LAG() shows the value in the previous row and LEAD() the value in the next one. They are very useful for analysing period-over-period change in time series.

9. A Real Analysis: Monthly Sales Change

Imagine we want to calculate monthly sales and compare each month with the previous one.

Monthly comparison with a CTE and LAG
WITH monthly_sales AS (
    SELECT
        DATE_TRUNC('month', order_date) AS month,
        SUM(sales) AS total_sales
    FROM orders
    GROUP BY DATE_TRUNC('month', order_date)
)

SELECT
    month,
    total_sales,
    LAG(total_sales) OVER (
        ORDER BY month
    ) AS previous_month_sales
FROM monthly_sales
ORDER BY month;

This result can then be used for change rates, growth trends or dashboard KPIs.

10. Thinking Analytically in SQL

It is not enough for a data analyst to ask only “How do I write this query?”. Breaking the question into pieces matters more.

Step 1

What do I want to see?

Decide which metric or variable you are going to analyse.

Step 2

Where is the data?

Find the tables you need and the relationships between them.

Step 3

What is the scope?

Define the date, segment, channel and any other filters.

Step 4

How will I group it?

Choose category, customer, period or channel according to the business question.

Step 5

Which calculation?

Pick the right structure: SUM, COUNT, AVG or a window function.

Step 6

Does the result make sense?

Check the output and interpret it in its business context.

Common Mistakes When Learning SQL

MistakeWhy is it a problem?Better approach
Using SELECT * everywhereUnnecessary columns come along.Select only the fields you need.
Not checking the JOIN logicThe number of records can multiply unexpectedly.Check the cardinality of the relationship.
Mixing up WHERE and HAVINGThe query logic ends up wrong.Think about the row filter / group filter distinction.
Ignoring NULL valuesCalculations can differ from what you expect.Check NULL behaviour and structures such as COALESCE.
Not validating the resultAn analysis can run technically and still be wrong.Look at sample records and run independent checks.

The Most Used Structures in SQL

Basics

SELECT + WHERE

Select the data and filter out the records you do not need.

Summary

GROUP BY + HAVING

Group the data, calculate the metrics and filter the groups.

Combining

JOIN

Bring together information from different tables in a meaningful way.

Logic

CASE WHEN

Create new categories and segments based on rules.

Advanced

Window functions

Do ranking, previous/next value and cumulative analyses.

Ordering

ORDER BY

Arrange the results according to what the analysis needs.

Conclusion

SQL is not only a technical skill for a data analyst; it is one of the basic ways of asking questions of data. Structures such as SELECT, WHERE, GROUP BY, HAVING, JOIN and CASE WHEN build a solid foundation, while window functions add important flexibility for more advanced analyses.

The goal while learning SQL should not be memorising hundreds of queries. It is far more valuable to take a business question, break it into small pieces and learn to solve each piece with the right SQL structure.

“Good SQL is not just a query that runs; it is a query that produces a reliable answer to the right question from the right data.”

Veri Analistinin Bir Günlük Çalışma Akışı

Python ile veri görselleştirme yöntemleri

Python ile veri görselleştirme yöntemleri

Big Data (Büyük Veri) Nedir?