ฟังก์ชันหลายค่าที่มีค่าเป็นตารางเทียบกับฟังก์ชันที่มีค่าของตารางแบบอินไลน์


199

ตัวอย่างที่จะแสดงเพียงใส่ในกรณี:

ค่าในตาราง Inline

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

ตารางคำสั่งหลายค่า

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

มีความได้เปรียบในการใช้หนึ่งประเภท (คำสั่งในบรรทัดหรือหลายคำสั่ง) กับอีกประเภทหนึ่งหรือไม่? มีสถานการณ์บางอย่างเมื่อสิ่งหนึ่งดีกว่าอีกสถานการณ์หนึ่งหรือมีความแตกต่างของการสร้างประโยคอย่างแท้จริงหรือไม่? ฉันรู้ว่าแบบสอบถามสองตัวอย่างกำลังทำสิ่งที่แตกต่างกัน แต่มีเหตุผลที่ฉันจะเขียนพวกเขาด้วยวิธีนี้หรือไม่?

การอ่านเกี่ยวกับพวกเขาและข้อดี / ความแตกต่างนั้นยังไม่ได้รับการอธิบาย


ข้อดีอย่างหนึ่งของฟังก์ชั่นอินไลน์คือคุณสามารถเลือกคอลัมน์ ROWID (TIMESTAMP) ในขณะที่คุณไม่สามารถแทรกข้อมูล TIMESTAMP ลงในตารางส่งคืนในฟังก์ชั่นหลายขั้นตอน!
Artru

3
ขอบคุณสำหรับกระทู้ที่ยอดเยี่ยม ฉันได้เรียนรู้มากมาย อย่างไรก็ตามสิ่งหนึ่งที่ต้องจำไว้คือเมื่อเปลี่ยนฟังก์ชั่นที่เป็น ITV เป็น MSTV ผู้สร้างโปรไฟล์คิดว่าคุณกำลังเปลี่ยน ITV ไม่ว่าคุณจะทำอะไรเพื่อรับไวยากรณ์จากมุมมอง MSTV การคอมไพล์ใหม่จะล้มเหลวเสมอโดยปกติจะอยู่รอบคำสั่งแรกหลังจาก BEGIN วิธีเดียวที่จะทำเช่นนี้คือการลดฟังก์ชั่นเก่าและสร้างใหม่ในฐานะ MSTV
Fandango68

คำตอบ:


141

ในการค้นคว้าความคิดเห็นของ Matt ฉันได้แก้ไขคำแถลงเดิมของฉันแล้ว เขาถูกต้องจะมีความแตกต่างในประสิทธิภาพการทำงานระหว่างฟังก์ชั่นมูลค่าตารางแบบอินไลน์ (ITVF) และฟังก์ชั่นมูลค่าตารางหลายคำสั่ง (MSTVF) แม้ว่าพวกเขาทั้งสองเพียงแค่ดำเนินการคำสั่ง SELECT SQL Server จะปฏิบัติต่อ ITVF ค่อนข้างเหมือนVIEWในการที่จะคำนวณแผนการดำเนินการโดยใช้สถิติล่าสุดในตารางที่มีปัญหา MSTVF เทียบเท่ากับการบรรจุเนื้อหาทั้งหมดของคำสั่ง SELECT ของคุณลงในตัวแปรตารางแล้วเข้าร่วมกับที่ ดังนั้นคอมไพเลอร์ไม่สามารถใช้สถิติตารางใด ๆ บนตารางใน MSTVF ดังนั้นทุกสิ่งเท่าเทียมกัน (ซึ่งไม่ค่อยมี) ITVF จะทำงานได้ดีกว่า MSTVF ในการทดสอบของฉันความแตกต่างของประสิทธิภาพในเวลาที่เสร็จสมบูรณ์นั้นเล็กน้อย แต่จากจุดยืนทางสถิติ

ในกรณีของคุณฟังก์ชั่นทั้งสองนั้นไม่สามารถใช้งานได้เทียบเท่ากัน ฟังก์ชัน MSTV ทำการสอบถามเพิ่มเติมทุกครั้งที่มีการเรียกใช้และที่สำคัญที่สุดคือการกรองรหัสลูกค้า ในแบบสอบถามขนาดใหญ่เครื่องมือเพิ่มประสิทธิภาพจะไม่สามารถใช้ประโยชน์จากการรวมประเภทอื่น ๆ ได้เนื่องจากจะต้องเรียกใช้ฟังก์ชันสำหรับแต่ละรหัสลูกค้าที่ส่งผ่าน อย่างไรก็ตามหากคุณเขียนฟังก์ชั่น MSTV ของคุณใหม่เช่น:

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

ในแบบสอบถามเพิ่มประสิทธิภาพจะสามารถเรียกใช้ฟังก์ชันที่ครั้งหนึ่งและสร้างแผนการดำเนินการที่ดีขึ้น แต่ก็ยังจะไม่ดีกว่าเทียบเท่าไม่แปร ITVS VIEWหรือ

ITVF ควรเป็นที่นิยมมากกว่า MSTVF เมื่อทำได้เนื่องจาก datatypes, nullability และ collation จากคอลัมน์ในตารางในขณะที่คุณประกาศคุณสมบัติเหล่านั้นในฟังก์ชัน multi-statement table ที่มีค่าและที่สำคัญคุณจะได้รับแผนการดำเนินการที่ดีขึ้นจาก ITVF จากประสบการณ์ของฉันฉันไม่พบหลายสถานการณ์ที่ ITVF เป็นตัวเลือกที่ดีกว่า VIEW แต่ระยะทางอาจแตกต่างกัน

ขอบคุณแมตต์

ส่วนที่เพิ่มเข้าไป

เนื่องจากฉันเห็นสิ่งนี้เกิดขึ้นเมื่อเร็ว ๆ นี้นี่คือการวิเคราะห์ที่ยอดเยี่ยมโดย Wayne Sheffield เปรียบเทียบความแตกต่างของประสิทธิภาพระหว่างฟังก์ชั่น Inline Table Valued และ Multi-Statement

โพสต์บล็อกเดิมของเขา

คัดลอกบน SQL Server Central


40
สิ่งนี้ไม่เป็นความจริง - ฟังก์ชั่นหลายข้อความมักเป็นที่นิยมอย่างมากเนื่องจากมันหยุดการเพิ่มประสิทธิภาพการสืบค้นจากการใช้สถิติ หากฉันมี $ 1 ทุกครั้งที่ฉันเห็นการใช้งานหลายงบทำให้เลือกแผนการดำเนินการที่แย่มาก (ส่วนใหญ่เป็นเพราะโดยปกติแล้วจะประมาณจำนวนแถวที่คืนเป็น 1) ฉันจะมีเพียงพอที่จะซื้อรถยนต์ขนาดเล็ก
Matt Whitfield

คำอธิบายที่ดีที่สุดที่ฉันเคยพบคือคำตอบแรกและโพสต์ที่เกี่ยวข้อง: stackoverflow.com/questions/4109152/…อย่าพลาดเอกสารที่เกี่ยวข้องกับ teh คุณสามารถอ่านได้อย่างรวดเร็วและน่าสนใจมาก
JotaBe

1
จะมีการอัปเดตคำตอบนี้สำหรับ SQL Server 2017 หรือไม่: youtube.com/watch?time_continue=2&v=szTmo6rTUjM
Ralph

29

ภายใน SQL Server จะปฏิบัติต่อฟังก์ชั่นที่มีค่าของตารางแบบอินไลน์คล้ายกับว่ามันจะดูและปฏิบัติกับฟังก์ชั่นที่มีคุณค่าของตารางหลายคำสั่งคล้ายกับวิธีที่มันจะเก็บกระบวนการ

เมื่อใช้ฟังก์ชันที่มีค่าเป็นตารางแบบอินไลน์เป็นส่วนหนึ่งของเคียวรีภายนอกตัวประมวลผลเคียวรีจะขยายนิยาม UDF และสร้างแผนการดำเนินการที่เข้าถึงวัตถุต้นแบบโดยใช้ดัชนีบนวัตถุเหล่านี้

สำหรับฟังก์ชั่นที่มีมูลค่าตารางหลายคำสั่งแผนปฏิบัติการจะถูกสร้างขึ้นสำหรับฟังก์ชั่นนั้นเองและเก็บไว้ในแคชแผนปฏิบัติการ (เมื่อฟังก์ชั่นถูกดำเนินการในครั้งแรก) หากใช้หลายฟังก์ชั่นมูลค่าตารางคำสั่งเป็นส่วนหนึ่งของแบบสอบถามที่มีขนาดใหญ่ดังนั้นเครื่องมือเพิ่มประสิทธิภาพจะไม่ทราบว่าฟังก์ชันส่งกลับและทำให้สมมติฐานมาตรฐานบางอย่าง - ในกรณีนี้สมมติว่าฟังก์ชันจะส่งคืนแถวเดียวและผลตอบแทนของ ฟังก์ชั่นจะสามารถเข้าถึงได้โดยใช้การสแกนตารางกับตารางที่มีแถวเดียว

โดยที่ฟังก์ชั่นที่มีคุณค่าของตารางหลายคำสั่งสามารถทำงานได้ไม่ดีคือเมื่อฟังก์ชันเหล่านั้นส่งคืนแถวจำนวนมากและเชื่อมต่อกับคิวรีด้านนอก ปัญหาด้านประสิทธิภาพนั้นโดยหลักแล้วมาจากความจริงที่ว่าเครื่องมือเพิ่มประสิทธิภาพจะสร้างแผนโดยสมมติว่ามีการส่งคืนแถวเดียวซึ่งไม่จำเป็นต้องเป็นแผนที่เหมาะสมที่สุด

ตามกฎทั่วไปแล้วเราพบว่าควรใช้ฟังก์ชั่นที่มีคุณค่าของตารางอินไลน์ในการตั้งค่ากับคำสั่งหลายคำสั่ง (เมื่อใช้ UDF เป็นส่วนหนึ่งของการสืบค้นภายนอก) เนื่องจากปัญหาประสิทธิภาพที่อาจเกิดขึ้นเหล่านี้


2
แม้ว่ามันอาจจะใช้กับฟังก์ชั่นที่มีมูลค่าคล้ายกับขั้นตอนการจัดเก็บหลายคำสั่งตารางที่มีฟังก์ชั่นการจัดเก็บเหมือนกันจะเร็วกว่าฟังก์ชั่นที่มีมูลค่าของตารางสำหรับชุดข้อมูลขนาดใหญ่ ฉันกำลังติดกับ procs ที่จัดเก็บไว้เหนือฟังก์ชันหลายค่าในตาราง
Kekoa

6
หากคุณไม่ต้องการเข้าร่วมผลลัพธ์เหล่านั้นในการสืบค้นอื่น
Guillermo Gutiérrez

ทำไมไม่ใช้ทั้งสองอย่าง? proc ที่เก็บไว้ซึ่งส่งคืนผลลัพธ์ของฟังก์ชันหลายค่าที่เป็นตาราง สุดยอดของทั้งสองโลก
Robino

13

มีความแตกต่างอื่น ฟังก์ชั่นมูลค่าตารางแบบอินไลน์สามารถแทรกเข้าไปปรับปรุงและลบได้เช่นเดียวกับมุมมอง ใช้ข้อ จำกัด ที่คล้ายกัน - ไม่สามารถอัปเดตฟังก์ชั่นโดยใช้การรวมไม่สามารถอัปเดตคอลัมน์จากการคำนวณและอื่น ๆ


3

ฉันคิดว่าตัวอย่างของคุณตอบคำถามได้ดีมาก ฟังก์ชั่นแรกสามารถทำได้ด้วยการเลือกเพียงครั้งเดียวและเป็นเหตุผลที่ดีในการใช้รูปแบบอินไลน์ อาจใช้คำสั่งที่สองเป็นคำสั่งเดียว (โดยใช้แบบสอบถามย่อยเพื่อให้ได้วันที่สูงสุด) แต่ผู้เขียนโค้ดบางคนอาจอ่านได้ง่ายขึ้นหรือเป็นเรื่องง่ายกว่าที่จะทำในหลาย ๆ คำสั่งที่คุณทำ ฟังก์ชั่นบางอย่างที่ธรรมดาไม่สามารถทำได้ในคำสั่งเดียวดังนั้นจึงต้องใช้หลายรุ่น

ฉันขอแนะนำให้ใช้แบบง่ายที่สุด (แบบอินไลน์) ทุกครั้งที่ทำได้และใช้หลายข้อความเมื่อจำเป็น (ชัด) หรือเมื่อความชอบส่วนตัว / ความสามารถในการอ่าน / อ่านทำให้การพิมพ์พิเศษเกิดขึ้นได้


ขอบคุณสำหรับคำตอบ. ดังนั้นโดยทั่วไปแล้วคำสั่งหลาย ๆ คำจะใช้เฉพาะเมื่อฟังก์ชันมีความซับซ้อนมากกว่าที่เป็นไปได้ที่จะทำในฟังก์ชั่นอินไลน์เพื่อประโยชน์ในการอ่าน? มีประโยชน์ใด ๆ ต่อประสิทธิภาพในการทำงานหลายข้อความหรือไม่?
AndrewC

ฉันไม่รู้ แต่ฉันก็ไม่คิดอย่างนั้น มันน่าจะดีกว่าถ้าให้ sql server หาการปรับแต่งที่คุณอาจลองทำด้วยตนเอง (โดยใช้ตัวแปร, temp table, หรืออะไรก็ตาม) แม้ว่าคุณจะสามารถทำการทดสอบประสิทธิภาพเพื่อพิสูจน์ / พิสูจน์หักล้างในบางกรณีได้
เรย์

ขอบคุณอีกครั้ง ฉันอาจดูเพิ่มเติมเกี่ยวกับเรื่องนี้เมื่อฉันมีเวลามากขึ้น! :)
AndrewC


0

ฉันยังไม่ได้ทดสอบสิ่งนี้ แต่ฟังก์ชั่นหลายคำสั่งแคชชุดผลลัพธ์ อาจมีบางกรณีที่เครื่องมือเพิ่มประสิทธิภาพเกินกว่าจะอินไลน์ฟังก์ชั่น ตัวอย่างเช่นสมมติว่าคุณมีฟังก์ชันที่ส่งคืนผลลัพธ์จากฐานข้อมูลที่แตกต่างกันขึ้นอยู่กับสิ่งที่คุณส่งเป็น "หมายเลข บริษัท " โดยปกติคุณสามารถสร้างมุมมองที่มีการรวมกันทั้งหมดแล้วกรองตามหมายเลข บริษัท แต่ฉันพบว่าบางครั้งเซิร์ฟเวอร์ sql ดึงการรวมทั้งหมดและไม่ฉลาดพอที่จะเรียกการเลือกหนึ่งครั้ง ฟังก์ชั่นตารางสามารถมีตรรกะในการเลือกแหล่งที่มา


0

อีกกรณีหนึ่งที่ใช้ฟังก์ชั่นมัลติไลน์คือการหลีกเลี่ยงเซิร์ฟเวอร์ sql จากการกดประโยคไหน

ตัวอย่างเช่นฉันมีตารางที่มีชื่อตารางและบางชื่อตารางมีรูปแบบเช่น C05_2019 และ C12_2018 และตารางทั้งหมดที่จัดรูปแบบด้วยวิธีนี้จะมีสคีมาเหมือนกัน ฉันต้องการรวมข้อมูลทั้งหมดนั้นไว้ในตารางเดียวแล้วแยก 05 และ 12 ออกเป็นคอลัมน์ CompNo และ 2018,2019 ลงในคอลัมน์ปี อย่างไรก็ตามมีตารางอื่น ๆ เช่น ACA_StupidTable ซึ่งฉันไม่สามารถแยก CompNo และ CompYr และจะได้รับข้อผิดพลาดในการแปลงหากฉันพยายาม ดังนั้นแบบสอบถามของฉันอยู่ในสองส่วนแบบสอบถามภายในที่ส่งกลับเฉพาะตารางที่จัดรูปแบบเช่น 'C_______' จากนั้นแบบสอบถามด้านนอกได้ทำการแปลงสตริงย่อยและการแปลง int ie Cast (Substring (2, 2) เป็น int) เป็น CompNo ทุกอย่างดูดียกเว้นเซิร์ฟเวอร์ sql ตัดสินใจที่จะนำฟังก์ชั่น Cast ของฉันก่อนที่ผลลัพธ์จะถูกกรองและดังนั้นฉันจึงได้รับข้อผิดพลาดในการแปลงข้อมูล ฟังก์ชันตารางคำสั่งหลายคำอาจป้องกันไม่ให้เกิดขึ้น


0

อาจเป็นไปในทางที่ย่อมาก ITVF (inline TVF): ยิ่งถ้าคุณเป็นคน DB เป็นชนิดของมุมมองแบบกำหนดพารามิเตอร์ใช้เวลาเลือกเดียว

MTVF (Multi-statement TVF): ผู้พัฒนาสร้างและโหลดตัวแปรตาราง


-2

หากคุณกำลังจะทำแบบสอบถามคุณสามารถเข้าร่วมในฟังก์ชั่นมูลค่าตารางแบบอินไลน์ของคุณเช่น:

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

มันจะมีค่าใช้จ่ายเล็กน้อยและทำงานได้ดี

หากคุณพยายามใช้ตาราง Multi Statement ที่มีค่าในการค้นหาที่คล้ายกันคุณจะพบปัญหาด้านประสิทธิภาพ:

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

เนื่องจากคุณจะดำเนินการฟังก์ชัน 1 ครั้งสำหรับแต่ละแถวที่ส่งคืนเนื่องจากชุดผลลัพธ์มีขนาดใหญ่มันจะทำงานช้าลงและช้าลง


อาคุณจะบอกว่าอินไลน์นั้นดีกว่าในแง่ของประสิทธิภาพ?
AndrewC

1
ไม่พวกมันทั้งคู่ส่งคืนตารางซึ่งทำให้ SQL ตัวที่สองของคุณไม่ถูกต้องในขณะที่คุณพยายามวางตารางในคอลัมน์
cjk

1
@ck ฉันได้อัปเดตข้อความค้นหาที่แสดงความคิดเห็นแล้ว พารามิเตอร์ของฟังก์ชันที่ใช้ในฟังก์ชันที่สองให้ยืมเพื่อใช้เป็นแบบสอบถามย่อยซึ่งจะส่งผลให้ประสิทธิภาพแย่ลง
กม.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.