3

我需要一個返回序列的第一個元素的FIRST聚合函數,我將使用HAVING子句。CLR用戶定義的聚合函數,以獲得序列的第一個元素

這是一個問題,可以被視爲我的上一個問題的延續:FIRST aggregate function which I can use with HAVING clause

事實證明,只有一個聚合函數可以解決我的問題,所以我試圖創建一個:

[Serializable] 
[SqlUserDefinedAggregate(
    Format.UserDefined, //use clr serialization to serialize the intermediate result 
    IsInvariantToNulls = true, //optimizer property 
    IsInvariantToDuplicates = false, //optimizer property 
    IsInvariantToOrder = false, //optimizer property 
    MaxByteSize = 8000) //maximum size in bytes of persisted value 
] 
public struct GetFirst : IBinarySerialize { 

    private string allValues; 

    public void Init() { 
     allValues = string.Empty; 
    } 

    private void incrementAndAdd(SqlInt32 value) { 

     allValues += (value.Value.ToString() + ","); 
    } 

    public void Accumulate(SqlInt32 Value) { 

     incrementAndAdd(Value); 
    } 

    public void Merge(GetFirst Group) { 

    } 

    public SqlInt32 Terminate() { 
     // Put your code here 
     return new SqlInt32(int.Parse(allValues.Split(',')[0])); 
    } 

    // This is a place-holder member field 
    private SqlInt32 var1; 


    public void Read(System.IO.BinaryReader r) { 

     allValues = r.ReadString(); 
    } 

    public void Write(System.IO.BinaryWriter w) { 

     w.Write(this.allValues); 
    } 
} 

這就是我如何使用它:

DECLARE @fooTable AS TABLE(
    ID INT, 
    CategoryName NVARCHAR(100), 
    Name NVARCHAR(100), 
    MinAllow INT, 
    Price DECIMAL(18,2) 
); 

INSERT INTO @fooTable VALUES(1, 'Cat1', 'Product1', 3, 112.2); 
INSERT INTO @fooTable VALUES(2, 'Cat2', 'Product2', 4, 12.34); 
INSERT INTO @fooTable VALUES(3, 'Cat1', 'Product3', 5, 233.32); 
INSERT INTO @fooTable VALUES(4, 'Cat3', 'Product4', 4, 12.43); 
INSERT INTO @fooTable VALUES(5, 'Cat3', 'Product5', 1, 13.00); 
INSERT INTO @fooTable VALUES(7, 'Cat4', 'Product7', 1, 15.00); 
INSERT INTO @fooTable VALUES(6, 'Cat4', 'Product6', 3, 13.00); 

DECLARE @minAllowParam AS INT = 3; 

SELECT ft.CategoryName, SUM(ft.Price), dbo.GetFirst(ft.MinAllow) FROM @fooTable ft 
GROUP BY ft.CategoryName 
HAVING dbo.GetFirst(ft.MinAllow) >= @minAllowParam; 

它有時會返回正確的結果,有時不是,我不確定我是否實施了正確的。任何想法,如果我根據我的要求得到這個權利?

+2

如果如果(這是不是像@Mikael埃裏克森指出)訂單保證是存在的,您不是*在您的查詢中指定*排序 - 表格不要*有一個訂單,所以要求查詢中沒有出現'ORDER BY'的'FIRST'*某處*是不明確的。 – 2012-04-12 12:06:21

回答

3

我認爲你的問題是IsInvariantToOrder尚未實現。

留作將來使用。 查詢處理器當前未使用此屬性:目前不保證訂單。

因此,不能保證您的函數將以正確的順序或甚至在運行之間的相同順序被調用。

我不知道那麼多的C#,但我認爲有可能有兩個參數給你的函數,其中第二個是你想要排序的字段。在返回第一個值之前,將對保存在列表中並在第二個字段上排序。

更新:

我不做C#這樣可能有任意數量的錯誤這裏惡劣做法我沒有看到的。但它看起來在我所做的測試中工作得很好。

using System; 
using System.Data; 
using Microsoft.SqlServer.Server; 
using System.Data.SqlTypes; 
using System.IO; 
using System.Text; 

[Serializable] 
[SqlUserDefinedAggregate(
    Format.UserDefined, //use clr serialization to serialize the intermediate result 
    IsInvariantToNulls = true, //optimizer property 
    IsInvariantToDuplicates = false, //optimizer property 
    IsInvariantToOrder = false, //optimizer property 
    MaxByteSize = 8000) //maximum size in bytes of persisted value 
] 

public class GetFirst : IBinarySerialize 
{ 
    private int Value; 
    private int OrderBy; 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public void Init() 
    { 
     Value = 0; 
     OrderBy = int.MaxValue; 
    } 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public void Accumulate(SqlInt32 SValue, SqlInt32 SOrderBy) 
    { 
     if (SValue.IsNull) 
     { 
      return; 
     } 

     if (SOrderBy.IsNull) 
     { 
      return; 
     } 

     if (SOrderBy.Value < OrderBy) 
     { 
      Value = SValue.Value; 
      OrderBy = SOrderBy.Value; 
     } 
    } 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public void Merge(GetFirst other) 
    { 
     if (other.OrderBy < OrderBy) 
     { 
      Value = other.Value; 
      OrderBy = other.OrderBy; 
     } 
    } 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public SqlInt32 Terminate() 
    { 
     return new SqlInt32(Value); 
    } 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public void Read(BinaryReader r) 
    { 
     Value = r.ReadInt32(); 
     OrderBy = r.ReadInt32(); 
    } 

[SqlFunctionAttribute(IsDeterministic = true)] 
    public void Write(BinaryWriter w) 
    { 
     w.Write(Value); 
     w.Write(OrderBy); 
    } 
} 

安裝:

CREATE ASSEMBLY GetFirstAsm FROM 'First.dll' 
GO 
CREATE AGGREGATE GetFirst (@Value int, @OrderBy int) RETURNS int 
EXTERNAL NAME GetFirstAsm.GetFirst 

使用這樣的:

declare @T table 
(
    ID1 int, 
    ID2 int, 
    Val int, 
    Grp int 
) 

insert into @T values 
(1, 5, '1', 1), 
(2, 4, '2', 1), 
(3, 3, '3', 1), 
(4, 2, '4', 2), 
(5, 1, '5', 2) 

select Grp, dbo.GetFirst(Val, ID1) as Val 
from @T 
group by Grp 

select Grp, dbo.GetFirst(Val, ID2) as Val 
from @T 
group by Grp 

結果:

(5 row(s) affected) 
Grp   Val 
----------- ----------- 
1   1 
2   4 

(2 row(s) affected) 

Grp   Val 
----------- ----------- 
1   3 
2   5 

(2 row(s) affected) 
+0

好吧,那很糟糕。謝謝! – tugberk 2012-04-12 12:32:47

相關問題