3

シーケンスの最初の要素を返すFIRST集計関数が必要です。これをHAVING節とともに使用します。CLRユーザ定義集合関数

これは私の以前の質問の続きとして見ることができる質問です:FIRST aggregate function which I can use with HAVING clause

それが唯一の集約関数は、私は1つ作成しようとしたので、私の問題を解決できることが判明:

[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'を問い合わせる*クエリのどこか*が正しく定義されていません。 –

答えて

3

あなたの問題は、IsInvariantToOrderがまだ実装されていないと思います。

将来のために予約されています。現在このプロパティはクエリプロセッサ で使用されていません。注文は現在保証されていません。

したがって、正しい順序で実行されても、同じ順序で実行されるという保証はありません。

私はそれほどC#についてはわかりませんが、2番目のフィールドは注文するフィールドが2つあることが可能です。最初の値を返す前に、ペアをリストに保存し、2番目のフィールドで並べ替えます。

更新:

私は見ていないここでエラーが悪い慣行の任意の数があるかもしれないので、私は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

ok、それは吸う。ありがとう! – tugberk

関連する問題