MariaDB ve MySQL’de Belirli Sütunları Seçmek için SELECT Kullanımı
Veritabanı yönetiminde en çok kullanılan komutların başında SELECT gelir. Ama çoğu zaman bir tablodan sadece belirli sütunları çekmemiz gerekir, tüm tabloyu değil. SELECT * kullanmak pratik görünse de production ortamında bu alışkanlık hem performans sorunlarına hem de güvenlik açıklarına yol açabilir. Bu yazıda MariaDB ve MySQL üzerinde belirli sütunları seçmenin tüm yöntemlerini, gerçek dünya senaryolarıyla birlikte ele alacağız.
Neden SELECT * Kullanmamalısınız?
Sysadmin olarak veritabanı performansından da sorumlu olduğunuzda, geliştiricilerin yazdığı SELECT * sorgularının neden sorun çıkardığını anlamak zorundasınız. Basit bir örnekle başlayalım.
Diyelim ki employees adında bir tablonuz var ve bu tabloda 50 sütun bulunuyor. Kullanıcı arayüzünde sadece çalışanın adını ve soyadını göstermek istiyorsunuz. Eğer SELECT * kullanırsanız:
- Gereksiz 48 sütun da ağ üzerinden taşınır
- Bellek kullanımı artar
- Index kullanımı optimizasyondan uzaklaşır
- Uygulama ile veritabanı arasındaki bant genişliği boşa harcanır
- Tabloya yeni sütun eklendiğinde uygulama beklenmedik davranışlar sergileyebilir
Belirli sütunları seçmek bu sorunların tamamını çözer. Şimdi detaylara girelim.
Temel SELECT Sözdizimi
MariaDB ve MySQL’de belirli sütunları seçmek için temel sözdizimi şu şekildedir:
# Temel yapı
SELECT sutun1, sutun2, sutun3 FROM tablo_adi;
# Örnek: Sadece isim ve email sütunlarını çek
SELECT first_name, last_name, email FROM employees;
Bu kadar basit. Ama işin içine WHERE, JOIN, ORDER BY ve diğer clauselar girdiğinde dikkat etmeniz gereken noktalar artıyor.
Örnek Veritabanı Kurulumu
Yazı boyunca kullanacağımız örnek veritabanını oluşturalım. Bu, küçük bir e-ticaret sistemini simüle ediyor:
-- Veritabanı oluştur ve seç
CREATE DATABASE eticaret_db;
USE eticaret_db;
-- Müşteri tablosu
CREATE TABLE customers (
id INT AUTO_INCREMENT PRIMARY KEY,
first_name VARCHAR(50),
last_name VARCHAR(50),
email VARCHAR(100),
phone VARCHAR(20),
address TEXT,
city VARCHAR(50),
country VARCHAR(50),
birth_date DATE,
registration_date DATETIME DEFAULT NOW(),
is_active TINYINT(1) DEFAULT 1,
credit_limit DECIMAL(10,2),
notes TEXT
);
-- Ürün tablosu
CREATE TABLE products (
id INT AUTO_INCREMENT PRIMARY KEY,
product_name VARCHAR(100),
sku VARCHAR(50),
category VARCHAR(50),
price DECIMAL(10,2),
stock_quantity INT,
supplier_id INT,
description TEXT,
created_at DATETIME DEFAULT NOW()
);
-- Sipariş tablosu
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
customer_id INT,
order_date DATETIME DEFAULT NOW(),
total_amount DECIMAL(10,2),
status VARCHAR(20),
shipping_address TEXT,
payment_method VARCHAR(30),
notes TEXT
);
-- Örnek veri ekle
INSERT INTO customers (first_name, last_name, email, phone, city, country, credit_limit) VALUES
('Ahmet', 'Yilmaz', '[email protected]', '5551234567', 'Istanbul', 'TR', 5000.00),
('Fatma', 'Kaya', '[email protected]', '5559876543', 'Ankara', 'TR', 3000.00),
('Mehmet', 'Demir', '[email protected]', '5554561234', 'Izmir', 'TR', 7500.00);
Belirli Sütunları Seçmek: Temel Senaryolar
Senaryo 1: Kullanıcı Listesi Sayfası
Web uygulamanızın admin panelinde müşteri listesi gösterilecek. Adres, kredi limiti ve notlar gibi hassas bilgileri bu listede göstermek istemiyorsunuz:
-- Yanlış yol: Tüm sütunları çekiyorsunuz
SELECT * FROM customers;
-- Doğru yol: Sadece ihtiyaç duyulan sütunları çekin
SELECT
id,
first_name,
last_name,
email,
city,
registration_date,
is_active
FROM customers
WHERE is_active = 1
ORDER BY registration_date DESC;
Bu sorgu hem daha hızlı çalışır hem de credit_limit, address, notes gibi hassas alanları gereksiz yere açığa çıkarmaz.
Senaryo 2: Stok Takip Raporu
Depo ekibinin sadece ürün adını, SKU kodunu ve stok miktarını görmesi gerekiyor. Fiyat bilgisini bu rapora dahil etmek istemiyorsunuz:
-- Stok takip raporu için sadece ilgili sütunlar
SELECT
id,
product_name,
sku,
category,
stock_quantity
FROM products
WHERE stock_quantity < 50
ORDER BY stock_quantity ASC;
Senaryo 3: Şehre Göre Müşteri Sayısı
Belirli sütunları seçerken agregasyon fonksiyonlarıyla birleştirmek çok yaygın bir ihtiyaçtır:
-- Şehre göre müşteri dağılımı
SELECT
city,
country,
COUNT(id) AS musteri_sayisi,
AVG(credit_limit) AS ortalama_kredi_limiti
FROM customers
WHERE is_active = 1
GROUP BY city, country
ORDER BY musteri_sayisi DESC;
Sütun Alias (Takma Ad) Kullanımı
Sütun isimlerini sorgu çıktısında farklı göstermek için AS kullanılır. Bu özellikle raporlama sorgularında çok işe yarar:
-- AS ile sütun adlarını özelleştir
SELECT
first_name AS 'Ad',
last_name AS 'Soyad',
email AS 'E-Posta Adresi',
city AS 'Sehir',
registration_date AS 'Kayit Tarihi'
FROM customers
WHERE country = 'TR';
-- Hesaplanan sütunlar için alias
SELECT
product_name AS 'Urun Adi',
price AS 'Fiyat',
stock_quantity AS 'Stok',
(price * stock_quantity) AS 'Toplam Deger'
FROM products
WHERE category = 'Elektronik'
ORDER BY (price * stock_quantity) DESC;
CONCAT ile Sütunları Birleştirmek
Bazen birden fazla sütunu birleştirerek tek bir sütun halinde göstermek istersiniz. CONCAT fonksiyonu bu iş için kullanılır:
-- Ad ve soyadı birleştir
SELECT
id,
CONCAT(first_name, ' ', last_name) AS 'Tam Ad',
email,
CONCAT(city, ' / ', country) AS 'Konum'
FROM customers
WHERE is_active = 1;
-- Telefon numarasını formatla
SELECT
id,
CONCAT(first_name, ' ', last_name) AS tam_ad,
CONCAT('+90 ', phone) AS telefon,
email
FROM customers
ORDER BY last_name, first_name;
Bu tür sorgular özellikle CRM sistemlerinde veya müşteri portallarında kullanıcı dostu bir görünüm elde etmenizi sağlar.
DISTINCT ile Tekil Değerleri Seçmek
Belirli sütunlardaki tekrar eden değerleri filtrelemek için DISTINCT kullanılır:
-- Hangi şehirlerde müşterimiz var?
SELECT DISTINCT city, country
FROM customers
WHERE is_active = 1
ORDER BY country, city;
-- Hangi kategorilerde ürün var?
SELECT DISTINCT category
FROM products
ORDER BY category;
-- Kaç farklı şehirde müşterimiz var?
SELECT COUNT(DISTINCT city) AS farkli_sehir_sayisi
FROM customers
WHERE country = 'TR';
WHERE ile Koşullu Sütun Seçimi
WHERE koşulu tablodaki satırları filtrelerken, sütun seçimi veriyi dikey olarak daraltır. İkisini birlikte kullanmak sorgularınızı hem hızlandırır hem de anlamlı kılar:
-- Aktif müşterilerin iletişim bilgileri
SELECT
id,
first_name,
last_name,
email,
phone
FROM customers
WHERE is_active = 1
AND country = 'TR'
AND credit_limit > 2000;
-- Belirli tarih aralığında verilen siparişler
SELECT
id,
customer_id,
order_date,
total_amount,
status
FROM orders
WHERE order_date BETWEEN '2024-01-01' AND '2024-12-31'
AND status IN ('completed', 'shipped')
ORDER BY order_date DESC;
JOIN Sorgularında Belirli Sütunları Seçmek
Bu konu sysadminler için kritik. JOIN sorgularında hangi tablonun hangi sütununu seçtiğinizi açıkça belirtmezseniz, sütun adı çakışmaları yaşanabilir. Tablo adını veya alias’ı sütun adının önüne ekleme alışkanlığı edinin:
-- JOIN ile belirli sütunlar
SELECT
c.id AS musteri_id,
c.first_name,
c.last_name,
c.email,
o.id AS siparis_id,
o.order_date,
o.total_amount,
o.status
FROM customers c
INNER JOIN orders o ON c.id = o.customer_id
WHERE o.status = 'completed'
ORDER BY o.order_date DESC;
-- LEFT JOIN örneği: Hiç sipariş vermemiş müşteriler dahil
SELECT
c.id,
c.first_name,
c.last_name,
c.email,
COUNT(o.id) AS siparis_sayisi,
COALESCE(SUM(o.total_amount), 0) AS toplam_harcama
FROM customers c
LEFT JOIN orders o ON c.id = o.customer_id
GROUP BY c.id, c.first_name, c.last_name, c.email
ORDER BY toplam_harcama DESC;
Subquery ile Sütun Seçimi
Alt sorgular, başka bir sorgunun sonucunu sütun olarak kullanmanıza olanak tanır:
-- Her müşterinin son sipariş tarihini getir
SELECT
c.id,
c.first_name,
c.last_name,
c.email,
(
SELECT MAX(o.order_date)
FROM orders o
WHERE o.customer_id = c.id
) AS son_siparis_tarihi,
(
SELECT COUNT(o.id)
FROM orders o
WHERE o.customer_id = c.id
) AS toplam_siparis_sayisi
FROM customers c
WHERE c.is_active = 1
ORDER BY son_siparis_tarihi DESC;
CASE WHEN ile Koşullu Sütun Değerleri
Sütun değerlerini belirli koşullara göre dönüştürerek göstermek için CASE WHEN kullanılır:
-- Sipariş durumunu Türkçe göster ve kategorilendir
SELECT
id,
customer_id,
order_date,
total_amount,
CASE
WHEN status = 'completed' THEN 'Tamamlandi'
WHEN status = 'shipped' THEN 'Kargoda'
WHEN status = 'pending' THEN 'Beklemede'
WHEN status = 'cancelled' THEN 'Iptal Edildi'
ELSE 'Bilinmiyor'
END AS siparis_durumu,
CASE
WHEN total_amount >= 1000 THEN 'Yuksek Degerli'
WHEN total_amount >= 500 THEN 'Orta Degerli'
ELSE 'Dusuk Degerli'
END AS siparis_kategorisi
FROM orders
ORDER BY order_date DESC;
IF ile Basit Koşullar
IF fonksiyonu CASE WHENin daha basit halidir, iki olası sonuç için kullanılır:
-- Stok durumunu göster
SELECT
product_name,
sku,
stock_quantity,
price,
IF(stock_quantity > 0, 'Stokta Var', 'Stokta Yok') AS stok_durumu,
IF(stock_quantity < 10, 'Kritik Stok', 'Normal') AS stok_uyarisi
FROM products
ORDER BY stock_quantity ASC;
Production Ortamında Dikkat Edilmesi Gerekenler
EXPLAIN ile Sorgu Analizi
Yazdığınız sorgunun index kullanıp kullanmadığını, kaç satır taradığını görmek için EXPLAIN kullanın:
-- Sorgu çalışmadan önce execution plan'ı gör
EXPLAIN SELECT
id,
first_name,
last_name,
email
FROM customers
WHERE city = 'Istanbul' AND is_active = 1;
-- Daha detaylı analiz için
EXPLAIN FORMAT=JSON SELECT
id,
first_name,
email
FROM customers
WHERE is_active = 1
ORDER BY registration_date DESC
LIMIT 100;
EXPLAIN çıktısında dikkat etmeniz gereken alanlar:
- type: ALL değeri tam tablo taraması anlamına gelir, index kullanımı tercih edilmeli
- rows: Taranacak tahmini satır sayısı, bu değer ne kadar düşükse o kadar iyi
- key: Kullanılan index adı, NULL ise index kullanılmıyor
- Extra: Using filesort veya Using temporary gibi ifadeler performans sorununa işaret eder
Index ve Sütun Seçimi İlişkisi
Covering index kavramı burada devreye girer. Eğer sorgunuzda seçtiğiniz sütunların tamamı bir index içindeyse, MariaDB/MySQL tabloyu hiç okumadan sadece index üzerinden sonucu döndürebilir:
-- Bu sütunlar için composite index oluştur
CREATE INDEX idx_customers_active_city ON customers(is_active, city, first_name, last_name, email);
-- Şimdi bu sorgu sadece index'i okuyarak sonuç döndürür (covering index)
SELECT first_name, last_name, email
FROM customers
WHERE is_active = 1 AND city = 'Istanbul';
Güvenlik Açısından Sütun Seçimi
Rol tabanlı erişim kontrolü uyguladığınızda, belirli sütunları seçmek güvenlik katmanı oluşturur. MySQL/MariaDB’de sütun bazında yetki verilebilir:
-- Sadece belirli sütunlara okuma yetkisi ver
GRANT SELECT (id, first_name, last_name, email, city) ON eticaret_db.customers TO 'rapor_kullanicisi'@'localhost';
-- Bu kullanıcı artık sadece bu sütunları sorgulayabilir
-- SELECT * veya credit_limit gibi hassas sütunlara erişemez
View Oluşturarak Tekrar Kullanım
Aynı sütun seçimini sürekli tekrar ediyorsanız, view oluşturmak bu tekrarı önler ve güvenlik katmanı ekler:
-- Hassas sütunlar olmadan müşteri view'ı oluştur
CREATE VIEW v_customers_public AS
SELECT
id,
first_name,
last_name,
email,
city,
country,
registration_date,
is_active
FROM customers;
-- Stok durumu view'ı
CREATE VIEW v_stock_status AS
SELECT
id,
product_name,
sku,
category,
stock_quantity,
IF(stock_quantity > 10, 'Normal', IF(stock_quantity > 0, 'Kritik', 'Tukendi')) AS durum
FROM products;
-- View'ları kullan
SELECT * FROM v_customers_public WHERE city = 'Istanbul';
SELECT * FROM v_stock_status WHERE durum = 'Kritik';
View’lar özellikle şu durumlarda çok işe yarar:
- Farklı uygulama katmanlarının aynı sütun setine ihtiyacı olduğunda
- Hassas sütunları gizlemek istediğinizde
- Karmaşık sorguları basitleştirmek istediğinizde
- Kullanıcı yetkilerini sütun bazında yönetmek istediğinizde
Performans İyileştirme: Gerçek Dünya Senaryosu
Bir e-ticaret sitesinin yoğun saatlerde yavaşladığını düşünün. slow query logu incelediğinizde şu sorguyu görüyorsunuz:
-- Yavaş çalışan orijinal sorgu (production'da gerçekten gördüğüm türden)
SELECT * FROM orders
WHERE status = 'pending'
ORDER BY order_date;
-- Optimize edilmiş versiyon
SELECT
id,
customer_id,
order_date,
total_amount,
payment_method
FROM orders
WHERE status = 'pending'
ORDER BY order_date ASC
LIMIT 100;
-- Index ekle
CREATE INDEX idx_orders_status_date ON orders(status, order_date);
Bu değişikliklerle birlikte:
SELECT *yerine 5 sütun seçilerek ağ trafiği azaltıldıLIMITeklenerek tüm bekleyen siparişler yerine ilk 100 tanesi çekildi- Composite index eklenerek tam tablo taraması önlendi
Sonuç
MariaDB ve MySQL’de belirli sütunları seçmek, basit görünse de arkasında ciddi bir performans ve güvenlik felsefesi barındırır. SELECT * kullanımından kaçınmak, yazdığınız her sorgunun gerçekten neye ihtiyaç duyduğunu düşünmek sizi iyi bir veritabanı yöneticisi yapan alışkanlıkların başında gelir.
Özetlemek gerekirse:
- Her zaman ihtiyacınız olan sütunları açıkça belirtin
ASile anlamlı alias’lar kullanınCONCAT,CASE WHEN,IFgibi fonksiyonlarla sütunları dönüştürünEXPLAINile sorgularınızın performansını analiz edin- Tekrar kullandığınız sütun setleri için View oluşturun
- Hassas sütunları yetki sistemiyle koruyun
- Covering index mantığını göz önünde bulundurarak sütun ve index seçimini birlikte planlayın
Slow query log’unuzu düzenli takip etmek ve EXPLAIN çıktılarını okumayı öğrenmek, veritabanı performans sorunlarının büyük çoğunluğunu çözmenizi sağlar. Çoğu zaman karmaşık yapılandırma değişikliklerine gerek kalmadan, sadece daha iyi yazılmış sorgularla dramatik performans iyileştirmeleri elde edilebilir.
