我需要一个FIRST聚合函数,它返回一个序列的第一个元素,我将使用它与HAVING子句。
这是一个可以被视为我上一个问题的延续的问题: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;
它有时会返回正确的结果,有时不会,我不确定我是否正确实施。如果我根据自己的要求做到这一点,有什么想法吗?
答案 0 :(得分: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)