← Kursa Dön
📄 Text · 35 min

INSERT INTO — Tekli, Çoklu ve SELECT ile

Giriş — Tablolar Boş Durmuyor

Önceki bölümde veritabanımızı ve tablolarımızı oluşturduk. Ama şu an hepsi boş — içlerinde tek bir satır veri yok. Bir mağaza açıp rafları kurmuş ama hiç ürün koymamış gibi.

Bu derste tablolara veri eklemeyi öğreneceğiz. INSERT INTO komutu, SQL'de en sık kullanacağın komutlardan biridir. Her kullanıcı kaydı, her sipariş, her ürün ekleme işlemi arka planda bir INSERT komutu çalıştırır.

🎯 Analoji: Bir kütüphaneye yeni kitap eklemeyi düşün. Her kitap için bir kayıt formu doldurursun: kitap adı, yazarı, yayın yılı, rafı... İşte INSERT INTO da veritabanına "kayıt formu doldurma" işlemidir.


Temel Sözdizimi

INSERT INTO tablo_adi (sutun1, sutun2, sutun3, ...)
VALUES (deger1, deger2, deger3, ...);

Üç ana bileşen:

  1. Tablo adı — Hangi tabloya eklenecek?

  2. Sütun listesi — Hangi sütunlara değer verilecek?

  3. Değerler — Sütunlara atanacak değerler


Tekli Kayıt Ekleme

Tüm Sütunları Belirterek

-- Müşteri ekle — sütun listesiyle
INSERT INTO customers (first_name, last_name, email, phone, city)
VALUES ('Ahmet', 'Yılmaz', 'ahmet@email.com', '05551234567', 'İstanbul');

Çıktı:

Query OK, 1 row affected (0.01 sec)

Eklenen kaydı kontrol edelim:

SELECT * FROM customers WHERE email = 'ahmet@email.com';
+-------------+------------+-----------+------------------+-------------+----------+-------------------+-----------+
| customer_id | first_name | last_name | email            | phone       | city     | registration_date | is_active |
+-------------+------------+-----------+------------------+-------------+----------+-------------------+-----------+
| 1           | Ahmet      | Yılmaz    | ahmet@email.com  | 05551234567 | İstanbul | 2024-01-15        | 1         |
+-------------+------------+-----------+------------------+-------------+----------+-------------------+-----------+

Dikkat et:

  • customer_idAUTO_INCREMENT olduğu için otomatik atandı (1)

  • registration_dateDEFAULT (CURRENT_DATE) olduğu için bugünün tarihi atandı

  • is_activeDEFAULT TRUE olduğu için 1 (true) atandı

Sütun Listesi Olmadan (Tüm Sütunlar İçin Değer Verme)

-- Sütun listesi yazılmadığında TÜM sütunlara sırasıyla değer verilmeli
INSERT INTO customers 
VALUES (NULL, 'Zeynep', 'Kaya', 'zeynep@email.com', '05559876543', 'Ankara', '2024-01-10', TRUE);

Burada NULL verdik çünkü customer_id AUTO_INCREMENT — MySQL otomatik atayacak.

⚠️ Dikkat: Sütun listesi olmadan INSERT yapmak kötü pratiktir. Neden? - Tabloya yeni sütun eklendiğinde tüm INSERT komutların bozulur - Sütun sırası değişirse yanlış yerlere değer gider - Kodu okuyan kişi hangi değerin hangi sütuna gittiğini anlamaz Her zaman sütun listesini açıkça yaz.

Sadece Bazı Sütunlara Değer Verme

-- Sadece zorunlu alanları doldur
INSERT INTO customers (first_name, last_name, email)
VALUES ('Mehmet', 'Demir', 'mehmet@email.com');
-- phone → NULL, city → NULL
-- registration_date → DEFAULT (bugün)
-- is_active → DEFAULT (TRUE)

Belirtilmeyen sütunlara:

  • DEFAULT değeri varsa → o değer atanır

  • DEFAULT yoksa ve NULL kabul ediyorsa → NULL atanır

  • NOT NULL ve DEFAULT yoksa → HATA

-- ❌ HATA — first_name NOT NULL ama değer verilmedi
INSERT INTO customers (last_name, email)
VALUES ('Kara', 'ali@email.com');
-- ERROR: Field 'first_name' doesn't have a default value

Çoklu Kayıt Ekleme (Batch Insert)

Tek bir INSERT komutuyla birden fazla satır ekleyebilirsin:

-- Birden fazla müşteri ekle
INSERT INTO customers (first_name, last_name, email, city) VALUES
    ('Ayşe', 'Çelik', 'ayse@email.com', 'İzmir'),
    ('Fatma', 'Öztürk', 'fatma@email.com', 'Bursa'),
    ('Ali', 'Şahin', 'ali.sahin@email.com', 'Antalya'),
    ('Elif', 'Arslan', 'elif@email.com', 'İstanbul'),
    ('Can', 'Koç', 'can@email.com', 'Ankara');

Çıktı:

Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

Neden çoklu INSERT kullanmalısın?

Performans farkı dramatiktir:

-- ❌ Yavaş — Her biri ayrı bir transaction ve ağ isteği
INSERT INTO products (product_name, price) VALUES ('Ürün 1', 100);
INSERT INTO products (product_name, price) VALUES ('Ürün 2', 200);
INSERT INTO products (product_name, price) VALUES ('Ürün 3', 300);
-- ... 1000 satır = 1000 ayrı istek

-- ✅ Hızlı — Tek transaction, tek ağ isteği
INSERT INTO products (product_name, price) VALUES
    ('Ürün 1', 100),
    ('Ürün 2', 200),
    ('Ürün 3', 300);
-- ... 1000 satır = 1 istek

Çoklu INSERT, tekli INSERT'e göre genellikle 5-20 kat daha hızlıdır çünkü:

  • Tek bir ağ round-trip

  • Tek bir transaction commit

  • Index güncellemesi toplu yapılır

💡 İpucu: Çok sayıda veri eklerken (10,000+ satır), tek bir INSERT'e hepsini sığdırma. MySQL'in max_allowed_packet limiti var (varsayılan ~64MB). 1000'erlik gruplar halinde eklemek iyi bir pratiktir.


Örnek Veri Setimizi Oluşturalım

E-ticaret veritabanımıza anlamlı veriler ekleyelim. Bu verileri kursun geri kalanında kullanacağız:

-- Kategoriler
INSERT INTO categories (category_name, parent_category_id, description) VALUES
    ('Elektronik', NULL, 'Elektronik ürünler ve aksesuarlar'),
    ('Bilgisayar', 1, 'Masaüstü ve dizüstü bilgisayarlar'),
    ('Telefon', 1, 'Akıllı telefonlar ve aksesuarları'),
    ('Giyim', NULL, 'Erkek ve kadın giyim ürünleri'),
    ('Erkek Giyim', 4, 'Erkek kıyafetleri'),
    ('Kadın Giyim', 4, 'Kadın kıyafetleri'),
    ('Kitap', NULL, 'Kitaplar ve dergiler'),
    ('Ev & Yaşam', NULL, 'Ev dekorasyonu ve yaşam ürünleri');

-- Ürünler
INSERT INTO products (product_name, category_id, price, stock_quantity) VALUES
    ('MacBook Pro 14"', 2, 54999.99, 25),
    ('iPhone 15 Pro', 3, 64999.99, 100),
    ('Samsung Galaxy S24', 3, 44999.99, 75),
    ('Dell XPS 15', 2, 42999.99, 30),
    ('Kablosuz Mouse', 1, 299.99, 500),
    ('Mekanik Klavye', 1, 1299.99, 200),
    ('Erkek Polo Tişört', 5, 349.99, 300),
    ('Kadın Elbise', 6, 599.99, 150),
    ('SQL Öğreniyorum', 7, 89.99, 1000),
    ('Python Programlama', 7, 129.99, 800),
    ('USB-C Hub', 1, 599.99, 400),
    ('Monitör 27"', 2, 8999.99, 50),
    ('Bluetooth Kulaklık', 1, 1999.99, 350),
    ('Erkek Jean', 5, 499.99, 250),
    ('Kadın Blazer', 6, 899.99, 100);

-- Müşteriler
INSERT INTO customers (first_name, last_name, email, phone, city) VALUES
    ('Ahmet', 'Yılmaz', 'ahmet@email.com', '05551001001', 'İstanbul'),
    ('Zeynep', 'Kaya', 'zeynep@email.com', '05551001002', 'Ankara'),
    ('Mehmet', 'Demir', 'mehmet@email.com', '05551001003', 'İzmir'),
    ('Ayşe', 'Çelik', 'ayse@email.com', '05551001004', 'İstanbul'),
    ('Fatma', 'Öztürk', 'fatma@email.com', '05551001005', 'Bursa'),
    ('Ali', 'Şahin', 'ali@email.com', '05551001006', 'Antalya'),
    ('Elif', 'Arslan', 'elif@email.com', NULL, 'İstanbul'),
    ('Can', 'Koç', 'can@email.com', '05551001008', 'Ankara'),
    ('Deniz', 'Yıldız', 'deniz@email.com', '05551001009', 'İzmir'),
    ('Selin', 'Aydın', 'selin@email.com', '05551001010', 'İstanbul');

-- Siparişler
INSERT INTO orders (customer_id, order_date, total_amount, status) VALUES
    (1, '2024-01-10 10:30:00', 55299.98, 'delivered'),
    (1, '2024-01-20 14:15:00', 1599.98, 'delivered'),
    (2, '2024-01-12 09:45:00', 64999.99, 'delivered'),
    (3, '2024-01-15 16:00:00', 45299.98, 'shipped'),
    (4, '2024-01-18 11:30:00', 349.99, 'delivered'),
    (5, '2024-02-01 13:00:00', 899.99, 'processing'),
    (2, '2024-02-05 10:00:00', 219.98, 'pending'),
    (6, '2024-02-10 15:30:00', 8999.99, 'shipped'),
    (7, '2024-02-12 09:00:00', 1299.99, 'pending'),
    (1, '2024-02-15 17:45:00', 599.99, 'pending');

-- Sipariş kalemleri
INSERT INTO order_items (order_id, product_id, quantity, unit_price, discount_percent) VALUES
    (1, 1, 1, 54999.99, 0),
    (1, 5, 1, 299.99, 0),
    (2, 6, 1, 1299.99, 0),
    (2, 5, 1, 299.99, 0),
    (3, 2, 1, 64999.99, 0),
    (4, 3, 1, 44999.99, 0),
    (4, 5, 1, 299.99, 0),
    (5, 7, 1, 349.99, 0),
    (6, 15, 1, 899.99, 0),
    (7, 9, 1, 89.99, 0),
    (7, 10, 1, 129.99, 0),
    (8, 12, 1, 8999.99, 0),
    (9, 6, 1, 1299.99, 0),
    (10, 11, 1, 599.99, 0);

INSERT INTO ... SELECT — Sorgu Sonucundan Veri Ekleme

Başka bir tablodan veya sorgu sonucundan veri ekleyebilirsin:

-- Geçici bir tablo oluştur
CREATE TABLE premium_customers (
    customer_id INT UNSIGNED PRIMARY KEY,
    first_name VARCHAR(50),
    last_name VARCHAR(50),
    email VARCHAR(100),
    total_orders INT
);

-- İstanbul'daki müşterileri premium tabloya kopyala
INSERT INTO premium_customers (customer_id, first_name, last_name, email, total_orders)
SELECT 
    c.customer_id,
    c.first_name,
    c.last_name,
    c.email,
    COUNT(o.order_id) AS total_orders
FROM customers c
LEFT JOIN orders o ON c.customer_id = o.customer_id
WHERE c.city = 'İstanbul'
GROUP BY c.customer_id, c.first_name, c.last_name, c.email;

Dikkat edilmesi gerekenler:

  • SELECT'teki sütun sayısı ve sıra, INSERT'teki sütun listesiyle uyuşmalı

  • Veri tipleri uyumlu olmalı

  • VALUES keyword'ü kullanılmaz — yerini SELECT alır

Aynı Tablodan Kopyalama

-- İndirimli ürünleri başka bir tabloya kopyala
CREATE TABLE discounted_products LIKE products;  -- Aynı yapıda boş tablo

INSERT INTO discounted_products
SELECT * FROM products WHERE price > 10000;

Farklı Veritabanından Kopyalama

INSERT INTO e_commerce.products (product_name, price)
SELECT item_name, item_price 
FROM old_database.items 
WHERE item_active = 1;

💡 İpucu: INSERT INTO ... SELECT veri taşıma (data migration), raporlama tabloları doldurma ve ETL (Extract-Transform-Load) süreçlerinde çok kullanılır.


LAST_INSERT_ID() — Son Eklenen ID'yi Alma

AUTO_INCREMENT ile eklenen son kaydın ID'sini almak için:

INSERT INTO customers (first_name, last_name, email)
VALUES ('Yeni', 'Müşteri', 'yeni@email.com');

-- Az önce eklenen müşterinin ID'si
SELECT LAST_INSERT_ID();
-- Sonuç: 11 (veya sıradaki ID ne ise)

-- Bu ID'yi hemen kullanabilirsin
INSERT INTO orders (customer_id, total_amount)
VALUES (LAST_INSERT_ID(), 500.00);

Bu pattern özellikle ilişkili kayıtlar eklerken çok kullanışlıdır:

-- Sipariş oluştur ve kalemlerini ekle
INSERT INTO orders (customer_id, total_amount, status)
VALUES (1, 2599.98, 'pending');

-- Sipariş kalemlerini ekle — az önce oluşturulan siparişin ID'sini kullan
SET @new_order_id = LAST_INSERT_ID();

INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
    (@new_order_id, 6, 1, 1299.99),
    (@new_order_id, 5, 1, 299.99),
    (@new_order_id, 13, 1, 999.99);

⚠️ Dikkat: LAST_INSERT_ID() oturum bazlıdır (session-specific). Başka bir kullanıcının INSERT'i senin LAST_INSERT_ID() değerini etkilemez. Bu güvenli bir mekanizmadır.


INSERT IGNORE — Hataları Yoksay

-- E-posta UNIQUE olduğu için aynı e-posta eklemeye çalışınca hata alırız
INSERT INTO customers (first_name, last_name, email)
VALUES ('Ahmet', 'Başka', 'ahmet@email.com');
-- ERROR: Duplicate entry 'ahmet@email.com' for key 'uq_customers_email'

-- IGNORE ile hata yerine uyarı alırız — komut devam eder
INSERT IGNORE INTO customers (first_name, last_name, email)
VALUES ('Ahmet', 'Başka', 'ahmet@email.com');
-- Query OK, 0 rows affected, 1 warning (eklenmedi ama hata da vermedi)

-- Birden fazla satırda IGNORE kullanımı
INSERT IGNORE INTO customers (first_name, last_name, email) VALUES
    ('Yeni1', 'Müşteri', 'yeni1@email.com'),       -- ✅ Eklenir
    ('Ahmet', 'Tekrar', 'ahmet@email.com'),          -- ⚠️ Atlanır (duplicate)
    ('Yeni2', 'Müşteri', 'yeni2@email.com');         -- ✅ Eklenir
-- Query OK, 2 rows affected, 1 warning

⚠️ Dikkat: INSERT IGNORE sadece duplicate hatalarını değil, tüm hataları uyarıya çevirir. Veri tipi uyuşmazlığı, NOT NULL ihlali gibi hataları da sessizce yutabilir. Bu yüzden dikkatli kullan — hataları bilmek istiyorsan IGNORE kullanma.


Gerçek Dünya Örneği — Sipariş Süreci

Bir müşterinin sipariş vermesi sırasında veritabanında neler olduğunu simüle edelim:

-- Senaryo: Müşteri Zeynep (ID: 2) sepetindeki ürünleri satın alıyor
-- Sepette: 1x Mekanik Klavye (1299.99), 2x Kablosuz Mouse (299.99)

-- Adım 1: Transaction başlat (ya hep ya hiç)
START TRANSACTION;

-- Adım 2: Stok kontrolü
SELECT product_id, product_name, stock_quantity 
FROM products 
WHERE product_id IN (6, 5);
-- Mekanik Klavye: stock = 200 ✓
-- Kablosuz Mouse: stock = 500 ✓

-- Adım 3: Sipariş oluştur
INSERT INTO orders (customer_id, order_date, total_amount, status, shipping_address)
VALUES (2, NOW(), 1899.97, 'pending', 'Ankara, Çankaya, 06000');

-- Adım 4: Sipariş ID'sini al
SET @order_id = LAST_INSERT_ID();

-- Adım 5: Sipariş kalemlerini ekle
INSERT INTO order_items (order_id, product_id, quantity, unit_price) VALUES
    (@order_id, 6, 1, 1299.99),
    (@order_id, 5, 2, 299.99);

-- Adım 6: Stokları güncelle
UPDATE products SET stock_quantity = stock_quantity - 1 WHERE product_id = 6;
UPDATE products SET stock_quantity = stock_quantity - 2 WHERE product_id = 5;

-- Adım 7: Her şey başarılı — kaydet
COMMIT;

-- Kontrol
SELECT o.order_id, o.total_amount, o.status,
       oi.product_id, oi.quantity, oi.unit_price
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
WHERE o.order_id = @order_id;

Bu örnekte birden fazla tabloya veri ekleniyor ve güncelleniyor. Transaction sayesinde herhangi bir adım başarısız olursa tüm değişiklikler geri alınır.


Sıkça Yapılan Hatalar

  1. Sütun listesini yazmamak: INSERT INTO customers VALUES (...) tehlikelidir. Tabloya sütun eklendiğinde komut bozulur. Her zaman sütun listesini yaz.

  2. Veri tipi uyumsuzluğu: price sütununa 'yüz lira' yazmak hata verir. Sayısal sütuna sayı, tarih sütununa tarih formatında veri gir.

  3. String değerlerde tek tırnak unutmak: VALUES (Ahmet) hata verir çünkü Ahmet bir sütun adı olarak aranır. VALUES ('Ahmet') olmalı.

  4. String içinde tek tırnak: Müşterinin adı O'Brien ise 'O'Brien' hata verir. Escape etmelisin: 'O''Brien' (tek tırnağı iki kez yaz) veya 'O\'Brien' (MySQL'e özel backslash escape).

  5. Bulk insert'te tek bir hatalı satır: 1000 satırlık INSERT'te bir tanesi hatalı olursa tüm INSERT başarısız olur. INSERT IGNORE veya hatalı veriyi önceden temizlemek gerekir.

  6. LAST_INSERT_ID() zamanlaması: LAST_INSERT_ID() sadece son INSERT'in ID'sini döndürür. Çoklu INSERT'te son satırın ID'sini değil, ilk satırın ID'sini döndürür.


Özet

  • INSERT INTO tablo (sütunlar) VALUES (değerler) temel veri ekleme komutudur

  • Sütun listesini her zaman yaz — güvenli ve okunabilir kod için

  • Çoklu INSERT (VALUES (...), (...), (...)) tekli INSERT'ten çok daha hızlıdır

  • INSERT INTO ... SELECT başka sorguların sonucundan veri eklemeye yarar

  • LAST_INSERT_ID() son eklenen AUTO_INCREMENT değerini döndürür

  • INSERT IGNORE duplicate ve diğer hataları sessizce atlar

  • Belirtilmeyen sütunlara DEFAULT değer atanır, yoksa NULL olur

  • İlişkili kayıtlar eklerken transaction kullan — veri tutarlılığı için kritik