7 puan yazan GN⁺ 2024-09-26 | 2 yorum | WhatsApp'ta paylaş

İçindekiler

Biçimlendirme/okunabilirlik

  • Alanları ayırırken başta virgül kullanın
  • WHERE koşulunda kukla değer kullanın
  • Uygun kod girintilemesi
  • Karmaşık sorgular yazarken CTE kullanmayı düşünün

Yararlı özellikler

  • Veri türü dönüştürmek için :: operatörünü kullanın
  • Anti join kullanın
  • Pencere fonksiyonu filtrelemesinde QUALIFY kullanın
  • GROUP BY için sütun konumu kullanılabilir

Kaçınılması gereken tuzaklar

  • NULL değerlerle NOT IN kullanırken dikkat edin
  • Belirsizliği önlemek için hesaplanmış alan adlarını değiştirin
  • Her sütunun hangi tabloya ait olduğunu belirtin
  • Çalıştırma sırasını anlayın
  • Koda yorum ekleyin
  • Belgelerin tamamını okuyun

Biçimlendirme/okunabilirlik

Alanları ayırırken başta virgül kullanın

  • SELECT bölümünde alanları ayırırken başta virgül kullanarak yeni sütunları daha net ayırabilirsiniz
  • Baştaki virgül, virgülün eksik olup olmadığını kolayca fark ettiren görsel bir ipucu sağlar
SELECT
  employee_id,
  employee_name,
  job,
  salary
FROM employees;

WHERE koşulunda kukla değer kullanın

  • WHERE koşulunda kukla değer kullanarak koşulları dinamik olarak ekleyip kaldırabilirsiniz
SELECT *
FROM employees
WHERE 1=1 -- kukla değer
  AND job IN ('Clerk', 'Manager')
  AND dept_no != 5;

Uygun kod girintilemesi

  • Kodu girintileyerek okunabilirliği artırabilir ve ekip arkadaşlarınız ile gelecekteki kendiniz için kodu anlamayı kolaylaştırabilirsiniz
-- kötü örnek:
SELECT
  timeslot_date,
  timeslot_channel,
  overnight_fta_share,
  IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) > 7,
    LAG(overnight_fta_share, 1) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),
    NULL) AS C7_fta_share,
  IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) >= 29,
    LAG(overnight_fta_share, 2) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),
    NULL) AS C28_fta_share
FROM timeslot_data;

-- iyi örnek:
SELECT
  timeslot_date,
  timeslot_channel,
  overnight_fta_share,
  IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) > 7,
    LAG(overnight_fta_share, 1) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),
    NULL) AS C7_fta_share,
  IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) >= 29,
    LAG(overnight_fta_share, 2) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),
    NULL) AS C28_fta_share
FROM timeslot_data;

Karmaşık sorgular yazarken CTE kullanmayı düşünün

  • İç içe inline view kullanmak yerine, ortak tablo ifadeleri (CTE) ile kodun okunabilirliğini ve düzenini artırabilirsiniz
-- inline view kullanımı:
SELECT
  vhs.movie,
  vhs.vhs_revenue,
  cs.cinema_revenue
FROM
  (SELECT
    movie_id,
    SUM(ticket_sales) AS cinema_revenue
  FROM tickets
  GROUP BY movie_id) AS cs
INNER JOIN
  (SELECT
    movie,
    movie_id,
    SUM(revenue) AS vhs_revenue
  FROM blockbuster
  GROUP BY movie, movie_id) AS vhs
ON cs.movie_id = vhs.movie_id;

-- CTE kullanımı:
WITH cinema_sales AS (
  SELECT
    movie_id,
    SUM(ticket_sales) AS cinema_revenue
  FROM tickets
  GROUP BY movie_id
),
vhs_sales AS (
  SELECT
    movie,
    movie_id,
    SUM(revenue) AS vhs_revenue
  FROM blockbuster
  GROUP BY movie, movie_id
)
SELECT
  vhs.movie,
  vhs.vhs_revenue,
  cs.cinema_revenue
FROM cinema_sales AS cs
INNER JOIN vhs_sales AS vhs
ON cs.movie_id = vhs.movie_id;

Yararlı özellikler

Veri türü dönüştürmek için :: operatörünü kullanın

  • Bazı RDBMS'lerde :: operatörüyle bir değeri başka bir veri türüne dönüştürebilirsiniz
SELECT CAST('5' AS INTEGER); -- CAST fonksiyonu kullanımı
SELECT '5'::INTEGER; -- :: sözdizimi kullanımı

Anti join kullanın

  • Anti join, yalnızca tek bir tabloda bulunan satırları döndürmek gerektiğinde çok kullanışlıdır
  • Alt sorgu da kullanılabilir, ancak genel olarak anti join daha hızlıdır
-- anti join:
SELECT
  video_content.*
FROM video_content
LEFT JOIN archive
ON video_content.series_id = archive.series_id
WHERE archive.series_id IS NULL;

-- alt sorgu:
SELECT
  *
FROM video_content
WHERE series_id NOT IN (SELECT DISTINCT series_id FROM archive);

-- ilişkili alt sorgu:
SELECT
  *
FROM video_content
WHERE NOT EXISTS (
  SELECT 1
  FROM archive a
  WHERE a.series_id = vc.series_id
);

-- EXCEPT:
SELECT series_id
FROM video_content
EXCEPT
SELECT series_id
FROM archive;

Pencere fonksiyonu filtrelemesinde QUALIFY kullanın

  • QUALIFY ile pencere fonksiyonlarının sonuçlarını filtreleyebilirsiniz
  • Kod satırı sayısını azaltmada faydalıdır
-- QUALIFY kullanımı:
SELECT
  product,
  market,
  SUM(revenue) AS market_revenue
FROM sales
GROUP BY product, market
QUALIFY DENSE_RANK() OVER (PARTITION BY product ORDER BY SUM(revenue) DESC) <= 10
ORDER BY product, market_revenue;

-- QUALIFY olmadan:
SELECT
  product,
  market,
  market_revenue
FROM (
  SELECT
    product,
    market,
    SUM(revenue) AS market_revenue,
    DENSE_RANK() OVER (PARTITION BY product ORDER BY SUM(revenue) DESC) AS market_rank
  FROM sales
  GROUP BY product, market
)
WHERE market_rank <= 10
ORDER BY product, market_revenue;

GROUP BY için sütun konumu kullanılabilir

  • Sütun adı yerine sütun konumu kullanarak GROUP BY veya ORDER BY yapabilirsiniz
  • Geçici sorgular için kullanışlıdır, ancak prodüksiyon kodunda her zaman sütun adlarını referans almalısınız
SELECT
  dept_no,
  SUM(salary) AS dept_salary
FROM employees
GROUP BY 1 -- dept_no, SELECT bölümündeki ilk sütundur
ORDER BY 2 DESC;

Kaçınılması gereken tuzaklar

NULL değerlerle NOT IN kullanırken dikkat edin

  • NOT IN, NULL değerler varsa doğru çalışmaz
  • Bunun yerine NOT EXISTS kullanılmalıdır
INSERT INTO departments (id)
VALUES (1), (2), (NULL);

-- NULL değer yüzünden çalışmaz
SELECT *
FROM employees
WHERE department_id NOT IN (SELECT DISTINCT id FROM departments);

-- çözüm
SELECT *
FROM employees e
WHERE NOT EXISTS (
  SELECT 1
  FROM departments d
  WHERE d.id = e.department_id
);

Belirsizliği önlemek için hesaplanmış alan adlarını değiştirin

  • Hesaplanmış bir alanı mevcut bir sütun adıyla yeniden adlandırmak beklenmedik davranışlara yol açabilir
INSERT INTO products (product, revenue)
VALUES ('Shark', 100), ('Robot', 150), ('Alien', 90);

-- pencere fonksiyonu 'Robot' ürününü 1. sıraya yerleştirir
SELECT
  product,
  CASE product WHEN 'Robot' THEN 0 ELSE revenue END AS revenue,
  RANK() OVER (ORDER BY revenue DESC)
FROM products;

Her sütunun hangi tabloya ait olduğunu belirtin

  • Karmaşık sorgularda her sütunun hangi tabloya ait olduğunu belirtmek, sorunları izlemeyi kolaylaştırır
SELECT
  vc.video_id,
  vc.series_name,
  metadata.season,
  metadata.episode_number
FROM video_content AS vc
INNER JOIN video_metadata AS metadata
ON vc.video_id = metadata.video_id;

Çalıştırma sırasını anlayın

  • SQL öğrenen biri için en önemli tavsiye, çalıştırma sırasını anlamaktır
  • Çalıştırma sırasını anladığınızda sorgu yazma biçiminiz tamamen değişir

Koda yorum ekleyin

  • Kod yazarken nedenini açıklayan yorumlar eklemelisiniz
  • Ekip arkadaşlarınız ve gelecekteki kendiniz bunun için size teşekkür eder
SELECT
  video_content.*
FROM video_content
LEFT JOIN archive -- yeni CMS arşiv video biçimini işleyemiyor
ON video_content.series_id = archive.series_id
WHERE archive.series_id IS NULL;

Belgelerin tamamını okuyun

  • Belgeleri baştan sona okumak, beklenmedik sorunlardan kaçınmanıza yardımcı olabilir
  • Belgeleri okumak yalnızca birkaç dakikanızı alır ve beklenmedik sorunları çözmenize yardımcı olur
-- belgeleri biraz daha okusaydınız NULL sorununu çözebilirdiniz
SELECT COALESCE(GREATEST(signup_date, consumption_date), signup_date, consumption_date);

-- GREATEST_IGNORE_NULLS fonksiyonu kullanılabilir
SELECT GREATEST_IGNORE_NULLS(signup_date, consumption_date);

GN⁺ özeti

  • Bu yazı, SQL'i daha verimli ve okunabilir yazmak için çeşitli ipuçları ve püf noktaları sunuyor
  • SQL'i yeni öğrenenler ya da deneyimli veri analistleri için çok sayıda yararlı bilgi içeriyor
  • Özellikle karmaşık sorgular yazarken CTE kullanımı, anti join'den yararlanma ve QUALIFY kullanımı pratikte büyük fayda sağlayacaktır
  • SQL'in çalıştırma sırasını anlamak, koda yorum eklemek ve belgeleri dikkatle okuma alışkanlığı edinmek önemlidir
  • Benzer işlevlere sahip diğer araçlar arasında PostgreSQL, MySQL ve Oracle bulunur

2 yorum

 
hiyama 2024-09-26

Bu gönderide baştaki virgüllerin hepsi sondaki virgül olarak yazılmış görünüyor. Orijinal metinde başta yer alıyorlar.

-- Good:  
SELECT   
timeslot_date  
, timeslot_channel   
, overnight_fta_share  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) &gt; 7, -- First argument of IFF.  
	LAG(overnight_fta_share, 1) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity), -- Second argument of IFF.  
		NULL) AS C7_fta_share -- Third argument of IFF.  
, IFF(DATEDIFF(DAY, timeslot_date, CURRENT_DATE()) &gt;= 29,   
		LAG(overnight_fta_share, 2) OVER (PARTITION BY timeslot_date, timeslot_channel ORDER BY timeslot_activity),   
			NULL) AS C28_fta_share  
FROM timeslot_data  
;  
 
GN⁺ 2024-09-26
Hacker News görüşleri
  • DB sunucusunu iyi anlamak ve sorgu planını sık sık kontrol etme gerekliliği

    • EXISTS, çoğu durumda IN'den daha hızlıdır
    • NOT EXISTS ve EXCEPT, NULL değerlerini işlerken farklı davranır
    • Tablo join'i yerine alt sorgu sütunlarının kullanılması önerilir
    • Tablo taramalarından kaçınılmalı ve indeks eklenmelidir
    • İfade filtrelemede hesaplanmış sütunlar ve indeksler kullanılabilir
    • UNION ALL, OR'dan daha hızlı olabilir
    • Alt sorgu JOIN'i ile filtreleme sırası zorlanabilir
  • Karmaşık saklı prosedür işlemleri için ipuçları

    • Kalıcı tabloyu geçici tabloya kopyalayıp yalnızca gerekli satırları filtreleyin
    • Geçici tablo üzerinde işlem yapın
    • Transaction içinde kalıcı tabloyu güncelleyin, hata durumunda rollback yapın
    • Uzak tablolarla çalışırken dikkatli olun; geçici tabloya kopyaladıktan sonra çalışmanız önerilir
    • Sorgu planı kafa karıştırıcı olabileceğinden işi küçük adımlara bölün
    • Sorgu planını her zaman kontrol edin
  • Kod okunabilirliğiyle ilgili görüşler

    • İlk iki örnek, okunabilirlikten ödün verip yazım kolaylığını artırıyor
    • Son örnekte girintileme çok büyük bir etki yaratmıyor
  • SQL'de FROM-first ve piping sözdiziminin kullanılması önerisi

    • Kusto query language kullanma deneyiminin büyük bir ilerleme olduğu belirtiliyor
  • Anti Join ile ilgili ipuçları

    • Koşul tabanlı alt sorgularda satır varlığını kontrol ederken avantajlı olduğu için EXISTS kullanımı önerilir
  • SELECT ifadesinde başta virgül kullanmanın avantajları

    • Tek tek satırları yorum satırına almak mümkündür
    • Kod girintisiyle okunabilirlik artar
  • MSSQL'de yorum kullanırken -- yerine /* */ kullanılması önerilir

    • Bunun nedeni query store'un sorguları satır sonu olmadan saklamasıdır
  • Window function kullanımının önerilmesi

  • WHERE koşulunda 1=1 kullanımı üzerine tartışma

  • AI2sql tanıtımı

    • Düz İngilizce prompt ile SQL sorguları oluşturulabilir
    • Karmaşık sorgular yazarken faydalıdır