Artículo
· 5 nov, 2024 Lectura de 2 min

Escribiendo una función de agregación definida por el usuario en IRIS - ejemplo: Mediana

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 implementing a Median aggregate function for IRIS SQL
Class DC.Demo.Median
{

/// Returns a new global ref in IRISTEMP to use to store intermediate results
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
}

/// Updates temp global for a single record
ClassMethod 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)\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
}

/// To actually define the UDAF from an SQL perspective, call this classmethod.
ClassMethod Define()
{
	// Drop the function in case something has changed
	&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!

Comentarios (0)1
Inicie sesión o regístrese para continuar