Multi-statement Table Valued Function vs Inline Table Valued Function

198

Beberapa contoh untuk ditampilkan, cukup dimetikan:

Inline Table Valued

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

Multi Statement Table Dinilai

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

Apakah ada keuntungan menggunakan satu jenis (in-line atau multi pernyataan) daripada yang lain? Adakah skenario tertentu ketika yang satu lebih baik dari yang lain atau apakah perbedaannya murni sintaksis? Saya menyadari dua contoh pertanyaan melakukan hal yang berbeda tetapi apakah ada alasan saya akan menulisnya dengan cara itu?

Membaca tentang mereka dan kelebihan / perbedaan belum benar-benar dijelaskan.

AndrewC
sumber
Juga salah satu manfaat besar fungsi inline adalah bahwa Anda dapat memilih kolom ROWID (TIMESTAMP), sementara Anda tidak dapat memasukkan data TIMESTAMP ke tabel kembali dalam fungsi multistatement!
Artru
3
Terima kasih atas utasnya. Saya sudah belajar banyak. Namun, satu hal yang perlu diingat adalah ketika MENGUBAH fungsi yang ITV ke MSTV, profiler berpikir Anda mengubah ITV. Apa pun yang Anda lakukan untuk mendapatkan sintaks yang benar dari sudut pandang MSTV, kompilasi ulang selalu gagal, biasanya di sekitar pernyataan pertama setelah BEGIN. Satu-satunya cara untuk mengatasi hal ini adalah MENGHENTIKAN fungsi lama dan MENCIPTAKAN yang baru sebagai MSTV.
Fandango68

Jawaban:

141

Dalam meneliti komentar Matt, saya telah merevisi pernyataan asli saya. Dia benar, akan ada perbedaan kinerja antara fungsi nilai tabel inline (ITVF) dan fungsi nilai tabel multi-pernyataan (MSTVF) bahkan jika mereka berdua hanya menjalankan pernyataan SELECT. SQL Server akan memperlakukan ITVF sepertiVIEWkarena akan menghitung rencana eksekusi menggunakan statistik terbaru pada tabel yang dimaksud. MSTVF setara dengan menjejalkan seluruh isi pernyataan SELECT Anda ke dalam variabel tabel dan kemudian bergabung dengannya. Dengan demikian, kompilator tidak dapat menggunakan statistik tabel apa pun pada tabel di MSTVF. Jadi, semua hal dianggap sama, (yang jarang terjadi), ITVF akan tampil lebih baik daripada MSTVF. Dalam pengujian saya, perbedaan kinerja dalam waktu penyelesaian dapat diabaikan namun dari sudut pandang statistik, itu terlihat.

Dalam kasus Anda, kedua fungsi tersebut tidak setara secara fungsional. Fungsi MSTV melakukan kueri tambahan setiap kali dipanggil dan, yang paling penting, filter pada id pelanggan. Dalam kueri besar, pengoptimal tidak akan dapat mengambil keuntungan dari gabungan jenis lain karena akan perlu memanggil fungsi untuk setiap pelanggan yang dilewati. Namun, jika Anda menulis ulang fungsi MSTV Anda seperti ini:

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

Dalam kueri, pengoptimal akan dapat memanggil fungsi itu sekali dan membangun rencana eksekusi yang lebih baik tetapi tetap tidak akan lebih baik daripada ITVS yang setara, non-parameterisasi atau a VIEW.

ITVFs harus lebih disukai daripada MSTVFs jika memungkinkan karena tipe data, nullability dan collation dari kolom dalam tabel sedangkan Anda mendeklarasikan properti-properti tersebut dalam fungsi bernilai tabel multi-pernyataan dan, yang penting, Anda akan mendapatkan rencana eksekusi yang lebih baik dari ITVF. Dalam pengalaman saya, saya belum menemukan banyak keadaan di mana ITVF adalah pilihan yang lebih baik daripada VIEW tetapi jarak tempuh dapat bervariasi.

Terima kasih untuk Matt.

Tambahan

Karena saya melihat ini muncul baru-baru ini, berikut adalah analisis yang sangat baik yang dilakukan oleh Wayne Sheffield membandingkan perbedaan kinerja antara fungsi Inline Table Valued dan fungsi Multi-Statement.

Posting blog aslinya.

Salin di SQL Server Central

Thomas
sumber
40
Ini tidak benar - Fungsi multi-pernyataan sangat sering menjadi hit kinerja besar karena mereka menghentikan optimizer kueri dari menggunakan statistik. Jika saya punya $ 1 untuk setiap kali saya melihat penggunaan fungsi multi-pernyataan menyebabkan pilihan yang sangat buruk dari rencana pelaksanaan (kebanyakan karena biasanya memperkirakan jumlah baris yang dikembalikan sebagai 1), saya akan cukup untuk membeli mobil kecil.
Matt Whitfield
Penjelasan terbaik yang pernah saya temukan adalah di jawaban pertama, dan posting terkait: stackoverflow.com/questions/4109152/… Jangan lewatkan dokumen terkait, Anda dapat membacanya dengan cepat, dan ini sangat menarik.
JotaBe
1
Apakah akan ada pembaruan untuk jawaban ini untuk SQL Server 2017 ?: youtube.com/watch?time_continue=2&v=szTmo6rTUjM
Ralph
29

Secara internal, SQL Server memperlakukan fungsi bernilai tabel inline seperti itu akan melihat dan memperlakukan fungsi multi-pernyataan tabel dihargai mirip dengan bagaimana itu akan prosedur yang tersimpan.

Ketika fungsi tabel bernilai inline digunakan sebagai bagian dari kueri luar, prosesor kueri memperluas definisi UDF dan menghasilkan rencana eksekusi yang mengakses objek yang mendasarinya, menggunakan indeks pada objek ini.

Untuk fungsi bernilai multi-pernyataan tabel, rencana eksekusi dibuat untuk fungsi itu sendiri dan disimpan dalam cache rencana eksekusi (setelah fungsi dijalankan pertama kali). Jika fungsi multi-pernyataan tabel bernilai digunakan sebagai bagian dari kueri yang lebih besar maka pengoptimal tidak tahu apa fungsi kembali, dan membuat beberapa asumsi standar - yang pada dasarnya mengasumsikan bahwa fungsi akan mengembalikan satu baris, dan bahwa pengembalian dari fungsi akan diakses dengan menggunakan pemindaian tabel terhadap tabel dengan satu baris.

Di mana fungsi multi-pernyataan tabel bernilai bisa berkinerja buruk adalah ketika mereka mengembalikan sejumlah besar baris dan bergabung melawan dalam kueri luar. Masalah kinerja terutama karena fakta bahwa pengoptimal akan menghasilkan rencana dengan asumsi bahwa satu baris dikembalikan, yang tidak harus menjadi rencana yang paling tepat.

Sebagai aturan umum, kami telah menemukan bahwa jika mungkin, fungsi inline table bernilai harus digunakan sebagai preferensi terhadap yang multi-pernyataan (ketika UDF akan digunakan sebagai bagian dari kueri luar) karena potensi masalah kinerja ini.

Paul McLoughlin
sumber
2
Meskipun mungkin memperlakukan fungsi multi-pernyataan tabel bernilai mirip dengan prosedur tersimpan, prosedur tersimpan identik secara fungsional jauh lebih cepat daripada fungsi bernilai tabel untuk kumpulan data besar. Saya bertahan dengan procs yang disimpan lebih dari fungsi multi-pernyataan tabel dihargai.
Kekoa
6
Kecuali jika Anda harus bergabung dengan hasil itu di kueri lain.
Guillermo Gutiérrez
mengapa tidak menggunakan keduanya? Proc tersimpan yang mengembalikan hasil fungsi bernilai tabel multi-pernyataan. Terbaik dari kedua dunia.
Robino
13

Ada perbedaan lain. Fungsi bernilai tabel inline dapat dimasukkan ke dalam, diperbarui, dan dihapus dari - seperti halnya tampilan. Pembatasan serupa berlaku - tidak dapat memperbarui fungsi menggunakan agregat, tidak dapat memperbarui kolom terhitung, dan sebagainya.

Craig Beere
sumber
3

Contoh Anda, saya pikir, menjawab pertanyaan dengan sangat baik. Fungsi pertama dapat dilakukan sebagai pilihan tunggal, dan merupakan alasan yang baik untuk menggunakan gaya inline. Yang kedua mungkin dapat dilakukan sebagai pernyataan tunggal (menggunakan sub-kueri untuk mendapatkan tanggal maksimum), tetapi beberapa coders mungkin merasa lebih mudah dibaca atau lebih alami untuk melakukannya dalam banyak pernyataan seperti yang telah Anda lakukan. Beberapa fungsi yang tidak bisa dilakukan dalam satu pernyataan, dan karenanya membutuhkan versi multi-pernyataan.

Saya sarankan menggunakan yang paling sederhana (sebaris) bila memungkinkan, dan menggunakan multi-pernyataan bila perlu (jelas) atau ketika preferensi pribadi / keterbacaan membuatnya dengan mengetik ekstra.

sinar
sumber
Terima kasih atas jawabannya. Jadi pada dasarnya, multi-pernyataan hanya benar-benar digunakan ketika fungsi lebih rumit daripada layak dilakukan dalam fungsi inline, demi keterbacaan? Apakah ada manfaat kinerja sama sekali untuk multi-pernyataan?
AndrewC
Saya tidak tahu, tapi saya rasa tidak. Mungkin lebih baik membiarkan sql server mengetahui optimasi yang mungkin Anda coba lakukan secara manual (dengan menggunakan variabel, temp tables, atau apa pun). Padahal Anda tentu bisa melakukan beberapa pengujian kinerja untuk membuktikan / membantah ini dalam kasus tertentu.
Ray
Terima kasih banyak lagi. Saya mungkin melihat lebih jauh ke dalam ini ketika saya memiliki lebih banyak waktu! :)
AndrewC
0

Saya belum menguji ini, tetapi fungsi pernyataan multi-cache hasil set. Mungkin ada kasus di mana ada terlalu banyak pengoptimal untuk menjalankan fungsi. Misalnya, Anda memiliki fungsi yang mengembalikan hasil dari database berbeda tergantung pada apa yang Anda lewati sebagai "Nomor Perusahaan". Biasanya, Anda bisa membuat tampilan dengan gabungan semua kemudian disaring berdasarkan nomor perusahaan tapi saya menemukan bahwa kadang-kadang server sql menarik kembali seluruh serikat dan tidak cukup pintar untuk memanggil satu pilih. Fungsi tabel dapat memiliki logika untuk memilih sumber.

William Egge
sumber
0

Kasus lain untuk menggunakan fungsi multi-line adalah untuk menghindari sql server menekan klausa where.

Sebagai contoh, saya memiliki tabel dengan nama tabel dan beberapa nama tabel diformat seperti C05_2019 dan C12_2018 dan dan semua tabel yang diformat seperti itu memiliki skema yang sama. Saya ingin menggabungkan semua data itu menjadi satu tabel dan menguraikan 05 dan 12 ke kolom CompNo dan 2018.2019 ke dalam kolom tahun. Namun, ada tabel lain seperti ACA_StupidTable yang saya tidak dapat mengekstrak CompNo dan CompYr dan akan mendapatkan kesalahan konversi jika saya mencoba. Jadi, kueri saya ada di dua bagian, kueri dalam yang mengembalikan hanya tabel yang diformat seperti 'C_______' kemudian kueri luar melakukan konversi sub-string dan int. yaitu Cast (Substring (2, 2) sebagai int) sebagai CompNo. Semua terlihat bagus kecuali bahwa sql server memutuskan untuk meletakkan fungsi Cast saya sebelum hasilnya difilter dan jadi saya mendapatkan kesalahan konversi pikiran. Fungsi tabel multi pernyataan dapat mencegah hal itu terjadi,

William Egge
sumber
0

Mungkin dengan cara yang sangat kental. ITVF (inline TVF): lebih jika Anda adalah orang DB, adalah jenis tampilan parameter, ambil satu SELECT st

MTVF (Multi-statement TVF): Pengembang, membuat dan memuat variabel tabel.

LinchenPal
sumber
-2

jika Anda akan melakukan kueri, Anda dapat bergabung dalam fungsi Nilai Tabel Inline Anda seperti:

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

itu akan menimbulkan sedikit overhead dan berjalan dengan baik.

jika Anda mencoba menggunakan Tabel Multi Pernyataan yang Dinilai dalam kueri serupa, Anda akan memiliki masalah kinerja:

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

karena Anda akan menjalankan fungsi 1 kali untuk setiap baris yang dikembalikan, karena hasil yang ditetapkan menjadi besar, itu akan berjalan lebih lambat dan lebih lambat.

KM.
sumber
Ah, jadi Anda akan mengatakan bahwa inline jauh lebih baik dalam hal kinerja?
AndrewC
1
Tidak, keduanya mengembalikan tabel, yang membuat SQL kedua Anda tidak valid saat Anda mencoba meletakkan tabel di kolom.
cjk
1
@ ya, saya telah memperbarui permintaan yang Anda komentari. parameter fungsi yang digunakan dalam fungsi kedua meminjamkannya untuk digunakan sebagai sub kueri, yang akan menghasilkan kinerja yang lebih buruk.
KM.