MariaDB ve MySQL’de INSERT INTO ile Yeni Kayıt Ekleme
Veritabanı yönetiminin temel taşlarından biri olan kayıt ekleme işlemi, her sysadmin’in günlük rutininde mutlaka karşılaştığı bir görevdir. Ister bir uygulama veritabanını yönetiyor olun, ister manuel veri girişi yapıyor olun, INSERT INTO komutunu doğru ve verimli kullanmak işlerin aksamadan ilerlemesi için kritik öneme sahiptir. Bu yazıda MariaDB ve MySQL üzerinde INSERT INTO komutunun tüm özelliklerini, pratik kullanım senaryolarıyla birlikte ele alacağız.
INSERT INTO Komutunun Temel Yapısı
INSERT INTO komutu, bir tabloya yeni satır eklemek için kullanılan SQL ifadesidir. İki temel sözdizimi biçimi vardır.
Birinci yöntem: Sütun adlarını belirterek değer girme
INSERT INTO tablo_adi (sutun1, sutun2, sutun3)
VALUES (deger1, deger2, deger3);
İkinci yöntem: Tüm sütunlara sırayla değer girme
INSERT INTO tablo_adi
VALUES (deger1, deger2, deger3, deger4);
Sysadmin olarak ikinci yöntemi pek önermiyorum. Tablo yapısı değiştiğinde, yani sütun eklenip çıkarıldığında sorgularınız bozulur. Sütun adlarını açıkça belirtmek her zaman daha güvenli ve bakımı kolay bir yaklaşımdır.
Önce çalışacağımız örnek veritabanını ve tablolarını oluşturalım. Gerçek dünyaya yakın bir senaryo kurgulamak için bir e-ticaret uygulamasının veritabanını ele alacağız.
-- Veritabanı oluşturma ve seçme
CREATE DATABASE IF NOT EXISTS eticaret_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE eticaret_db;
-- Kullanıcılar tablosu
CREATE TABLE kullanicilar (
id INT AUTO_INCREMENT PRIMARY KEY,
ad VARCHAR(50) NOT NULL,
soyad VARCHAR(50) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
telefon VARCHAR(15),
kayit_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
aktif TINYINT(1) DEFAULT 1
);
-- Ürünler tablosu
CREATE TABLE urunler (
id INT AUTO_INCREMENT PRIMARY KEY,
urun_adi VARCHAR(150) NOT NULL,
kategori VARCHAR(50),
fiyat DECIMAL(10, 2) NOT NULL,
stok INT DEFAULT 0,
olusturma_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP
);
-- Siparişler tablosu
CREATE TABLE siparisler (
id INT AUTO_INCREMENT PRIMARY KEY,
kullanici_id INT NOT NULL,
urun_id INT NOT NULL,
adet INT DEFAULT 1,
toplam_fiyat DECIMAL(10, 2),
siparis_tarihi DATETIME DEFAULT CURRENT_TIMESTAMP,
durum VARCHAR(20) DEFAULT 'beklemede'
);
Tekil Kayıt Ekleme
En sık kullanılan senaryo, tek seferde bir kayıt eklemektir. Müşteri kaydı oluşturma, ürün ekleme gibi işlemler bu kategoriye girer.
-- Kullanıcı tablosuna tekil kayıt ekleme
INSERT INTO kullanicilar (ad, soyad, email, telefon)
VALUES ('Ahmet', 'Yılmaz', '[email protected]', '0532-111-2233');
-- Kayıt ID'sini öğrenmek için
SELECT LAST_INSERT_ID();
-- Ürün ekleme
INSERT INTO urunler (urun_adi, kategori, fiyat, stok)
VALUES ('Samsung Galaxy S24', 'Telefon', 24999.90, 50);
LAST_INSERT_ID() fonksiyonu, son eklenen kaydın otomatik artan birincil anahtar değerini döndürür. Uygulama geliştirme veya script yazarken bu değere ihtiyaç duyduğunuz çok olacaktır. Özellikle ilişkili tablolara ardışık kayıt eklerken bu fonksiyon hayat kurtarır.
Dikkat etmeniz gereken bir nokta var: AUTO_INCREMENT sütunlar için değer girmek zorunda değilsiniz. MariaDB otomatik olarak bir sonraki değeri atayacaktır. Eğer siz bir değer girerseniz ve bu değer zaten kullanılmışsa hata alırsınız.
Birden Fazla Kayıt Aynı Anda Ekleme
Toplu veri yüklemelerinde her kayıt için ayrı INSERT çalıştırmak hem yavaş hem de verimsizdir. INSERT INTO komutu tek sorguda birden fazla satır eklemenize olanak tanır.
-- Tek sorguda birden fazla kullanıcı ekleme
INSERT INTO kullanicilar (ad, soyad, email, telefon)
VALUES
('Fatma', 'Kaya', '[email protected]', '0542-222-3344'),
('Mehmet', 'Demir', '[email protected]', '0553-333-4455'),
('Ayşe', 'Çelik', '[email protected]', '0505-444-5566'),
('Ali', 'Şahin', '[email protected]', '0516-555-6677'),
('Zeynep', 'Arslan', '[email protected]', NULL);
-- Birden fazla ürün ekleme
INSERT INTO urunler (urun_adi, kategori, fiyat, stok)
VALUES
('iPhone 15 Pro', 'Telefon', 54999.00, 30),
('MacBook Air M2', 'Dizüstü Bilgisayar', 44999.00, 15),
('AirPods Pro 2', 'Kulaklık', 8999.00, 100),
('iPad Air', 'Tablet', 22999.00, 25);
Performans açısından konuşursak, 1000 ayrı INSERT sorgusu çalıştırmak yerine değerleri gruplandırmak çok daha hızlıdır. Bu yaklaşım ağ trafiğini azaltır, transaction overhead’i düşürür ve genel sorgu performansını artırır. Büyük veri yüklemelerinde bunu hep aklınızda tutun.
Ancak çok büyük miktarda veri yükleyecekseniz, binlerce satırı tek bir VALUES listesine koymak da sorun yaratabilir. Bu durumda 500-1000 satırlık gruplar halinde çalışmak daha dengeli bir yaklaşımdır.
INSERT INTO SELECT Kullanımı
Bir tablodan veri çekip başka bir tabloya aktarmak istediğinizde INSERT INTO SELECT yapısı devreye girer. Bu yöntem özellikle yedekleme tabloları oluştururken, veri taşıma işlemlerinde veya özet tablolar doldururken çok işinize yarar.
-- Yedekleme tablosu oluşturma ve veri kopyalama
CREATE TABLE kullanicilar_yedek LIKE kullanicilar;
INSERT INTO kullanicilar_yedek
SELECT * FROM kullanicilar;
-- Belirli koşula göre seçici kopyalama
-- Sadece aktif kullanıcıları yedekleme
INSERT INTO kullanicilar_yedek (ad, soyad, email, telefon, kayit_tarihi, aktif)
SELECT ad, soyad, email, telefon, kayit_tarihi, aktif
FROM kullanicilar
WHERE aktif = 1;
-- Farklı tablolardan birleştirerek veri ekleme
-- Sipariş özet tablosu oluşturalım
CREATE TABLE siparis_ozet (
kullanici_email VARCHAR(100),
toplam_siparis INT,
toplam_harcama DECIMAL(12, 2)
);
INSERT INTO siparis_ozet (kullanici_email, toplam_siparis, toplam_harcama)
SELECT
k.email,
COUNT(s.id) AS toplam_siparis,
SUM(s.toplam_fiyat) AS toplam_harcama
FROM kullanicilar k
JOIN siparisler s ON k.id = s.kullanici_id
GROUP BY k.email;
Bu yöntemin güzelliği, SELECT sorgusunun tüm gücünü kullanabilmenizdir. JOIN, WHERE, GROUP BY, ORDER BY, alt sorgular, hesaplanan değerler, bunların hepsi INSERT INTO SELECT ile birlikte kullanılabilir.
INSERT IGNORE Kullanımı
Toplu veri ekleme işlemlerinde bazen bazı kayıtlar duplicate key hatası veya kısıtlama ihlali nedeniyle eklenemeyebilir. Normal şartlarda bu durum tüm sorgunun başarısız olmasına yol açar. INSERT IGNORE komutu ise sorunlu kayıtları atlayıp diğerlerini eklemeye devam eder.
-- Normal INSERT ile duplicate email hatası
-- Bu sorgu hata verir çünkü [email protected] zaten var
INSERT INTO kullanicilar (ad, soyad, email)
VALUES ('Ahmet2', 'Yılmaz2', '[email protected]');
-- ERROR 1062 (23000): Duplicate entry '[email protected]' for key 'email'
-- INSERT IGNORE ile hata sessizce geçilir
INSERT IGNORE INTO kullanicilar (ad, soyad, email)
VALUES
('Ahmet2', 'Yılmaz2', '[email protected]'), -- bu atlanır
('Burak', 'Öztürk', '[email protected]'), -- bu eklenir
('Selin', 'Akın', '[email protected]'); -- bu eklenir
-- Kaç satırın eklendiğini kontrol et
SELECT ROW_COUNT();
INSERT IGNORE kullanırken dikkatli olun. Bazı durumlarda veri tutarlılığı açısından hataların sessizce geçilmesi tehlikeli olabilir. Log dosyalarınızı takip edin veya ROW_COUNT() ile kaç kaydın eklendiğini doğrulayın. Özellikle üretim ortamlarında hata yönetimini uygulama katmanında yapmak genellikle daha güvenlidir.
INSERT ON DUPLICATE KEY UPDATE
Bu yapı, kayıt yoksa ekle, varsa güncelle mantığıyla çalışır. UPSERT olarak da bilinen bu yaklaşım, özellikle periyodik veri senkronizasyon işlemlerinde çok kullanışlıdır.
-- Ürün stok güncelleme senaryosu
-- Eğer ürün yoksa ekle, varsa stok ve fiyatı güncelle
INSERT INTO urunler (urun_adi, kategori, fiyat, stok)
VALUES ('Samsung Galaxy S24', 'Telefon', 23999.90, 75)
ON DUPLICATE KEY UPDATE
fiyat = VALUES(fiyat),
stok = VALUES(stok);
-- Daha gelişmiş bir örnek: Stok miktarını artırma
INSERT INTO urunler (urun_adi, kategori, fiyat, stok)
VALUES ('AirPods Pro 2', 'Kulaklık', 8999.00, 50)
ON DUPLICATE KEY UPDATE
stok = stok + VALUES(stok),
fiyat = VALUES(fiyat);
-- Kullanıcı giriş sayısı takibi
CREATE TABLE kullanici_giris_log (
kullanici_id INT PRIMARY KEY,
giris_sayisi INT DEFAULT 0,
son_giris DATETIME
);
INSERT INTO kullanici_giris_log (kullanici_id, giris_sayisi, son_giris)
VALUES (1, 1, NOW())
ON DUPLICATE KEY UPDATE
giris_sayisi = giris_sayisi + 1,
son_giris = NOW();
ON DUPLICATE KEY UPDATE için tabloda benzersiz kısıtlama (UNIQUE) veya birincil anahtar (PRIMARY KEY) bulunması gerekir. Çakışma bu kısıtlamalar üzerinden tespit edilir. VALUES(sutun_adi) ifadesi, INSERT kısmında belirtilen değeri referans almanızı sağlar.
REPLACE INTO Kullanımı
REPLACE INTO, INSERT ON DUPLICATE KEY UPDATE‘e benzer ama farklı çalışır. Çakışma durumunda mevcut kaydı tamamen siler ve yenisini ekler. Bu iki işlem arasındaki fark ID değerlerini etkiler; silme ve yeniden ekleme nedeniyle AUTO_INCREMENT değeri artar.
-- REPLACE INTO ile kayıt ekleme/değiştirme
REPLACE INTO urunler (id, urun_adi, kategori, fiyat, stok)
VALUES (1, 'Samsung Galaxy S24 Ultra', 'Telefon', 44999.90, 20);
-- Dikkat: Bu işlem mevcut kaydı SILER ve yenisini EKLER
-- AUTO_INCREMENT artar
-- Foreign key ilişkileri varsa sorun çıkabilir
Üretim ortamlarında REPLACE INTO yerine INSERT ON DUPLICATE KEY UPDATE kullanmanızı şiddetle tavsiye ederim. Özellikle başka tablolardan referans alan kayıtlarda REPLACE INTO beklenmedik foreign key hatalarına yol açabilir.
Gerçek Dünya Senaryosu: Toplu Ürün Aktarımı Script’i
Bir e-ticaret sisteminde CSV dosyasından ürün verilerini MariaDB’ye aktaran basit bir bash script örneği:
#!/bin/bash
# Ürün verilerini CSV'den veritabanına aktarma scripti
DB_HOST="localhost"
DB_USER="app_user"
DB_PASS="gizli_sifre"
DB_NAME="eticaret_db"
LOG_FILE="/var/log/urun_aktarim.log"
CSV_FILE="/tmp/yeni_urunler.csv"
echo "$(date '+%Y-%m-%d %H:%M:%S') - Ürün aktarımı başladı" >> $LOG_FILE
# CSV formatı: urun_adi,kategori,fiyat,stok
# Başlık satırını atla, verileri işle
tail -n +2 "$CSV_FILE" | while IFS=',' read -r urun_adi kategori fiyat stok; do
# Tek tırnak karakterlerini escape et
urun_adi=$(echo "$urun_adi" | sed "s/'/''/g")
mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" <<EOF 2>> $LOG_FILE
INSERT INTO urunler (urun_adi, kategori, fiyat, stok)
VALUES ('$urun_adi', '$kategori', $fiyat, $stok)
ON DUPLICATE KEY UPDATE
fiyat = VALUES(fiyat),
stok = VALUES(stok);
EOF
if [ $? -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') - OK: $urun_adi" >> $LOG_FILE
else
echo "$(date '+%Y-%m-%d %H:%M:%S') - HATA: $urun_adi" >> $LOG_FILE
fi
done
echo "$(date '+%Y-%m-%d %H:%M:%S') - Aktarım tamamlandı" >> $LOG_FILE
# Özet bilgi
EKLENEN=$(mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME"
-se "SELECT COUNT(*) FROM urunler WHERE DATE(olusturma_tarihi) = CURDATE();")
echo "Bugün eklenen/güncellenen ürün sayısı: $EKLENEN"
Bu script gerçek bir ortamda kullanılacaksa SQL injection koruması için hazırlanmış ifadeler (prepared statements) kullanmanız veya mysql_real_escape_string benzeri mekanizmalar uygulamanız gerekir.
Transaction ile Güvenli Veri Ekleme
Birden fazla ilişkili tabloya aynı anda veri eklerken transaction kullanmak veri bütünlüğünü korur. Örneğin sipariş oluştururken hem siparişler tablosuna kayıt ekleyecek hem de stok tablosunu güncelleyeceksiniz. Bu iki işlemin ya hep beraber başarılı olması ya da hiçbirinin gerçekleşmemesi gerekir.
-- Transaction ile sipariş oluşturma
START TRANSACTION;
-- Siparişi ekle
INSERT INTO siparisler (kullanici_id, urun_id, adet, toplam_fiyat, durum)
VALUES (1, 2, 2, 89998.00, 'onaylandi');
-- Eklenen siparişin ID'sini al
SET @siparis_id = LAST_INSERT_ID();
-- Stok güncelleme
UPDATE urunler
SET stok = stok - 2
WHERE id = 2 AND stok >= 2;
-- Etkilenen satır sayısını kontrol et
-- Eğer 0 ise stok yetersiz demektir
SET @etkilenen = ROW_COUNT();
-- Koşula göre commit veya rollback
-- Gerçek uygulamada bu kontrol uygulama katmanında yapılır
IF @etkilenen = 0 THEN
ROLLBACK;
SELECT 'Hata: Yetersiz stok, sipariş oluşturulamadı' AS mesaj;
ELSE
COMMIT;
SELECT CONCAT('Sipariş başarıyla oluşturuldu. Sipariş ID: ', @siparis_id) AS mesaj;
END IF;
MariaDB’de IF ifadesi stored procedure dışında çalışmaz. Yukarıdaki mantığı gerçekte ya uygulama kodunuzda ya da bir stored procedure içinde uygularsınız. Transaction kavramını anlamanız açısından bu yapıyı kavramsal olarak gösterdim.
INSERT ile Performans İpuçları
Büyük miktarda veri eklerken performansı artıracak bazı pratik ipuçları:
- bulk_insert_buffer_size: MyISAM tablolarında toplu ekleme için kullanılan buffer boyutunu artırabilirsiniz. MariaDB yapılandırma dosyasına eklenebilir.
- innodb_flush_log_at_trx_commit: Değeri 2 yapmak her transaction’da diske yazmayı azaltır, performansı artırır ama güç kesintisinde son birkaç saniyenin verisi kaybolabilir.
- autocommit = 0: Otomatik commit kapatıp her 1000 kayıtta bir manuel commit yapmak toplu eklemelerde büyük hız farkı yaratır.
-- Büyük veri eklemeleri için optimizasyon
SET autocommit = 0;
SET unique_checks = 0;
SET foreign_key_checks = 0;
-- Binlerce kayıt ekle
INSERT INTO urunler (urun_adi, kategori, fiyat, stok) VALUES
('Ürün 1', 'Kategori A', 99.90, 100),
('Ürün 2', 'Kategori B', 199.90, 50),
-- ... binlerce satır daha
('Ürün N', 'Kategori Z', 299.90, 75);
COMMIT;
-- Sonra her şeyi geri aç
SET unique_checks = 1;
SET foreign_key_checks = 1;
SET autocommit = 1;
unique_checks ve foreign_key_checks kapatmak riski beraberinde getirir. Bunu sadece temiz ve doğrulanmış verilerle çalışırken yapın. Üretim veritabanlarında dikkatli olun.
Sık Karşılaşılan Hatalar ve Çözümleri
Günlük işlerde en çok karşılaşılan INSERT hatalarını ve çözümlerini şu şekilde özetleyebilirim:
- ERROR 1062: Duplicate entry: Benzersiz kısıtlama ihlali.
INSERT IGNOREveyaON DUPLICATE KEY UPDATEkullanın ya da veriyi önceden kontrol edin. - ERROR 1048: Column cannot be null:
NOT NULLtanımlı bir sütuna NULL değer girmeye çalışıyorsunuz. Değeri sağlayın veya tablo tanımını gözden geçirin. - ERROR 1452: Cannot add or update a child row: Foreign key ihlali. Önce parent tabloya kayıt ekleyin.
- ERROR 1406: Data too long for column: Veri, sütun boyutunu aşıyor. Veriyi kırpın veya sütun boyutunu artırın.
- ERROR 1292: Incorrect datetime value: Tarih formatı yanlış.
STR_TO_DATE()veya doğru format kullanın.
-- Tarih formatı sorunu çözümü
INSERT INTO kullanicilar (ad, soyad, email, kayit_tarihi)
VALUES ('Test', 'Kullanici', '[email protected]',
STR_TO_DATE('25-12-2024', '%d-%m-%Y'));
-- NULL kontrolü ile güvenli ekleme
INSERT INTO kullanicilar (ad, soyad, email, telefon)
VALUES (
'Deniz',
'Yıldız',
'[email protected]',
NULLIF('', '') -- Boş string yerine NULL ekler
);
Sonuç
INSERT INTO komutu, görünürde basit ama derinlemesine incelendiğinde oldukça zengin bir SQL ifadesidir. Tekil kayıttan toplu aktarıma, basit eklemeden UPSERT operasyonlarına kadar geniş bir kullanım yelpazesi sunar.
Sysadmin olarak günlük işlerde en çok işinize yarayacak noktaları özetlemek gerekirse: toplu veri aktarımlarında çoklu VALUES kullanımı performansı dramatik şekilde artırır, periyodik senkronizasyon işlemleri için ON DUPLICATE KEY UPDATE vazgeçilmezdir ve kritik işlemlerde transaction kullanımı veri bütünlüğünü garanti altına alır.
Üretim ortamlarında INSERT sorgularını çalıştırmadan önce daima bir test ortamında deneyin ve önemli işlemler öncesinde yedek aldığınızdan emin olun. Veritabanı yönetimi konusunda “önce yedekle, sonra uygula” prensibi asla değişmez bir kural olarak kalmalıdır.
