Una advertencia: las UDAFs no tienen la misma paridad entre objeto y SQL que otros tipos de funciones, por lo que realmente necesitáis ejecutar SQL para definir la función de agregación (envuelta útilmente en un método de clase en el ejemplo de abajo). Compilar solo la clase no es suficiente.
/// Class implementing a Median aggregate function for IRIS SQLClass DC.Demo.Median
{
/// Returns a new global ref in IRISTEMP to use to store intermediate resultsClassMethod Initialize() As%String [ PublicList = ref, SqlProc ]
{
New ref
Set ref = $Name(^IRIS.Temp.UDAF.Median($Increment(^IRIS.Temp.UDAF.Median)))
Set @ref = 0Quit ref
}
/// Updates temp global for a single recordClassMethod Iterate(ref As%String, value As%Numeric) As%String [ SqlProc ]
{
If (value '= "") {
Do$Increment(@ref)
Do$Increment(@ref@(+value))
}
Quit ref
}
/// Finds the actual median (possibly an average of the two middle values)ClassMethod Finalize(ref As%String) As%Numeric [ SqlProc ]
{
Set median = ""Set total = @ref
Set position1 = (total+1)\2Set position2 = (total+2)\2Set val1 = ""Set val2 = ""Set reached = 0Set key = ""For {
Set key = $Order(@ref@(key),1,refCount)
Quit:key=""set reached = reached + refCount
if (reached >= position1) && (val1 = "") {
Set val1 = key
}
if (reached >= position2) && (val2 = "") {
Set val2 = key
}
If (val1 '= "") && (val2 '= "") {
Set median = (val1+val2)/2Quit
}
}
Kill @ref
Quit median
}
/// To actually define the UDAF from an SQL perspective, call this classmethod.ClassMethod Define()
{
// Drop the function in case something has changed
&sql(DROPAGGREGATE DC_Demo.Median)
&sql(CREATEAGGREGATE DC_Demo.Median(arg NUMERIC) RETURNS NUMERIC
INITIALIZE WITH DC_Demo.Median_Initialize
ITERATE WITH DC_Demo.Median_Iterate
FINALIZE WITH DC_Demo.Median_Finalize)
$$$ThrowSQLIfError(SQLCODE,%msg)
}
}