視圖與獲取表頂部記錄的函式?
我正在與一位同事討論解決問題的方法。
我們有一個跟踪填充過程的表,該表在列
Lot
和TestGrade
.最近,我們添加了邏輯,以便在
TestGrade
創建任何高於 1 的 a 時,創建所有較低的級別。某些邏輯已完成,因此較低級別的行不一定與頂層相同。例如,我插入一些東西,
Lot='ABCD'
然後TestGrade=5
在我的Fills
表中我看到id Lot TestGrade OtherColumns ====================================== 10 'ABCD' 1 blah3 9 'ABCD' 2 bar 8 'ABCD' 3 foo 7 'ABCD' 4 blah1 6 'ABCD' 5 blah
現在的問題是在很多情況下,我只想要 Fills 表的頂級記錄,所以在範例中,我只想要帶有
id=6
.關於如何做到這一點,我們有兩個想法。我想做一個看起來像的視圖
CREATE VIEW [dbo].[vwFills] AS SELECT t.* FROM [dbo].[Fills] t JOIN (SELECT [Lot], MAX(TestGrade) as TestGrade FROM dbo.Fills GROUP BY [Lot]) t2 ON t.[Lot] = t2.[Lot] AND t.[TestGrade] = t2.[TestGrade]
我的同事想做類似的事情,但在一個看起來像這樣的函式內部
ALTER FUNCTION [dbo].[fnFills] ( @Fills tyFills READONLY ) RETURNS @returnTable TABLE( //Copy of the table definition of Fills, without id INT NOT NULL, Lot VARCHAR(10) NOT NULL, TestGrade INT NOT NULL, //rest of the columns.. ) AS Begin insert into @returnTable select t2.* from ( select [Lot], max([TestGrade]) as TestGrade from @Fills) t1 left join Fills t2 on t1.[Lot] = t2.[Lot] and t1.[TestGrade] = t2.[TestGrade] Return End
然後呼叫這個函式,它看起來像
DECALARE @FillRecords tyFills; INSERT INTO @FillRecords SELECT * FROM db.Fills; SELECT * FROM dbo.fnFills(@FillRecords);
我們
dbo.Fills
確實在Lot
和上有索引TestGrade
。然而,我的直覺是,在我們插入變數然後將其提供給函式的函式方法中,我們會失去這些索引,因此通過這種連接,我們正在查看 O(n^2) 的性能。這是一個公平的評價嗎?我擔心性能,因為伺服器在硬體方面並不強大,因此任何有助於查詢執行的東西都是首選。一般來說,在這種情況下,是否有最佳實踐方法 - 視圖或函式是“明顯”還是更好的選擇?
sql server 中的函式通常是個壞主意
SQL Server 中的函式,特別是標量函式和多語句表值函式,存在很多問題。我將跳過標量 UDF,因為這不是您的問題。
對於您的同事建議的函式類型,即多語句表值函式,問題主要
@table
在於支持它的變數。你會遇到的問題是:即使在這里索引
@table
變數也不會對您非常有用。您可以通過使用內聯表值函式來避免大多數問題。它們的性能僅與您在其中輸入的查詢以及可用的支持索引一樣差。
CREATE OR ALTER FUNCTION dbo.fnFills_Inline ( @Fills tyFills READONLY ) RETURNS table AS RETURN SELECT t2.* FROM ( SELECT [Lot], MAX([TestGrade]) AS TestGrade FROM @Fills ) t1 LEFT JOIN Fills t2 ON t1.[Lot] = t2.[Lot] AND t1.TestGrade = t2.[TestGrade];
在這種情況下,視圖和函式之間應該沒有實質性區別,但是在涉及視窗函式的視圖中,可能需要一個內聯表值函式來將謂詞推過序列項目運算符。
雖然我同意其他答案,即通常視圖比函式遇到性能問題的可能性更小,但它也特別取決於一個人編寫的查詢。我之所以提到這一點,是因為選項 C是使用一個利用
ROW_NUMBER()
視窗函式有效地獲取每個的頂部記錄的視圖,Lot
如下所示:WITH LotGradesSorted AS ( SELECT id, Lot, TestGrade, OtherColumns, ROW_NUMBER() OVER (PARTITION BY Lot ORDER BY TestGrade DESC) AS GroupSortId FROM dbo.Fills ) SELECT id, Lot, TestGrade, OtherColumns FROM LotGradesSorted WHERE GroupSortId = 1
這會為按欄位降序排序的欄位
PARTITION
上的分組 ( ) 中的每一行生成一個唯一 ID ,這樣每組s 中最大的行始終是第一行。最終查詢僅返回那些行(使用過濾器)。Lot``TestGrade``TestGrade``Lot``SELECT``WHERE GroupSortId = 1
請注意,這僅適用於
ORDER BY
視窗函式子句中的欄位在子句中欄位的分組中是唯一的PARTITION
(即,如果您在相同的欄位中從未有相同的TestGrade
重複項Lot
)。否則,如果被排序的欄位不是唯一的,那麼排序是不確定的(因為兩行將具有相同的確切值來排序,因此沒有一致的選擇方法),這可能導致半隨機結果。但是只要您的TestGrade
欄位在每個 s 分組中都是唯一的,Lot
那麼您就永遠不會遇到這個問題。您的視圖中的此查詢比您建議的查詢更有效的原因是因為它應該導致更少的通過您的表,並且 Microsoft 已將視窗函式設計為在正確情況下使用時的高性能工具。