3

シーケンスの最初の要素を返す FIRST 集計関数が必要で、それを HAVING 句で使用します。

これは、以前の質問の続きと見なすことができる質問です: FIRST 集計関数を HAVING 句で使用できます

集計関数だけが私の問題を解決できることが判明したので、作成しようとしました:

[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;

適切な結果が返されることもあれば、返されないこともあり、正しく実装したかどうかはわかりません。私の要件を考慮してこれが正しいとしたら、何か考えはありますか?

4

1 に答える 1

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)
于 2012-04-12T11:44:31.157 に答える