Diziler ve Normalleştirilmiş Tablolar
Dizilerin doğru tercih olduğu durumlar.
Diziler ve Normalleştirilmiş Tablolar, CoddyKit'te ücretsiz bir SQL Academy dersidir. Bu, 4 dersinin 4. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, SQL Academy öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. SQL Academy kursu toplamda 4 dersten oluşur.
Birden Çok Değeri Saklamanın İki Yolu
Tek bir satırın birbiriyle ilişkili birden çok değeri tutması gerektiğinde PostgreSQL size iki temel yaklaşım sunar: değerleri aynı satırda bir dizi sütunu olarak saklamak veya her değerin kendi satırına sahip olduğu ayrı bir alt tablo oluşturmak.
Her yaklaşımın ne zaman kullanılacağını anlamak, verimli ve bakımı kolay veritabanları tasarlamak için önemli bir beceridir.
Normalleştirilmiş Yaklaşım
Tamamen normalleştirilmiş bir şemada her veri parçası kendi satırında bulunur. Bir kullanıcının birden çok telefon numarası olabiliyorsa user_phones tablosunu oluşturur ve bu tabloya users tablosuna geri bağlanan bir yabancı anahtar eklersiniz.
Bu, klasik ilişkisel modeldir ve çoğu durumda varsayılan tercihtir.
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE user_phones (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
phone TEXT NOT NULL
);
INSERT INTO users (name) VALUES ('Alice'), ('Bob');
INSERT INTO user_phones (user_id, phone) VALUES
(1, '+1-555-0101'),
(1, '+1-555-0102'),
(2, '+1-555-0200');Dizi Yaklaşımı
PostgreSQL'in TEXT[] türü (veya başka bir türün ardından gelen []), birden çok değeri doğrudan tek bir sütunda saklamanızı sağlar. Ek bir tabloya gerek yoktur.
Aynı telefon numarası verileri, kullanıcı başına tek ve kompakt bir satırda saklanabilir.
CREATE TABLE users_with_phones (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
phones TEXT[]
);
INSERT INTO users_with_phones (name, phones) VALUES
('Alice', ARRAY['+1-555-0101', '+1-555-0102']),
('Bob', ARRAY['+1-555-0200']);Dizileri Sorgulamak Kolaydır
Bir dizi sütununda arama yapmak ANY işleci veya @> (içerir) işleciyle oldukça kolaydır. Basit bir WHERE yan tümcesiyle belirli bir telefon numarasına sahip tüm kullanıcıları bulabilirsiniz.
-- Find users who have a specific phone number
SELECT name
FROM users_with_phones
WHERE '+1-555-0101' = ANY(phones);
-- Or using the array-contains operator
SELECT name
FROM users_with_phones
WHERE phones @> ARRAY['+1-555-0101'];Dizilerin Öne Çıktığı Durumlar: Basit Aramalar
Diziler şu durumlarda güçlü bir tercihtir:
- Değer listesi bir bütün olarak birlikte okunuyorsa (etiketler, adlar, kategoriler)
- Tek tek öğeler üzerinde hiçbir zaman birleştirme yapmanız gerekmiyorsa
- Listenin doğal bir üst sınırı varsa ve liste nadiren parça parça güncelleniyorsa
Klasik bir örnek, etiketleri bir blog gönderisinde saklamaktır. Tüm etiketleri her zaman birlikte alır ve karmaşık bir birleştirmede tek bir etikete göre gönderileri nadiren sorgularsınız.
CREATE TABLE posts (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL,
tags TEXT[]
);
INSERT INTO posts (title, tags) VALUES
('Intro to SQL', ARRAY['sql', 'beginner', 'database']),
('Advanced Indexes', ARRAY['sql', 'performance', 'indexes']),
('NoSQL Overview', ARRAY['nosql', 'beginner']);
-- Get all posts tagged 'beginner'
SELECT title FROM posts
WHERE 'beginner' = ANY(tags);Normalleştirilmiş Tabloların Öne Çıktığı Durumlar: İlişkiler
Normalleştirilmiş tablolar şu durumlarda daha iyi bir tercihtir:
- Tek tek değerlerin kendilerine ait özniteliklere ihtiyacı varsa (örneğin bir telefon numarasının türü vardır: ev/iş)
- Tek tek değerler üzerinde birleştirme yapmanız gerekiyorsa
- Değerler bağımsız olarak ve sık sık değişiyorsa
- Yabancı anahtarlar aracılığıyla başvuru bütünlüğüne ihtiyacınız varsa
-- Phone numbers need a 'type' attribute — array can't do this cleanly
CREATE TABLE user_phones (
id SERIAL PRIMARY KEY,
user_id INT REFERENCES users(id),
phone TEXT NOT NULL,
type TEXT CHECK (type IN ('home', 'work', 'mobile'))
);
INSERT INTO user_phones (user_id, phone, type) VALUES
(1, '+1-555-0101', 'home'),
(1, '+1-555-0102', 'work');Dizin Oluşturma Farkı
Normalleştirilmiş bir tabloda yabancı anahtar veya değer sütunu üzerinde standart bir B-ağacı dizini oluşturabilirsiniz. Dizilerde, dizi içinde hızlı aramaları etkinleştirmek için bir GIN dizini (Genelleştirilmiş Ters Dizin) gerekir.
GIN dizinleri iyi çalışır; ancak B-ağacı dizinlerinden daha büyüktür ve güncellenmeleri daha yavaştır.
-- Index for fast array element lookups
CREATE INDEX idx_posts_tags ON posts USING GIN (tags);
-- Now this query uses the index efficiently
EXPLAIN SELECT title FROM posts
WHERE tags @> ARRAY['sql'];Satırlar Arasında Toplama: Normalleştirilmiş Yapı Önde
Tek tek değerleri saymanız, gruplamanız veya bunlar üzerinde toplama yapmanız gerektiğinde normalleştirilmiş tablolar çok daha doğaldır. Dizilerin içinde toplama yapmak, önce diziyi satırlara genişleten unnest() işlevini gerektirir — bu da sorgu sırasında normalleştirilmiş yapıyı temelde yeniden oluşturur.
-- Count posts per tag (array approach — needs unnest)
SELECT tag, COUNT(*) AS post_count
FROM posts, unnest(tags) AS tag
GROUP BY tag
ORDER BY post_count DESC;
-- With a normalized post_tags table this would be simpler:
-- SELECT tag, COUNT(*) FROM post_tags GROUP BY tag;Dizi Öğelerini Değiştirme
Bir dizinin içindeki tek bir öğeyi güncellemek veya silmek kullanışsız bir söz dizimi gerektirir — dizinin tamamını değiştirmeniz ya da array_remove() kullanmanız gerekir. Normalleştirilmiş bir tabloda ise belirli satır üzerinde doğrudan DELETE veya UPDATE kullanabilirsiniz.
-- Remove a single tag from an array column
UPDATE posts
SET tags = array_remove(tags, 'beginner')
WHERE id = 1;
-- Append a new tag
UPDATE posts
SET tags = array_append(tags, 'tutorial')
WHERE id = 1;
SELECT title, tags FROM posts WHERE id = 1;Geçerli Değerleri Zorunlu Kılma
Normalleştirilmiş bir tabloda, her değerin bilinen bir kümeden gelmesini zorunlu kılmak için yabancı anahtar kullanabilirsiniz. Diziler başka bir tabloya başvuramaz; yabancı anahtar desteğine sahip değildir.
Her öğe için garanti edilmiş başvuru bütünlüğüne ihtiyacınız varsa tek seçenek bir alt tablodur.
-- Normalized: only valid category IDs allowed (FK enforced)
CREATE TABLE categories (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL
);
CREATE TABLE post_categories (
post_id INT REFERENCES posts(id),
category_id INT REFERENCES categories(id),
PRIMARY KEY (post_id, category_id)
);
-- Array: no constraint possible — any text value is accepted
-- UPDATE posts SET tags = ARRAY['totally_invalid_tag'] WHERE id = 1;Pratik Karar Rehberi
Veriler basit ve düz bir listeyse, her zaman bir bütün olarak okunuyorsa, öğe başına ek öznitelik yoksa ve başvuru bütünlüğü gerekmiyorsa (örneğin etiketler, adlar, arama anahtar sözcükleri) bir dizi kullanın.
Her öğenin kendine ait öznitelikleri varsa, tek tek değerler üzerinde birleştirme veya toplama yapıyorsanız, yabancı anahtarlara ihtiyacınız varsa ya da tek tek öğeler sık sık güncelleniyor veya siliniyorsa normalleştirilmiş bir alt tablo kullanın.
-- Summary example: tags as array (good fit)
SELECT title, tags
FROM posts
WHERE tags @> ARRAY['sql']
ORDER BY title;
-- Unnest when you need row-level processing
SELECT title, unnest(tags) AS tag
FROM posts
ORDER BY title, tag;Hızlı Kontrol
Verileri normalleştirilmiş bir alt tablo yerine PostgreSQL dizisi olarak saklamak için hangi senaryo en uygundur?
Ders Özeti
Bu derste PostgreSQL'de diziler ile normalleştirilmiş tablolar arasındaki temel ödünleşimleri öğrendiniz.
- Diziler, etiketler gibi düz ve bir bütün olarak okunan listeler için derli toplu ve kullanışlıdır; ancak yabancı anahtarlardan yoksundur, öğe bazında güncellemeleri kullanışsız hâle getirir ve hızlı aramalar için GIN dizinleri gerektirir.
- Normalleştirilmiş tablolar, öğe bazında öznitelikleri, yabancı anahtar bütünlüğünü, verimli toplamayı ve basit satır düzeyinde güncellemeleri destekler; bunun karşılığında ek bir birleştirme gerekir.
- Doğru seçim yalnızca verileri nasıl sakladığınıza değil, onları nasıl sorguladığınıza, güncellediğinize ve ilişkilendirdiğinize bağlıdır.
Sıkça Sorulan Sorular
“Diziler ve Normalleştirilmiş Tablolar” dersi ücretsiz mi?
Evet — “Diziler ve Normalleştirilmiş Tablolar” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve SQL Academy kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. SQL Academy kursu toplamda 4 dersten oluşur.
“Diziler ve Normalleştirilmiş Tablolar” dersinde ne öğreneceğim?
Dizilerin doğru tercih olduğu durumlar. SQL Academy ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.
SQL Academy öğrenmeye başlamak için deneyim gerekli mi?
Önceden deneyim gerekmez. CoddyKit'te SQL Academy, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 4. dersidir.
“Diziler ve Normalleştirilmiş Tablolar” dersi ne kadar sürer?
Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.
Bu SQL Academy dersinde kod yazıp çalıştırabilir miyim?
Evet. Her SQL Academy dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.
Bu kursun tüm dersleri
- Dizi Sütunlarının Temelleri
- Dizilerin İçinde Arama
- UNNEST ve Toplama
- Diziler ve Normalleştirilmiş Tablolar