Builder

web application developer blog

LATERAL Joins veya CROSS APPLY ile Tekrarsız Alt Sorgular Yazmak

İleri seviye SQL kullanımında, standart JOIN ile ilişkilendirmelerin artık işe yaramamaya başladığı ve iç içe SELECT sorguları yazmaya başladığınızda, aynı alt-sorgunun hem SELECT içinde, hem WHERE içinde belki başka bloklarda da kullanmanız gerekebilir.

İşte o zaman bu alt-sorgular RDBMS’in  canına okuyacaktır. Eğer bir SQL GURU’su iseniz ne dediğimi çoktan anlamışsınızdır 🙂

Bu problemin çözümü PostgreSQL ‘de LATERAL, MSSQL’de CROSS APPLY, Oracle’da da LATERAL ve kullanımı PostgreSQL ile aynı. MySQL’de bu işlevi yerine getirecek bir terim henüz mevcut değil.

Şimdi bir ilişkisel veri tabanı modeli çıkartalım ve örneklerimizi onun üzerinden anlatalım:

Tekrarsiz alt sorgu için örnek DB ER diyagramı

PostgreSQL için SQL kodları:

CREATE TABLE tbl_category (
    category_id SERIAL  NOT NULL,
    category_name CHARACTER VARYING(255),
    CONSTRAINT PK_tbl_category PRIMARY KEY (category_id)
);

CREATE TABLE tbl_product (
    product_id BIGSERIAL  NOT NULL,
    product_name CHARACTER VARYING(255),
    created_at TIMESTAMP,
    category_id INTEGER,
    CONSTRAINT PK_tbl_product PRIMARY KEY (product_id)
);


ALTER TABLE tbl_product ADD CONSTRAINT tbl_category_tbl_product 
    FOREIGN KEY (category_id) REFERENCES tbl_category (category_id);

MSSQL için SQL kodları:

CREATE TABLE [tbl_category] (
    [category_id] INTEGER IDENTITY(0,1) NOT NULL,
    [category_name] VARCHAR(255),
    CONSTRAINT [PK_tbl_category] PRIMARY KEY ([category_id])
)
GO


CREATE TABLE [tbl_product] (
    [product_id] BIGINT IDENTITY(0,1) NOT NULL,
    [product_name] VARCHAR(255),
    [created_at] TIMESTAMP,
    [category_id] INTEGER,
    CONSTRAINT [PK_tbl_product] PRIMARY KEY ([product_id])
)
GO


ALTER TABLE [tbl_product] ADD CONSTRAINT [tbl_category_tbl_product] 
    FOREIGN KEY ([category_id]) REFERENCES [tbl_category] ([category_id])
GO

TEKRARLI  ALT SORGU İÇİN ÖRNEK:

10’dan fazla ürün içeren kategorileri ve içerdikleri ürün sayılarını listeleyen bir sorgu yazalım:

Elbette bu basit örneği GROUP BY kullanarak da çözmek mümkün ancak burada amacımız alt-sorgu oluşturmak zorunda kaldığımız bir durumu küçük bir örnekle modellemek.

SELECT *,
       (SELECT COUNT(*)
          FROM
             tbl_product
           WHERE
             tbl_product.category_id = tbl_category.category_id
       ) AS product_number
FROM
  tbl_category
WHERE
      (SELECT COUNT(*)
          FROM
            tbl_product
          WHERE
            tbl_product.category_id = tbl_category.category_id
      ) > 10

Burada, aynı alt-sorgu iki kez işletiliyor. Bu bize bir kaç türlü olumsuz dönüş sağlar:

  • SQL kodu uzun olacağından, kod okunabilirliği azalır. Uzun sorguların, veritabanı sunucusuna iletilmesi ve anlaşılabilmesi de nispeten yavaştır. Ancak günümüz ağ teknolojilerinde bu hissedilebilir düzeyde olmayacaktır.
  • Eğer veritabanı sunucusu bir cache mekanizması kullanmıyorsa, aynı alt sorguyu iki kez çalıştırmak zorunda kalacaktır. Tabi bu arada ana sorgu da stack de kalacağından fazladan bellek tüketimi demektir.

gelelim LATERAL yada CROSS APPLY deyimlerine:

Ayını SQL kodunu LATERAL ile PostgreSQL için yazacak olursak:

TEKRARSIZ  ALT SORGU İÇİN ÖRNEKLER:

SELECT *,
       product_number_by_category.product_number
FROM
  tbl_category,
  LATERAL (SELECT COUNT(*) as product_number
          FROM
            tbl_product
          WHERE
            tbl_product.category_id = tbl_category.category_id) as product_number_by_category
      
WHERE
     product_number_by_category.product_number > 10

Aynı SQL kodunu CROSS APPLY ile MSSQL için yazacak olursak:

SELECT *,
       product_number_by_category.product_number
FROM
  tbl_category
  CROSS APPLY (SELECT COUNT(*) as product_number
          FROM
            tbl_product
          WHERE
            tbl_product.category_id = tbl_category.category_id) as product_number_by_category
      
WHERE
     product_number_by_category.product_number > 10

Gözlemleyebileceğiniz gibi LATERAL yada CROSS APPLY ile alınan alt sorgular, tek bir kayıt içeriyor olsalar bile ayrı bir tablo gibi davranmaktadırlar. Bu durumda bu ayrı tabloya JOIN yapmamak için de hiç bir sebep yok.

 

 

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir