MSSQL Server Indexed View

SQL Server’da Indexed View, performans artırmak için kullanılan bir tekniktir. Normal view sadece sorguların yazılmasını kolaylaştırırken, indexed view , sorguların daha hızlı çalışmasını sağlar çünkü sonuçları fiziksel olarak disk üzerinde saklar.

Normal view yapılarında , çalıştırıldıklarında her seferinde temel tablolar üzerinden veriyi dinamik olarak alır.  Normal view yapıları sanal tablolardır. Bellekte herhangi bir yer kaplamazlar. Indexed View, sonuçları fiziksel olarak depolar ve bir clustered index oluşturur.
Daha sonra bu indeksli görünüm, tıpkı bir tablo gibi sorgulanabilir ve performans avantajı sağlar.

1. View Tanımlama:
View oluşturulurken, performans iyileştirme için belirli kurallara uyulması gerekir. Örnek:

CREATE VIEW dbo.MyIndexedView
WITH SCHEMABINDING -- Zorunlu
AS
SELECT 
ProductID, 
COUNT_BIG(*) AS Row_count,
SUM(UnitPrice) AS TotalQuantity
FROM [Sales].[SalesOrderDetail]
GROUP BY ProductID;

Not: View oluştururken scheme ve tablo isimlerinin birlikte yazılması gerekmektedir. Yoksa hata mesajıyla karşılaşmış oluruz.

WITH SCHEMABINDING: Indexed View oluşturulması için zorunludur. Bu, görünüme dahil edilen tabloların şema değişikliklerinden korunmasını sağlar. Schemabinding’in mantığı view’in select ettiği tablolarda bir modifikasyon değişikliği yapılamaz olması. Schemabinding yapıda view’i oluşturduktan sonra view’in select ettiği tablolardaki kolonlarda aşağıdaki gibi bir modifikasyon yapılmak istendiğinde aşağıdaki gibi bir hata alırsınız. Bu yapı kullanılırken tabloların şema yapısının birlikte kullanılması gerekmektedir. View sadece bu yapı eklenerekte oluşturulabilir.

View yapımızı normal bir şekilde oluşturduktan sonra schemabinding  yapısına geçirebiliriz.

CREATE VIEW SalesOrderDetail23
AS
SELECT 
ProductID, 
COUNT(*) AS SaleCount, 
SUM(UnitPrice) AS TotalAmount
FROM [Sales].[SalesOrderDetail]
GROUP BY ProductID;

Değiştirilmiş hali sadece alter ifadesi ve with schemabinding yapısı eklenir.

ALTER VIEW SalesOrderDetail23
WITH SCHEMABINDING
AS
SELECT 
ProductID, 
COUNT(*) AS SaleCount, 
SUM(UnitPrice) AS TotalAmount
FROM [Sales].[SalesOrderDetail]
GROUP BY ProductID;

Not: Select edilen kolonların alians ile birlikte yazılması gerekmektedir. Yoksa hata mesajıyla karşılaşmış oluruz.

COUNT_BIG(*):  Sql server indexed view’lerde tablo satırlarını benzersiz bir şekilde izlemek için bu ifadeyi kullanmaktadır. İndexed view oluşturmak içi zorunlu bir yapı olarak karşımıza çıkmaktadır. Yoksa aşağıdaki şekilde hata mesajıyla karşılaşırız.

Çünkü indexed view yapılarında group by ifadelerinde COUNT_BIG ifadesinin yazılması gerekmektedir. Eğer view yapımızı WITH SCHEMABINDING yapısıyla oluştururken COUNT_BIG(*) ifadesi yazmayı unuttunuz oluşturduğunuz view altında index oluşturmak istediğinizde aşağıdaki hata mesajı alınmaktadır. Bunun için view yapımızın düzeltilmesi gerekmektedir.

Msg 10138, Level 16, State 1, Line 10

Cannot create index on view ‘AdventureWorks2014.dbo.MyIndexedView’ because its select list does not include a proper use of COUNT_BIG. Consider adding COUNT_BIG(*) to select list.


İlgili view yapısına select çektiğimize verilerin dağınık bir şekilde olduğunu görmekteyiz.

2. Clustered Index Oluşturma:
Indexed View’ı etkinleştirmek için bir clustered index oluşturulmalıdır:

CREATE UNIQUE CLUSTERED INDEX IX_MyIndexedView_ProductID
ON [dbo].[MyIndexedView](ProductID desc);

İlgili view’e select çektiğimizde verilerimiz sıralı bir şekilde gelmektedir.

Not: Unique clustered index ekledikten sonra  non-clustered index eklenebilir.

Önemli Noktalar
1. Performans İyileştirme:
Yoğun sorgular için performans iyileştirir.
Özellikle karmaşık aggregation, JOIN işlemleri içeren sorgular için uygundur.

2. Güncellemeler:
Indexed View’lar temel tablolardaki değişikliklerden otomatik olarak etkilenir.
Ancak, bu işlem fazladan işlem yükü getirir. Yüksek yazma (insert/update/delete) işlemleri olan tablolar için uygun olmayabilir.

3. Kısıtlamalar:
Görünümde kullanılan tüm tablo ve sütunlar, aynı şemada (schema) olmalıdır.
Görünüm, sadece deterministik (öngörülebilir) ifadeler içermelidir.
SELECT  kullanılamaz.
TEXT, IMAGE, XML, veya VARCHAR(MAX) gibi veri türleri desteklenmez.

4. Query Hints:
Indexed View’ların otomatik olarak kullanılması için Query Optimizer uygun sorgu planını seçebilir.
Ancak, gerektiğinde WITH (NOEXPAND) kullanarak, görünüme zorla erişim sağlanabilir:

SELECT  *
FROM dbo.MyIndexedView WITH (NOEXPAND);

Indexed View Kullanım Örnekleri
1. Aggregation işlemlerinde kullanılır.

CREATE VIEW SalesOrderDetail_view
WITH SCHEMABINDING
AS
SELECT 
ProductID, 
COUNT(*) AS SaleCount, 
SUM(UnitPrice) AS TotalAmount
FROM [Sales].[SalesOrderDetail]
GROUP BY ProductID;

2. Join ve Filtreleme işlemlerinde birden fazla tabloyu birleştirme işlemlerinde kullanılır.

CREATE VIEW ProductSales34  
WITH SCHEMABINDING  
AS  
SELECT  
    p.ProductID,  
    p.Name AS ProductName,  
    SUM(s.OrderQty) AS TotalQuantity  
FROM Production.Product p  
INNER JOIN Sales.SalesOrderDetail s ON p.ProductID = s.ProductID  
GROUP BY p.ProductID, p.Name;


CREATE UNIQUE CLUSTERED INDEX IX_ProductSales
ON ProductSales (ProductID);

Yukarıdaki kod yapısında eğer index oluştururken aşağıdaki hata mesajıyla karşılaşırsak

Kod yapımıza COUNT_BIG(*) ifadesinin eklenmesi gerekmektedir. Sql server indexed view’lerde tablo satırlarını benzersiz bir şekilde izlemek için bu ifadeyi kullanmaktadır. İndexed view oluşturmak içi zorunlu bir yapı olarak karşımıza çıkmaktadır.

ALTER VIEW MyIndexedView  
WITH SCHEMABINDING  
AS  
SELECT  
    p.ProductID,  
    p.Name AS ProductName, 
	COUNT_BIG(*) AS Row_count,
    SUM(s.OrderQty) AS TotalQuantity  
FROM Production.Product p  
INNER JOIN Sales.SalesOrderDetail s ON p.ProductID = s.ProductID  
GROUP BY p.ProductID, p.Name;

Yukarıdaki değişimden sonra başarılı bir şekilde indexsimiz oluşmaktadır.
Indexed View’ın Avantaj ve Dezavantajları
Avantajlar:
Okuma sorgularında büyük performans kazancı sağlar.
Yoğun raporlama ve analitik işlemler için idealdir.
Karmaşık hesaplamaları önceden depoladığı için hızlı yanıtlar sunar.

Dezavantajlar:
Temel tablolar güncellendiğinde, indexed view’lar otomatik olarak yeniden güncellenir. Bu, yazma performansını olumsuz etkileyebilir.
Karmaşıklığı artırabilir ve tasarım maliyeti gerektirir.
Ek disk alanı kullanımı gerektirir.


Not:  Herhangi bir view oluşturulurken tüm ifadeler kullanılır. Ama bir view indexed view olarak yapılmak istenirse şimdi bahsedeceğimiz ifadeleri desteklememektedir. Tekrar söylüyorum normal view oluştururken bu kısıtlamalara takılmayız. Şimdi Indexed viewlerde kısıtlı olan ifadelere gelmek gerekirse sırasıyla: Top,Min,Max,Order By,UNION,UNION ALL,Having,Cross Apply,Distinct,Lef-Right-Full join türlerini vs ifadelerini kullanamayız. Sadece Inner join kullanılmaktadır. Having yerine where ifadesi kullanılması gerekmektedir.

Not: Indexed View içinde bazı veri tipleri yasaktır: TEXT, NTEXT, IMAGE, XML, VARCHAR(MAX), VARBINARY(MAX), GEOMETRY, GEOGRAPHY. Çözüm: TEXT yerine NVARCHAR kullan, XML yerine string tipleri kullanılması gerekmektedir.

Not:Indexed View aşağıdaki tablolara referans veremez:  Geçici tablolar (#TempTables),Değişken tablolar (@TableVariables), CTE (Common Table Expressions – WITH ifadesi),  Linked Server tabloları (Uzaktaki SQL Server veritabanına bağlanamaz)

Not: Kullanılamayan Group By Özellikleri Indexed View’lerde Group By kullanılabilir ama:  Group By’da ROLLUP, CUBE, GROUPING SETS kullanamazsın. HAVING eklenemez.

Not: View’lerin tablo ile aynı sonuç kümesini döndürmesi için SET seçeneklerine dikkat etmeniz gerekir. Çünkü farklı SET seçenekleri farklı sonuçlar doğurabilir. Örneğin SET CONCAT_NULL_YIELDS_NULL ON ifadesi view’in başında olursa select ifadesinde ‘x’+null dediğinizde sonuç null gelecektir. SET CONCAT_NULL_YIELDS_NULL OFF ifadesi view’in başında olursa bu değer bu sefer x olarak dönecektir.

set concat_null_yields_null on
select 'x'+null
set concat_null_yields_null off
select 'x'+null

Not: Aşağıdaki komut sayesinde bir view’in toplam boyutunu bulabiliriz. İndexed view değil normal bir view.

DECLARE @sql NVARCHAR(MAX);

-- Dinamik kolon listesi oluşturma
SELECT @sql = ISNULL(@sql + ' + ', '') + 'ISNULL(DATALENGTH(' + COLUMN_NAME + '), 0)'
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'vEmployee';

-- Toplam boyut sorgusunu oluştur
SET @sql = 'SELECT SUM(' + @sql + ') AS TotalSizeInBytes FROM HumanResources.vEmployee';

-- Dinamik sorguyu çalıştır
EXEC sp_executesql @sql;

Not: Distributed Partitioned View Birden fazla instance üzerindeki tablolardaki kayıtları UNION ALL ile bir araya getirebildiğimiz yapıdır. Lİnked server ile farklı instance altındaki tablolardan verileri alıp birleştirebiliriz. Normal view veya partitioned view yapılarındaki tabloların aynı instance altında olması gerekmektedir.

Not: Aşağıdaki komut ile view yapımızı bu şekilde oluşturabiliriz. Select sorgumuzda kullanıcı kolonlarının belirtilmesi gerekmektedir.

Create view ViewKullanicilar4
with SCHEMABINDING
as
select AdSoyad from dbo.Kullanicilar 

Aşağıdaki resimdeki gibi bir güncelleme yapılmak istendiğinde hata mesajıyla karşılaşmış oluruz.

Bu makalede Indexed View ifadesini detaylı bir şekilde görmüş olduk. Başka makalede görüşmek üzere..

“Ey Rabbimiz! Sen bizim gizlediğimizi de açığa vurduğumuzu da şüphesiz bilirsin. Çünkü yerde ve gökte, hiçbir şey Allah’tan gizli kalmaz.İbrahim-38

Author: Yunus YÜCEL

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir