Las funciones de agregación definidas por el usuario han sido compatibles con IRIS desde la versión 2021.1.0. Hace años deseaba tener esta funcionalidad antes de encontrar una forma alternativa y secreta de sobrescribir MAX y MIN en un tipo de dato personalizado, pero no tuve la oportunidad de probarlo realmente hasta hoy. Me pareció una experiencia/ejemplo interesante – ya antes surgió la pregunta de cómo obtener la Mediana en IRIS SQL – así que lo comparto aquí sin extenderme demasiado.
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 DC.Demo.Median
{
ClassMethod Initialize() As %String [ PublicList = ref, SqlProc ]
{
New ref
Set ref = $Name(^IRIS.Temp.UDAF.Median($Increment(^IRIS.Temp.UDAF.Median)))
Set @ref = 0
Quit ref
}
ClassMethod Iterate(ref As %String, value As %Numeric) As %String [ SqlProc ]
{
If (value '= "") {
Do $Increment(@ref)
Do $Increment(@ref@(+value))
}
Quit ref
}
ClassMethod Finalize(ref As %String) As %Numeric [ SqlProc ]
{
Set median = ""
Set total = @ref
Set position1 = (total+1)\2
Set position2 = (total+2)\2
Set val1 = ""
Set val2 = ""
Set reached = 0
Set 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)/2
Quit
}
}
Kill @ref
Quit median
}
ClassMethod Define()
{
&sql(DROP AGGREGATE DC_Demo.Median)
&sql(CREATE AGGREGATE 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)
}
}
¡Espero que esto ayude a alguien!