Çoklu Tablo Tablosu Değerli İşlev ve Satır İçi Tablo Değerli İşlev


199

Göstermek için birkaç örnek, sadece örtmek:

Satır İçi Tablo Değerli

CREATE FUNCTION MyNS.GetUnshippedOrders()
RETURNS TABLE
AS 
RETURN SELECT a.SaleId, a.CustomerID, b.Qty
    FROM Sales.Sales a INNER JOIN Sales.SaleDetail b
        ON a.SaleId = b.SaleId
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.ShipDate IS NULL
GO

Çoklu Tablo Tablosu Değerli

CREATE FUNCTION MyNS.GetLastShipped(@CustomerID INT)
RETURNS @CustomerOrder TABLE
(SaleOrderID    INT         NOT NULL,
CustomerID      INT         NOT NULL,
OrderDate       DATETIME    NOT NULL,
OrderQty        INT         NOT NULL)
AS
BEGIN
    DECLARE @MaxDate DATETIME

    SELECT @MaxDate = MAX(OrderDate)
    FROM Sales.SalesOrderHeader
    WHERE CustomerID = @CustomerID

    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a INNER JOIN Sales.SalesOrderHeader b
        ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c ON b.ProductID = c.ProductID
    WHERE a.OrderDate = @MaxDate
        AND a.CustomerID = @CustomerID
    RETURN
END
GO

Bir türün (satır içi veya çoklu deyim) diğerine göre kullanılmasının bir avantajı var mı? Biri diğerinden daha iyi olduğunda veya farklılıklar tamamen sözdizimsel olduğunda belirli senaryolar var mı? İki örnek sorgunun farklı şeyler yaptığını anlıyorum, ancak bunları bu şekilde yazmamın bir nedeni var mı?

Onlar hakkında okumalar ve avantajlar / farklılıklar gerçekten açıklanmamıştır.


Ayrıca, satır içi işlevinin en büyük avantajlarından biri ROWID (TIMESTAMP) sütunlarını seçebilmenizdir, oysa çoklu durum işlevindeki dönüş tablosuna TIMESTAMP verilerini ekleyemezsiniz!
Artru

3
Mükemmel bir iplik için teşekkürler. Çok şey öğrendim. Ancak, akılda tutulması gereken bir şey ITV'den MSTV'ye bir işlevi değiştirirken profiler bir ITV'yi değiştirdiğinizi düşünüyor. Sözdizimini bir MSTV açısından elde etmek için ne yaparsanız yapın, yeniden derleme her zaman başarısız olur, genellikle BEGIN'den sonraki ilk ifade etrafında. Bunun tek yolu eski işlevi DROP ve yeni bir MSTV OLUŞTURMAK oldu.
fandango68

Yanıtlar:


141

Matt'in yorumunu araştırırken orijinal ifademi gözden geçirdim. Doğru, bir satır içi tablo değerli işlevi (ITVF) ve çoklu tablo değerli bir işlevi (MSTVF) arasında performans farkı, her ikisi de bir SELECT deyimi yürütseler bile. SQL Server, bir ITVF'yeVIEWsöz konusu tablolardaki en son istatistikleri kullanarak bir yürütme planı hesaplayacaktır. Bir MSTVF, SELECT ifadenizin tüm içeriğini bir tablo değişkenine doldurmaya ve sonra buna katılmaya eşdeğerdir. Bu nedenle, derleyici MSTVF tablolarda herhangi bir tablo istatistiklerini kullanamaz. Böylece, her şey eşittir (nadiren oldukları gibi), ITVF MSTVF'den daha iyi performans gösterecektir. Testlerimde, tamamlanma süresindeki performans farkı göz ardı edilebilirdi, ancak istatistik açısından bakıldığında fark edildi.

Sizin durumunuzda, iki işlev işlevsel olarak eşdeğer değildir. MSTV işlevi her çağrıldığında fazladan bir sorgu yapar ve en önemlisi müşteri kimliğini filtreler. Büyük bir sorguda, optimize edici, geçen her customerId için işlevi çağırması gerekeceğinden diğer birleştirme türlerinden yararlanamaz. Ancak, MSTV işlevinizi şu şekilde yeniden yazdıysanız:

CREATE FUNCTION MyNS.GetLastShipped()
RETURNS @CustomerOrder TABLE
    (
    SaleOrderID    INT         NOT NULL,
    CustomerID      INT         NOT NULL,
    OrderDate       DATETIME    NOT NULL,
    OrderQty        INT         NOT NULL
    )
AS
BEGIN
    INSERT @CustomerOrder
    SELECT a.SalesOrderID, a.CustomerID, a.OrderDate, b.OrderQty
    FROM Sales.SalesOrderHeader a 
        INNER JOIN Sales.SalesOrderHeader b
            ON a.SalesOrderID = b.SalesOrderID
        INNER JOIN Production.Product c 
            ON b.ProductID = c.ProductID
    WHERE a.OrderDate = (
                        Select Max(SH1.OrderDate)
                        FROM Sales.SalesOrderHeader As SH1
                        WHERE SH1.CustomerID = A.CustomerId
                        )
    RETURN
END
GO

Bir sorguda, iyileştirici bu işlevi bir kez çağırabilir ve daha iyi bir yürütme planı oluşturabilir, ancak yine de eşdeğer, parametreleştirilmemiş bir ITVS veya a'dan daha iyi olmaz VIEW.

Mümkün olduğunda ITVF'ler MSTVF'lere göre tercih edilmelidir, çünkü tablodaki sütunlardan veri türleri, nullabilite ve harmanlama, bu özellikleri çok ifadeli tablo değerli bir işlevde beyan edersiniz ve daha da önemlisi, ITVF'den daha iyi yürütme planları alırsınız. Deneyimlerime göre, bir ITVF'nin bir VIEW'dan daha iyi bir seçenek olduğu pek çok koşul bulamadım, ancak kilometre değişebilir.

Matt sayesinde.

İlave

Son zamanlarda bunu gördüğümden beri, burada Wayne Sheffield tarafından Satır İçi Tablo Değerli işlevler ve Çoklu Tablo işlevleri arasındaki performans farkını karşılaştıran mükemmel bir analiz var.

Orijinal blog yazısı.

SQL Server Central'a kopyalama


40
Bu sadece doğru değil - Çok ifadeli işlevler genellikle çok iyi bir performans isabeti. Çok ifadeli işlev kullanımının çok zayıf bir yürütme planı seçimine neden olduğunu gördüğümde 1 dolarım olsaydı (çoğunlukla döndürülen satır sayısını 1 olarak tahmin ettiğinden), küçük bir araba satın almak için yeterli olurdu.
Matt Whitfield

Şimdiye kadar bulduğum en iyi açıklama ilk cevapta ve ilgili yazıda: stackoverflow.com/questions/4109152/… İlgili belgeyi kaçırmayın, hızlı bir şekilde okuyabilirsiniz ve son derece ilginç.
JotaBe

1
SQL Server 2017 için bu yanıtta bir güncelleme olacak mı ?: youtube.com/watch?time_continue=2&v=szTmo6rTUjM
Ralph

29

Dahili olarak SQL Server, satır içi tablo değerli bir işlevi bir görünümde olduğu gibi ele alır ve çok deyimli tablo değerli bir işlevi, saklı yordam gibi davranır.

Dış sorgunun bir parçası olarak satır içi tablo değerli bir işlev kullanıldığında, sorgu işlemcisi UDF tanımını genişletir ve bu nesneler üzerindeki dizinleri kullanarak temel alınan nesnelere erişen bir yürütme planı oluşturur.

Çok ifadeli tablo değerli bir işlev için, işlevin kendisi için bir yürütme planı oluşturulur ve yürütme planı önbelleğinde depolanır (işlev ilk kez yürütüldüğünde). Çok ifadeli tablo değerli işlevler daha büyük sorguların bir parçası olarak kullanılırsa, iyileştirici işlevin ne döndürdüğünü bilmez ve bu nedenle bazı standart varsayımlar yapar - aslında işlevin tek bir satır döndüreceğini ve işleve, tek sıralı bir tabloya karşı bir tablo taraması kullanılarak erişilir.

Çok ifadeli tablo değerli işlevlerin kötü performans gösterebildiği yerler, çok sayıda satır döndürdüğü ve dış sorgularda birleştirildiği zamandır. Performans sorunları öncelikle optimize edicinin tek bir satır döndürüldüğünü varsayarak bir plan üreteceği gerçeğine bağlıdır ve bu da en uygun plan olmayacaktır.

Genel bir kural olarak, bu potansiyel performans sorunları nedeniyle mümkün olduğunda satır içi tablo değerli işlevlerin çoklu deyim işlevlerine (UDF dış sorgunun bir parçası olarak kullanıldığında) tercih edilmesi gerektiğini bulduk.


2
Saklı yordama benzer çok ifadeli tablo değerli işlevleri tedavi edebilse de, işlevsel olarak özdeş bir saklı yordam büyük veri kümeleri için tablo değerli bir işlevden çok daha hızlıdır. Çok deyimli tablo değerli fonksiyonlar üzerinde saklı procs ile yapışıyorum.
Kekoa

6
Bu sonuçları başka bir sorguda birleştirmeniz gerekmedikçe.
Guillermo Gutiérrez

neden ikisini de kullanmıyorsun? Çok ifadeli tablo değerli bir işlevin sonucunu döndüren saklı bir proc. Her iki dünyanın en iyisi.
Robino

13

Başka bir fark daha var. Tıpkı bir görünümde olduğu gibi, satır içi tablo değerli bir işlev eklenebilir, güncellenebilir ve buradan silinebilir. Benzer kısıtlamalar geçerlidir - toplamalar kullanılarak işlevler güncellenemez, hesaplanan sütunlar güncellenemez vb.


3

Örnekleriniz bence soruyu çok iyi cevaplıyor. İlk işlev tek bir seçim olarak yapılabilir ve satır içi stilini kullanmak için iyi bir nedendir. İkincisi muhtemelen tek bir ifade olarak yapılabilir (maksimum tarihi almak için bir alt sorgu kullanarak), ancak bazı kodlayıcılar bunu yaptığınız gibi birden çok ifadede okumayı daha kolay veya daha doğal olarak bulabilir. Bazı işlevler sadece bir deyimde gerçekleştirilemez ve bu nedenle çoklu deyim sürümünü gerektirir.

Mümkün olduğunca basit (satır içi) kullanmanızı ve gerektiğinde (açıkça) çoklu kişisel ifadeler kullanmanızı veya kişisel tercih / okunabilirliği ekstra yazmayı gerektirdiğinde öneririm.


Cevap için teşekkürler. Yani temel olarak, çoklu ifade yalnızca işlev, satır içi işlevde yapılabilecek olandan daha karmaşık olduğunda, okunabilirlik uğruna kullanılmalıdır. Çoklu beyanda performans açısından herhangi bir fayda var mı?
Mart'ta AndrewC

Bilmiyorum, ama öyle düşünmezdim. Sql sunucusunun manuel olarak (değişkenleri, geçici tabloları veya herhangi bir şeyi kullanarak) yapmaya çalışabileceğiniz optimizasyonları bulmasına izin vermek daha iyidir. Gerçi bazı durumlarda bunu kanıtlamak / çürütmek için bazı performans testleri yapabilirsiniz.
Ray

Tekrar çok teşekkürler. Daha fazla zamanım olduğunda buna daha fazla bakabilirim! :)
AndrewC


0

Bunu test etmedim, ancak çoklu ifade işlevi sonuç kümesini önbelleğe alır. Optimize edicinin işlevi satır içine alması için çok fazla şeyin olduğu durumlar olabilir. Örneğin, "Şirket Numarası" olarak ilettiğiniz şeye bağlı olarak farklı veritabanlarından sonuç döndüren bir işleviniz olduğunu varsayalım. Normalde, bir sendika ile bir görünüm oluşturabilir, sonra şirket numarasına göre filtreleyebilirsiniz, ancak bazen sql sunucusunun tüm sendikayı geri çektiğini ve birini seçecek kadar akıllı olmadığını gördüm. Bir tablo işlevinin kaynağı seçmek için mantığı olabilir.


0

Çok satırlı bir işlevi kullanmak için başka bir durum sql sunucusunun where yan tümcesini aşağı itmesini önlemek olacaktır.

Örneğin, tablo adlarına sahip bir tablo var ve bazı tablo adları C05_2019 ve C12_2018 gibi biçimlendirilmiş ve bu şekilde biçimlendirilmiş tüm tablolar aynı şemaya sahip. Tüm bu verileri tek bir tabloda birleştirmek ve 05 ve 12'yi bir CompNo sütununa ve 2018,2019'u bir yıl sütununa ayrıştırmak istedim. Ancak, ACA_StupidTable gibi CompNo ve CompYr ayıklayamaz ve denedim, bir dönüştürme hatası alacağı gibi diğer tablolar vardır. Yani, benim sorgu iki bölümden, sadece 'C_______' gibi biçimlendirilmiş tabloları döndüren bir iç sorgu sonra dış sorgu bir alt dize ve int dönüşüm yaptı. yani CompNo olarak Cast (Substring (2, 2) int olarak). Sql sunucusu sonuçlar filtrelenmeden önce Cast işlevimi koymaya karar vermesi dışında tüm iyi görünüyor ve bu yüzden bir zihin dönüştürme hatası aldım. Çoklu ifade tablosu işlevi bunun olmasını engelleyebilir,


0

Belki çok yoğun bir şekilde. ITVF (satır içi TVF): daha fazla DB kişisiyseniz, bir tür parametreli görünümdür, tek bir SELECT st alın

MTVF (Çoklu ifadeli TVF): Geliştirici, bir tablo değişkeni oluşturur ve yükler.


-2

bir sorgu yapacaksanız, Satır İçi Tablo Değerli işlevinize aşağıdaki gibi katılabilirsiniz:

SELECT
    a.*,b.*
    FROM AAAA a
        INNER JOIN MyNS.GetUnshippedOrders() b ON a.z=b.z

biraz ek yüke neden olacak ve iyi çalışacaktır.

Değerli Çoklu Tablo Tablonuzu benzer bir sorguda kullanmaya çalışırsanız, performans sorunlarınız olacaktır:

SELECT
    x.a,x.b,x.c,(SELECT OrderQty FROM MyNS.GetLastShipped(x.CustomerID)) AS Qty
    FROM xxxx   x

işlevi, döndürülen her satır için 1 kez yürüteceğiniz için, sonuç kümesi büyüdükçe, daha yavaş ve daha yavaş çalışacaktır.


Ah, yani satır içi performans açısından çok daha iyi?
Mart'ta AndrewC

1
Hayır, ikisi de bir tablo döndürür, bu da bir sütuna tablo koymaya çalışırken ikinci SQL'inizi geçersiz kılar.
cjk

1
@ck, yo'nun yorumladığı sorguyu güncelledim. ikinci işlevde kullanılan işlevin parametreleri, alt sorgu olarak kullanılmasını sağlar ve bu da daha kötü performansa neden olur.
KM.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.