Veritabanı Sorgularında SQL Join İşlemleri: Verileri Birleştirme Rehberi

Gökhan Ayrancıoğlu
6 min readMay 23, 2023

SQL Join işlemi temel olarak adından da anlaşılabileceği gibi birden fazla tablodaki verileri birleştirmek için kullanılan yapılardır.

Genellikle birincil anahtarlar (primary keys) veya foreign key’ler aracılığıyla istediğimiz verilerin sorgulamasında tabloları birleştirmek için kullanılmaktadır. Bazı durumlarda birincil anahtar veya yabancı anahtar olmadan da JOIN işlemi gerçekleştirebiliririz. Bu durumda, JOIN işleminde sütunlar arasındaki benzerlikleri kullanarak tabloları birleştirebiliriz. Ancak, bu yöntem genellikle tavsiye edilmez çünkü doğruluk ve performans sorunlarına neden olabilir. Yani, JOIN işlemlerinde birincil anahtar veya yabancı anahtar kullanmak genellikle daha iyi bir yaklaşımdır. Bu, tablolar arasındaki ilişkiyi belirlemek ve daha doğru sonuçlar elde etmek için iyi bir veritabanı tasarımı gerektirir.

Basitçe JOIN işlemi iki temel keywordle birlikte gelir. JOIN ve ON keywordlerini kullanırız. JOIN işleminde SELECT ile belirtilen tabloyla birleştirmek istediğimiz tablonun adını JOIN anahtar kelimesinden sonra ifade edebiliriz. ON anahtar kelimesi ile birlikte iki tablodaki hangi sütunları baz alarak birleştirebileceğimizi söyleyebiliriz.

SELECT *
FROM table1
JOIN table2 ON table1.column1 = table2.column2;

Bu örnekte, table2 tablosu ile table1 tablolarını JOIN ederken, column1 sütunuyla column2 sütununu eşleştirerek bu birleştirme işlemini gerçekleştirilmesini söylüyoruz.

Basitçe join ifadesinin ne amaçla kullanıldığını, ne işe yaradığını ve temel bir join işleminin nasıl yapılabileceğini özetlemiş olduk. Şimdi biraz daha derinliklerine giderek farklı tipteki joinlerin hangi durumda hangi join işlemini kullanmamız gerektiğinide anlatalım. Bunun için ilk önce tablolarımızı yaratalım ve içlerine biraz veri ekleyelim.

CUSTOMER tablosunun oluşturulması ve veri ekleme:

CREATE TABLE customers (
customer_id INT PRIMARY KEY,
customer_name VARCHAR(100),
city VARCHAR(50),
country VARCHAR(50)
);

INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1001, 'Ahmet Yılmaz', 'İstanbul', 'Türkiye');
INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1002, 'Ayşe Demir', 'Ankara', 'Türkiye');
INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1003, 'Mehmet Kaya', 'İzmir', 'Türkiye');
INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1004, 'Fatma Öztürk', 'Bursa', 'Türkiye');
INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1005, 'Ali Can', 'Adana', 'Türkiye');
INSERT INTO customers (customer_id, customer_name, city, country)
VALUES (1006, 'Zeynep Gül', 'Antalya', 'Türkiye');

ORDER tablosunun oluşturulması ve veri ekleme:

CREATE TABLE orders (
order_id INT PRIMARY KEY,
order_date DATE,
customer_id INT,
total_amount DECIMAL(10, 2)
FOREIGN KEY (CustomerID) REFERENCES Customers(CustomerID)
);

INSERT INTO orders (order_id, order_date, customer_id, total_amount)
VALUES (1, '2023-04-01', 1001, 150.00);

INSERT INTO orders (order_id, order_date, customer_id, total_amount)
VALUES (2, '2023-04-05', 1002, 200.00);

INSERT INTO orders (order_id, order_date, customer_id, total_amount)
VALUES (3, '2023-04-10', 1003, 75.50);

Yukarıdaki sorgular, ORDER ve CUSTOMER tablolarını oluşturur ve örnek verileri ekler.

JOIN TIPLERI VE KULLANIMI

JOIN işlemi, farklı JOIN tipleri ile çeşitli senaryolara uygun şekilde uygulanabilir.

  1. INNER JOIN: Bu JOIN türü, birincil tablo ve eşleşen kayıtları olan diğer tablo arasında birleştirme yapar. INNER JOIN, her iki tablodan yalnızca eşleşen kayıtları getirir.
SELECT *
FROM orders
INNER JOIN customers
ON orders.customer_id = customers.customer_id;

Bu örnek, orders ve customers tablolarını INNER JOIN ile birleştirir ve her iki tablodan yalnızca eşleşen kayıtları getirir. Burada, ON ifadesiyle, orders tablosundaki customer_id sütunu ile customers tablosundaki customer_id sütunu eşleştirilir.

  • LEFT JOIN: Bu JOIN türü, birincil tablodaki tüm kayıtları ve eşleşen kayıtları olan diğer tablodaki kayıtları birleştirir. LEFT JOIN, her iki tablodan yalnızca eşleşen kayıtları getirirken, birincil tablodaki tüm kayıtları da getirir.
SELECT *
FROM customers
LEFT JOIN orders
ON customers.customer_id = orders.customer_id;

Bu örnek, customers tablosundaki tüm kayıtları ve orders tablosundaki eşleşen kayıtları birleştirir. Ancak, orders tablosunda eşleşen kayıt yoksa bile, customers tablosundaki tüm kayıtları getirir. Eşleşmeyen kayıtlar ise NULL değerleri ile doldurularak gösterilir.

  • RIGHT JOIN: Bu JOIN türü, diğer tablodaki tüm kayıtları ve eşleşen kayıtları olan birincil tablodaki kayıtları birleştirir. RIGHT JOIN, her iki tablodan yalnızca eşleşen kayıtları getirirken, ikincil tablodaki tüm kayıtları da getirir.
SELECT *
FROM orders
RIGHT JOIN customers
ON orders.customer_id = customers.customer_id;

Bu örnekte ise yine customers tablosunda ki değerleri görebilmek için bu sefer orders tablosundaki tüm kayıtları ve customers tablosundaki eşleşen kayıtları birleştirir. Ancak, customers tablosunda eşleşen kayıt yoksa bile, orders tablosundaki tüm kayıtları getirir. Eşleşmeyen kayıtlar ise NULL değeri ile gösterilir.

  • FULL OUTER JOIN: iki tablodaki tüm kayıtları getirir ve eşleşmeyen kayıtlar NULL değeri ile doldurulur. FULL OUTER JOIN, LEFT JOIN ve RIGHT JOIN işlemlerinin birleşimi gibi çalışır ve genellikle bir tabloda bulunan tüm kayıtların diğer tablodaki ilgili kayıtlarının alınması gereken durumlarda kullanılır.
SELECT *
FROM customers
FULL OUTER JOIN orders ON customers.customer_id = orders.customer_id;

WHERE Yapıları ile Join Işlemi Yapılabilir mi?

JOIN işlemi ile WHERE komutu arasında doğrudan bir denklik yoktur. JOIN işlemi, ilişkili kayıtları birleştirmek için kullanılırken, WHERE komutu ise filtreleme işlemleri için kullanılır. Yine de tabloları birleştirmek için kullanılabilen bir yöntemdir. Bu yöntem, eski bir JOIN formatıdır.

SELECT *
FROM orders, customers
WHERE orders.customer_id = customers.customer_id;

Bu sorgu da customer_id sütununu kullanarak orders ve customers tablolarını birleştirir.

Frameworkler’de durumlar nasil?

Frameworkler işlerimizi kolaylaştırıyor ve bizim için hazır Repository’ler aracılığıyla sorguları otomatik oluşturabiliyor. Peki join işlemlerinde nasıl bir davranış sergiliyorlar biraz bu soruyu irdeleyelim. Örneğin Order sınıfımızda böyle bir join işlemi uygulayalım.

@JoinTable( name = "order_customer",
joinColumns = @JoinColumn(name = "customer_id"))
private Customer customer;

Basitçe Repositoryimizi oluşturalım. Join işleminin arkada nasıl bir sorgu oluşturduğunu inceleyebiliriz.

@Repository
public interface OrderRepo extends JpaRepository<Order, Long> {

List<Order> findByCustomerId(Long customerId);
}

Aslında sadece ingilizce sözcükler yazarak Frameworklerin ne kadar kolay bir şekilde sorgu ürettiğini görebiliriz. Arka planda ise raw query olarak bize otomatik oluşturulmuş sorgular ürettiğini tahmin edebiliriz.

select o1_0.order_id,o1_0.total_amount,o1_1.customer_customer_id from orders o1_0 left join order_customer o1_1 on o1_0.order_id=o1_1.customer_id where o1_1.customer_customer_id=?

Oluşturulan sorgunun kolay okunabilir olmadığını söyleyebiliriz. Oluşturulan sorgu LEFT JOIN işlemi kullanılarak yapıldığını görebilirsiniz. Biz tablolarını join ettiğimiz için aslında çok mantıklı ve tutarlı bir çözüm üretmiş gibi duruyor. Farklı ve daha sık kullanılan bir birleştirme yöntemi kullandığımızda durum biraz daha farklılaşıyor.

@JoinColumn(name = "customer_id")
private Customer customer;

Böyle bir join işleminde aslında yaptığımız tabloları join etmek olmadığı için framework tabloları birleştirme ihtiyacını gütmüyor. Aynı Repository’i kullansak bile yaptığımız işlem tamamen farklı bir raw query’e bürünüyor.

select o1_0.order_id,o1_0.customer_id,o1_0.total_amount from orders o1_0 where o1_0.customer_id=?

Görüldüğü üzere tabloları birleştirmesini söylemediğimiz için daha önce de belirttiğimiz üzere WHERE koşulu ile bir filtreleme görevi görüyor. Daha farklı sorgularda daha farklı sonuçlar almak mümkün. Özellikle kompleks veya çok fazla veri çekmemiz gereken query’lerde oluşuturulan raw queryleri debug modda incelemeliyiz. Performansı etkileyecek parçalar var ise otomatik oluşuturulmuş query’ler yerine kendimizin raw query yazması daha fazla performans sağlayabilir. Dolayısıyla veritabanı işlemlerinde her zaman bi gözümüz oluşturulan query’lerde olursa uygulamamızın performansına yönelik hamleler yapabilmemiz kolaylaşacaktır.

SONUÇ

Join işlemleri özellikle tabloları birleştirmek için kullanılan temel SQL komutlarından biridir, birden fazla farklı tipi bulunur ve bunlar ihtiyaca göre doğru bir şekilde kullanılmalıdır.
Genel olarak, JOIN işlemlerini kullanırken dikkat etmeniz gereken noktalar; veri yoğunluğu, yetersiz/yanlış indeksleme, yanlış joinler sonucu fazla veri dönme sorunu ve yanlış PK ve FK kullanımı, veritabanı tasarımı gibi faktörleri göz önünde bulundurmanızdır. Bu durumlar, JOIN işlemlerinin performansını etkileyebilir ve gereksiz karmaşıklığa yol açabilir. Alternatif yaklaşımların kullanılması veya veritabanı tasarımının yeniden değerlendirilmesi gerekebilir.

Belirtilen tüm SQL sorgularını online olarak deneyebilirsiniz: https://sqliteonline.com/

— Bana ulaşmak için: Twitter, Linkedin

— 1:1 görüşmeler için: Superpeer

https://gokhana.dev

Yazan Konusuyor Podcast

--

--

Gökhan Ayrancıoğlu

Software Engineer @Yemeksepeti • #Java • #Spring Boot • #Kotlin • #Spark • #Microservices • https://gokhana.dev