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 INTOda 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:
Tablo adı — Hangi tabloya eklenecek?
Sütun listesi — Hangi sütunlara değer verilecek?
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_id→AUTO_INCREMENTolduğu için otomatik atandı (1)registration_date→DEFAULT (CURRENT_DATE)olduğu için bugünün tarihi atandıis_active→DEFAULT TRUEolduğ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
INSERTyapmak 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:
DEFAULTdeğeri varsa → o değer atanırDEFAULTyoksa veNULLkabul ediyorsa →NULLatanırNOT NULLveDEFAULTyoksa → 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: 0Neden ç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_packetlimiti 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ı
VALUESkeyword'ü kullanılmaz — yeriniSELECTalı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 ... SELECTveri 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 seninLAST_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 IGNOREsadece 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 istiyorsanIGNOREkullanma.
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
Sütun listesini yazmamak:
INSERT INTO customers VALUES (...)tehlikelidir. Tabloya sütun eklendiğinde komut bozulur. Her zaman sütun listesini yaz.Veri tipi uyumsuzluğu:
pricesütununa 'yüz lira' yazmak hata verir. Sayısal sütuna sayı, tarih sütununa tarih formatında veri gir.String değerlerde tek tırnak unutmak:
VALUES (Ahmet)hata verir çünkü Ahmet bir sütun adı olarak aranır.VALUES ('Ahmet')olmalı.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).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 IGNOREveya hatalı veriyi önceden temizlemek gerekir.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 komutudurSütun listesini her zaman yaz — güvenli ve okunabilir kod için
Çoklu INSERT (
VALUES (...), (...), (...)) tekli INSERT'ten çok daha hızlıdırINSERT INTO ... SELECTbaşka sorguların sonucundan veri eklemeye yararLAST_INSERT_ID()son eklenen AUTO_INCREMENT değerini döndürürINSERT IGNOREduplicate ve diğer hataları sessizce atlarBelirtilmeyen sütunlara DEFAULT değer atanır, yoksa NULL olur
İlişkili kayıtlar eklerken transaction kullan — veri tutarlılığı için kritik
AI Asistan
Sorularını yanıtlamaya hazır