2017-02-09 74 views
0

說你有下面的SQL表:建立更多的返回平分高效的MS SQL功能範圍

-- create temp table 
CREATE TABLE [tempNums] 
(
    id INT NOT NULL, 
    somedate datetime NULL 
) 
GO 

隨着一些數據(參見下面的tempSplitStringToInts定義):

-- with date 
INSERT INTO [tempNums] 
    SELECT id, GETUTCDATE() 
    FROM [tempSplitStringToInts] ('1,2,3,5,10,100,101,102,103,233,1001,5002,5003,5005,5007,5010',',') 
GO 

-- without date 
INSERT INTO [tempNums] 
    SELECT id, NULL 
    FROM [tempSplitStringToInts] ('6,7,8,150,151,152,153,433,2001,2002,2003,2005,3007,10010',',') 
GO 

怎麼辦你建立更快/更快的功能,將採取一定範圍的數量和一個 標誌位作爲輸入並返回一個範圍值表?

事情是這樣的工作,例如,但是慢了非常大的表:

-- create range function 
CREATE FUNCTION [tempFnGetIdRanges] 
(
    @apps INT, 
    @has_date BIT 
) 
RETURNS @ret TABLE 
(
    RangeNum INT, 
    MinNum INT, 
    MaxNum INT 
) 
AS 
BEGIN 

    DECLARE @i INT = 0; 
    DECLARE @count INT; 
    DECLARE @min INT; 
    DECLARE @max INT = 0; 

    IF @has_date = 1 
    BEGIN 
     SELECT @count = COUNT(id) 
      FROM [tempNums] 
      WHERE somedate IS NOT NULL 
    END 
    ELSE 
    BEGIN 
     SELECT @count = COUNT(id) 
      FROM [tempNums] 
      WHERE somedate IS NULL 

    END 

    DECLARE @top INT = @count/@apps; 

    WHILE @i<@apps 
    BEGIN 

     IF @[email protected] 
     BEGIN 
      -- on last get reminder 
      SET @top = @top + @apps 
     END 

     IF @has_date = 1 
     BEGIN  
      SELECT @min = MIN(id), @max = MAX(id) 
      FROM 
      (
       SELECT TOP (@top) id 
       FROM [tempNums] 
       WHERE somedate IS NOT NULL 
        AND id > @max 
       ORDER BY id 
      ) XX 
     END 
     ELSE 
     BEGIN 
      SELECT @min = MIN(id), @max = MAX(id) 
      FROM 
      (
       SELECT TOP (@top) id 
       FROM [tempNums] 
       WHERE somedate IS NULL 
        AND id > @max 
       ORDER BY id 
      ) XX 
     END 


     INSERT INTO @ret VALUES(@i, @min, @max) 

     SET @i = @i + 1; 
     CONTINUE 
    END 

    RETURN 
END 
GO 

所以,當您運行以下:

SELECT * FROM [tempFnGetIdRanges](4, 0) 
SELECT * FROM [tempFnGetIdRanges](4, 1) 

結果的第一條語句:

RangeNum MinNum MaxNum 
0   6  8 
1   150  152 
2   153  2001 
3   2002 10010 

第二個陳述的結果:

RangeNum MinNum MaxNum 
0   1  5 
1   10  102 
2   103  5002 
3   5003 5010 

分割功能(供參考,但不是這個問題的要點):

-- create split string function 
CREATE FUNCTION [tempSplitStringToInts] (@SourceString VARCHAR(MAX) , @delimeter VARCHAR(10)) 
RETURNS @IntList TABLE 
    (
    id INT 
    ) 
AS 
BEGIN 
IF RIGHT(@SourceString, LEN(@delimeter))<> @delimeter 
    BEGIN 
     SELECT @SourceString = @SourceString + @delimeter 
    END 

DECLARE @LocalStr VARCHAR(MAX) 
DECLARE @start INT 
DECLARE @end INT 
SELECT @start = 1 
SELECT @end = CHARINDEX (@delimeter , @SourceString , @start) 

WHILE @end > 0 
    BEGIN 
     SELECT @LocalStr = SUBSTRING (@SourceString , @start , @end - @start) 
     IF LTRIM(RTRIM(@LocalStr)) <> '' 
      BEGIN 
       INSERT @IntList (id) VALUES (CAST(@LocalStr AS INT)) 
      END 
     SELECT @start = @end + LEN(@delimeter) 
     SELECT @end = CHARINDEX (@delimeter , @SourceString , @start) 
    END 
    RETURN 
END 
GO 

正如我所說的這個工程,但它是非常大的錶慢。有沒有 更好的方法來編寫tempFnGetIdRanges函數?某些原生的SQL可以使用 SQL?如果相關,我正在使用MS SQL 2012

+1

這裏有一些確定的性能挑戰。首先,您正在創建表值函數,但它們是多語句表值函數,幾乎總是比標量函數更慢。對於表值函數的性能好處,必須是一個單獨的select語句,僅此而已。我會開始搗毀你的分離器。這是最糟糕的最糟糕的。它在表值函數中有一個循環。這是一些更好的選擇。 http://sqlperformance.com/2012/07/t-sql-queries/split-strings –

+0

非常感謝您的評論。我會看看更好的分離器功能。 – CrnaStena

+0

主要功能是幹什麼的?當HasDate = 1時,它只是將它們分成組,但是當它爲空時,輸出對我來說沒有任何意義。那裏的邏輯是什麼? –

回答

1

不太確定你的GetRanges函數試圖做什麼,但你肯定不需要循環。當你在HasDate通過爲1

create function GetRanges 
(
    @NumGroups int 
) returns table as return 

    with MyGroups as 
    (
     select NTILE(@NumGroups) over(order by t.id) as GroupNum 
      , t.id 
     from tempnums t 
    ) 

    select GroupNum 
     , MIN(id) as MinNum 
     , MAX(id) as MaxNum 
    from MyGroups 
    group by GroupNum 

- 編輯 -

現在,我看到你發佈的兩組樣本數據的我理解這個問題該函數返回相同的值作爲你的。

以下是您可以如何調整以適應NULL或NOT NULL的某個日期。

alter function GetRanges 
(
    @NumGroups int 
    , @HasDate bit 
) returns table as return 

    with MyGroups as 
    (
     select NTILE(@NumGroups) over(order by t.id) as GroupNum 
      , t.id 
     from tempnums t 
     where 
     (
      @HasDate = 1 
      AND 
      t.somedate is not null 
     ) 
     OR 
     (
      @HasDate = 0 
      AND 
      t.somedate is null 
     ) 
    ) 

    select GroupNum 
     , MIN(id) as MinNum 
     , MAX(id) as MaxNum 
    from MyGroups 
    group by GroupNum 

我看到的問題是,你只有14有空,因此不知道爲什麼你想要的輸出是你的方式行。由於NTILE將不均勻的行放入組中,使用NTILE將在您的樣本數據上產生稍微不同的結果。

+0

GetRanges函數試圖在每個範圍內以及每個範圍MIN,MAX數字中獲取相同數量的元素(或儘可能接近相同數字)。所以當date爲NULL時,對於下面的id 6,7,8,150,151,152,153,433,2001,2002,2003,2005,3007,10010',我需要每個範圍的MIN和MAX。當請求4個範圍時,範圍1將是6,7,8,其中6是最小值,8是最大值,範圍2將是150,151,152,其中150是最小值,152是最大值,範圍3是153,433,2001,其中433是分,2001年是最大和最後一次範圍4是2002,2003,2005,3007,10010,其中2002年是最小值,10010是最大值。 – CrnaStena

+0

爲什麼是6,7和8? 151 - 153?這些其他數字來自哪裏?從我所能看到的這些只是隨機數字,根本就沒有任何邏輯。必須有一些你不同意的規則。 –

+0

假設您在數據庫中有一個表格,並且ID列是標識,但是有些行會被刪除。我的例子是任意的,但我試圖創建隨機值來說明這一點。我無法舉出我的數據庫中的表名和值的例子。 – CrnaStena