2017-03-07 24 views
4

以這裏定義的(簡化的)存儲過程:是否有任何現有的,優雅的可選TOP條款模式?

create procedure get_some_stuffs 
    @max_records int = null 
as 
begin 
    set NOCOUNT on 

    select top (@max_records) * 
    from my_table 
    order by mothers_maiden_name 
end 

我想選擇僅當@max_records提供的記錄的數量來限制。

問題:

  1. 真正查詢是骯髒和大;我想,以避免它複製與此類似:

    if(@max_records is null) 
    begin 
        select * 
        from {massive query} 
    end 
    else 
    begin 
        select top (@max_records) 
        from {massive query} 
    end 
    
  2. 的任意定點值感覺不對:

    select top (ISNULL(@max_records, 2147483647)) * 
    from {massive query} 
    

    例如,如果@max_records2147483647行少null{massive query}回報,這是否相同:

    select * 
    from {massive query} 
    

    還是有某種懲罰從僅有50行的表中選擇top (2147483647) *

是否有任何其他現有模式允許可選計數限制的結果集,而無需重複查詢或使用標記值?

+1

什麼是從設置的參數到2147483647默認阻止你? –

+0

你基本上設置了一個視圖閾值。爲什麼不設置系統範圍的閾值,然後在用戶級別覆蓋? 我的意思是..有一個sysmax = 1000,然後檢索usermax ...比較它不應該超過sysmax ...理想情況下,你不應該在視圖中顯示的一切..特別是所有列 – maSTAShuFu

+1

我認爲你的問題的一部分是你想知道是否有一個開銷來設置一個最大值遠高於實際記錄。一切都告訴我不應該有。此外,如果您的表格正確索引,則開銷(如果有的話)可以忽略不計。 – DaniDev

回答

0

你可以不喜歡這樣(用你的例子):

create procedure get_some_stuffs 
    @max_records int = null 
as 
begin 
    set NOCOUNT on 

    select top (ISNULL(@max_records,1000)) * 
    from my_table 
    order by mothers_maiden_name 
end 

我知道你不喜歡這個(根據你的2點),但是這幾乎是它是如何做(在我的經驗)。

+0

這與上述第2點有何不同? –

+0

@Alex,它沒有。我只是告訴你不要擔心這樣做。 – BoltBait

+0

好的,謝謝。我真的希望有更好的東西。 –

2

我在想這個,雖然我喜歡Problem 1聲明中IF聲明的明確性,但我理解重複的問題。因此,你可以把主查詢在一個單一的CTE,並使用一些弄虛作假從中查詢(在粗體部分是該解決方案的亮點):

CREATE PROC get_some_stuffs 
(
    @max_records int = NULL 
) 
AS 
BEGIN 
    SET NOCOUNT ON; 

    WITH staged AS (
     -- Only write the main query one time 
     SELECT * FROM {massive query} 
    ) 
    -- This part below the main query never changes: 
    SELECT * 
    FROM (
     -- A little switcheroo based on the value of @max_records 
     SELECT * FROM staged WHERE @max_records IS NULL 
     UNION ALL 
     SELECT TOP(ISNULL(@max_records, 0)) * FROM staged WHERE @max_records IS NOT NULL 
    ) final 
    -- Can't use ORDER BY in combination with a UNION, so move it out here 
    ORDER BY mothers_maiden_name 
END 

我看了看實際的查詢計劃對於每個優化器都足夠聰明,可以完全避免不需要運行的部分UNION ALL

ISNULL(@max_records, 0)在那裏,因爲TOP NULL是無效的,它不會編譯。

+0

我喜歡這種創造性的方法,雖然有一些溫和的卷積。我想知道它是否會影響子查詢的索引選擇(我已經看到過類似'union all's)?將做一些測試並回來 - 但謝謝! –

0

這樣的事情(你必須真的看執行計劃,我沒有時間設置任何東西)?

create procedure get_some_stuffs 
    @max_records int = null 
as 
begin 
    set NOCOUNT on 

    select *, ROW_NUMBER(OVER order by mothers_maiden_name) AS row_num 
    from {massive query} 
    WHERE @max_records IS NULL OR row_num < @max_records 
end 

你可以用{大量查詢}做的另一件事是做一個視圖或內嵌表值函數(它,它的參數化),這通常是任何大的改動,並重復使用一個很好的做法。

0

有幾種方法,但你可能會注意到這些都看起來很醜或不必要的複雜。此外,你真的需要那個ORDER BY嗎?

你可以使用TOP (100) PERCENT和查看,但百分比只有當你真的不需要那麼貴ORDER BY因爲SQL Server將忽略你的工作,如果ORDER BY你試試吧。

我建議採取的存儲過程的優勢,但首先讓解釋特效類型的區別:

硬編碼參數嗅探

--Note the lack of a real parametrized column. See notes below. 
IF OBJECT_ID('[dbo].[USP_TopQuery]', 'U') IS NULL 
    EXECUTE('CREATE PROC dbo.USP_TopQuery AS ') 
GO 
ALTER PROC [dbo].[USP_TopQuery] @MaxRows NVARCHAR(50) 
AS 
BEGIN 
DECLARE @SQL NVARCHAR(4000) = N'SELECT * FROM dbo.ThisFile' 
     , @Option NVARCHAR(50) = 'TOP (' + @MaxRows + ') *' 
IF ISNUMERIC(@MaxRows) = 0 
    EXEC sp_executesql @SQL  
ELSE 
    BEGIN 
     SET @SQL = REPLACE(@SQL, '*', @Option) 
     EXEC sp_executesql @SQL 
    END 
END 

本地變量參數嗅探

IF OBJECT_ID('[dbo].[USP_TopQuery2]', 'U') IS NULL 
    EXECUTE('CREATE PROC dbo.USP_TopQuery2 AS ') 
GO 
ALTER PROC [dbo].[USP_TopQuery2] @MaxRows INT NULL 
AS 
BEGIN 
DECLARE @Rows INT; 
    SET @Rows = @MaxRows; 

IF @MaxRows IS NULL 
    SELECT * 
    FROM dbo.THisFile 
ELSE 
    SELECT TOP (@Rows) * 
    FROM dbo.THisFile 
END 

無參數嗅探,老方法

IF OBJECT_ID('[dbo].[USP_TopQuery3]', 'U') IS NULL 
    EXECUTE('CREATE PROC dbo.USP_TopQuery3 AS ') 
GO 
ALTER PROC [dbo].[USP_TopQuery3] @MaxRows INT NULL 
AS 
BEGIN 

IF @MaxRows IS NULL 
    SELECT * 
    FROM dbo.THisFile 
ELSE 
    SELECT TOP (@MaxRows) * 
    FROM dbo.THisFile 
END 

請注意有關參數嗅探:

的SQL Server在時間編譯初始化中存儲的特效變量,當它不解析。

這意味着,SQL Server將無法猜測查詢,並會 選擇用於查詢的最後一個有效的執行計劃,無論 無論是就算不錯了。

有兩種方法,硬編碼允許優化器猜測的局部變量。

  1. 的參數嗅探

    • 使用sp_executesql的硬編碼,不僅重用了查詢,但防止SQL注入。
    • 然而,在這種類型的查詢,就不會總是表現基本更好,因爲Top運算符是不是列或表(這樣的語句有效地在這個版本我用沒有變量)當時
    • 統計創建編譯計劃將決定如果對謂詞沒有使用變量(ON,,HAVING
    • 可以使用選項或提示來RECOMPILE來解決此問題,方法效果如何。
  2. 變量參數嗅探

    • 可變放慢參數嗅探,在另一方面,有足夠的靈活性witht統計工作在這裏,在我自己的測試,似乎變量參數具有的優勢使用統計信息查詢(特別是在我更新了統計數據後)。

Top Queries

  • 最終,性能的問題是關於哪一種方法將使用最少的步量通過小葉遍歷。 統計,在您的表中,和規則爲SQL Server將決定使用掃描VS Seek影響性能。

  • 運行不同值會顯示性能顯着變化,但通常優於USP_TopQuery3。所以不要假設一種方法一定比另一種更好。

    • 另外請注意,您可以使用一個表值函數做同樣的,但戴夫皮納爾會說:

如果你要回答「爲了避免重複代碼,您使用 函數' - 請認真思考!存儲過程可以做同樣的...

如果你要回答 '功能可以在SELECT中使用,而存儲過程不能使用' ' - 再想一想!

SQL SERVER – Question to You – When to use Function and When to use Stored Procedure

0

你可以使用SET ROWCOUNT

create procedure get_some_stuffs 
    @max_records int = null 
as 
begin 
    set NOCOUNT on 

    IF @max_records IS NOT NULL 
    BEGIN 
    SET ROWCOUNT @max_records 
    END 

    select top (@max_records) * 
    from my_table 
    order by mothers_maiden_name 

    SET ROWCOUNT 0 

end 
相關問題