Ana içeriğe geç
Kesit SQL
Orta · Veri Mühendisliği

Tekilleştirme (deduplication)

Tam ve mantıksal tekrarları ROW_NUMBER ile temizlemek.

Uygulamada aç, 5 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ç.

İki tür tekrar

WITH s AS (
  SELECT *,
         LOWER(TRIM(email)) AS email_norm,
         ROW_NUMBER() OVER (PARTITION BY LOWER(TRIM(email)) ORDER BY id) AS rn
  FROM raw_signups
)
SELECT id, email_norm, TRIM(full_name) AS ad, signup_date
FROM s WHERE rn = 1;

“Hangi kayıt kalsın?” sorusu iş kuralıdır: en yeni, en eksiksiz, ilk gelen… ORDER BY ifadesi bu kuralı kodlar.

Tablodan silerek tekilleştirme

DELETE FROM raw_signups
WHERE rowid NOT IN (
  SELECT MIN(rowid) FROM raw_signups GROUP BY id, full_name, email, city, signup_date
);
SELECT COUNT(*) FROM raw_signups;

PostgreSQL'de ctid, diğerlerinde CTE + ROW_NUMBER ile benzer yapılır.

Derinleş: “hangi kayıt kalır?” kuralı

Tekilleştirmenin teknik kısmı kolaydır: ROW_NUMBER() OVER (PARTITION BY anahtar ORDER BY kural) ve rn = 1. Zor kısım iki iş kararıdır: anahtar (iki kayıt ne zaman aynı varlıktır?) ve hayatta kalma kuralı (survivorship: hangisi kalır?).

Anahtar seçimi

Ad soyad iyi bir anahtar değildir: Lezzet Kurye'de 185 ad–şehir çifti birden fazla müşteri kaydında geçiyor ve bunların çoğu gerçekten farklı kişiler (yaygın adlar). E-posta daha güçlüdür ama onun da tuzağı var: raw_signups tablosunda can.kaya@gmail.com iki farklı id (8 ve 33) altında görünüyor. E-postayı anahtar yaparsan bu iki kaydı birleştirirsin; doğru olup olmadığını kaynağa sormak gerekir.

Hayatta kalma kuralı

KuralORDER BYNe zaman?
İlk gelen kalırcreated_at, idMüşteri kimliğinin ilk kaydı esas
En yeni kalırupdated_at DESC, id DESCAdres, telefon gibi güncel bilgi önemli
En eksiksiz kalırCASE WHEN city IS NULL THEN 1 ELSE 0 END, idKaynaklar farklı alanları dolduruyor
Güvenilir kaynak kalırCASE source WHEN 'crm' THEN 1 ELSE 2 ENDBirden çok sistem birleşiyor

ORDER BY'ın sonunda her zaman benzersiz bir sütun (eşitlik bozucu) bulunsun; yoksa aynı sorgu iki çalıştırmada farklı satırı seçebilir.

RANK tuzağı

WITH s AS (
  SELECT LOWER(TRIM(email)) AS e,
         ROW_NUMBER() OVER (PARTITION BY LOWER(TRIM(email)) ORDER BY id) AS rn,
         RANK()       OVER (PARTITION BY LOWER(TRIM(email)) ORDER BY id) AS rk
  FROM raw_signups
)
SELECT SUM(CASE WHEN rn = 1 THEN 1 ELSE 0 END) AS row_number_ile,
       SUM(CASE WHEN rk = 1 THEN 1 ELSE 0 END) AS rank_ile,
       COUNT(DISTINCT e) AS tekil_email
FROM s;

39 tekil e-posta var; ROW_NUMBER 39 satır bırakır, RANK ise 51. Tam tekrar satırlar aynı id'ye sahip olduğu için RANK hepsine 1 verir ve tekrarlar yerinde kalır.

Birleştirmenin bedeli

Kanat Havayolları'nda 33 e-posta birden fazla yolcu kaydında geçiyor. En eski kaydı (en küçük joined_date) bırakırsak 34 yolcu kaydı birleşir ve bunlara bağlı 172 rezervasyonun passenger_id'si kalan kayda taşınmalıdır. Tekilleştirme yalnızca bir tabloyu değil, ona bakan tüm tabloları etkiler.

SQL türlerine göre kısayollarPostgreSQL: SELECT DISTINCT ON (email) ... ORDER BY email, joined_date. BigQuery ve Snowflake: QUALIFY ROW_NUMBER() OVER (...) = 1. Diğerlerinde CTE + ROW_NUMBER kalıbı her yerde çalışır.
Alıştırmalara geç (5)