Escrito por

Sales Engineer at InterSystems Iberia
Artículo Ricardo Paiva · 22 mayo 6m read

Particionado artesanal

IRIS 2026.1 proporcionó tablas particionadas como una nueva opción para grandes conjuntos de datos. Es una gran mejora, ya que ofrece estandarización de esta funcionalidad.

Sin embargo: antes también era posible hacerlo, cumpliendo los requisitos y dejando espacio para la creatividad. Era menos elegante, con un poco más de código y menos automatismos.

 

La necesidad es obvia.

Un ejemplo:
Tenéis una tabla PERSON que contiene Clientes, Empleados, Socios, Proveedores, ....

El enfoque clásico sería indexar los distintos roles o heredar clases individuales a partir de una clase común.

El impacto inmediato:
Todos los registros acaban en los mismos 3 Globals para datos, índices y streams.

Esto podría significar que buscar uno de 37 empleados tarde, de media, lo mismo que buscar uno entre 277.876 clientes o entre 1.612 proveedores.

Simplificado: son habitantes del mismo estanque de ranas.

Separar su almacenamiento en Globals distintos podría prometer una mejora.

El enfoque más simple podría ser usar 4 tablas con nombres diferentes, con todo el impacto asociado en la nomenclatura de las herramientas de mantenimiento. Muchas implementaciones en el campo funcionan así. @Iryna Mykhailova lo describe en su artículo Almacenamiento de datos para clases y sus superclases por separado.
 

Existe un enfoque más sofisticado

Desde hace tiempo, estos 2 parámetros de clase permiten una solución bastante dinámica.

  • El parámetro DEFAULTGLOBAL se utiliza como raíz global por defecto para los valores de las palabras clave de almacenamiento DATALOCATION, IDLOCATION, INDEXLOCATION y STREAMLOCATION. 
    • Por ejemplo, si DEFAULTGLOBAL = "^Demo.Person"
    • esto se compila como
      • DATALOCATION = ^Demo.PersonD
      • IDLOCATION = ^Demo.PersonD
      • INDEXLOCATION = ^Demo.PersonI
      • STREAMLOCATION = ^Demo.PersonS
  • El parámetro MANAGEDEXTENT puede establecerse en 0 (cero) para hacer que el gestor de extensiones ignore esta clase. Si se establece en 1, el gestor de extensiones registrará los globals utilizados por la clase y detectará colisiones.

Ahora podemos usar DEFAULTGLOBAL en combinación con indirectio

  • Parámetro DEFAULTGLOBAL As STRING = "@%rcc"; 
    • Asignación de Globals por indirección en tiempo de ejecución.
    • compilado como:
      • <DataLocation>@%rccD</DataLocation>
      • <DefaultData>RCCDefaultData</DefaultData>
      • <IdLocation>@%rccD</IdLocation>
      • <IndexLocation>@%rccI</IndexLocation>
      • <StreamLocation>@%rccS</StreamLocation>
  • Parámetro MANAGEDEXTENT As INTEGER = 0;
    • permite un cambio dinámico de los Globals de almacenamiento
    • y protege contra errores durante la compilación de la clase.

Solo tenéis que proporcionar los nombres correctos para estas 3 variables antes de su uso:

  • set %rccD = "^"%rcc"D"
  • set %rccI = "^"%rcc"I"
  • set %rccS = "^"%rcc"S"

Empaquetarlo en un método de clase pequeño no es gran cosa.

Proyectarlo como SqlProcedure también lo hace disponible para cualquier acceso SQL.

El ejemplo para el Global ^Person*:

SAMPLES>write ##class(RCC).%rcc("Person")
1
SAMPLES>zwrite
 
%rccD="^PersonD"
%rccI="^PersonI"
%rccS="^PersonS"
%sqlcontext=<OBJECT REFERENCE>[2@%Library.ProcedureContext]
SAMPLES>write ##class(RCC).Populate(11)
11

SAMPLES>:sql
SQL Command Line Shell
----------------------------------------------------
The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.
[SQL]SAMPLES>>select * from RCC where %rcc('Person')=1
1.      select * from RCC where %rcc('Person')=1
 
| ID | city | dob | name | score |
| -- | -- | -- | -- | -- |
| 1 | Vail | 40558 | Leiberman,Sally E. | 97 |
| 2 | Chicago | 58285 | Ott,Lydia H. | 44 |
| 3 | Pueblo | 58873 | Hertz,Usha L. | 45 |
| 4 | Gansevoort | 40744 | Gomez,Heloisa O. | 53 |
| 5 | Ukiah | 34822 | Macrakis,Keith S. | 39 |
| 6 | Ukiah | 31655 | Gore,Chris N. | 9 |
| 7 | Hialeah | 39868 | Grabscheid,Amanda O. | 66 |
| 8 | Vail | 60410 | Burroughs,Greta W. | 88 |
| 9 | Tampa | 43499 | Zevon,Al W. | 15 |
| 10 | Tampa | 59787 | Feynman,Juanita Q. | 93 |
| 11 | Miami | 53081 | Zucherro,Bart R. | 42 |
 
11 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0774s/44,458/226,219/0ms
          execute time(s)/globals/cmds/disk: 0.0004s/12/1,938/0ms
                                query class: %sqlcq.SAMPLES.cls1
---------------------------------------------------------------------------
[SQL]SAMPLES>>	-

Misma sesión con el Global ^mtemp.Work*:

SAMPLES>write ##class(RCC).%rcc("mtemp.Work")
1
SAMPLES>zw
 
%rccD="^mtemp.WorkD"
%rccI="^mtemp.WorkI"
%rccS="^mtemp.WorkS"
%sqlcontext=<OBJECT REFERENCE>[2@%Library.ProcedureContext]
SAMPLES>w ##class(RCC).Populate(5)
5

SAMPLES>:sql
SQL Command Line Shell
----------------------------------------------------
 
The command prefix is currently set to: <<nothing>>.
Enter <command>, 'q' to quit, '?' for help.
[SQL]SAMPLES>>select name,city from RCC where %rcc('mtemp.Work')=1 order by city 
2.      select name,city from RCC where %rcc('mtemp.Work')=1 order by city
 
| name | city |
| -- | -- |
| Gaboriault,Quentin O. | Bensonhurst |
| Baker,Andrew Q. | Fargo |
| Zweifelhofer,Clint T. | Miami |
| Fives,Patrick T. | Newton |
| Alton,Geoffrey I. | Pueblo |
 
5 Rows(s) Affected
statement prepare time(s)/globals/cmds/disk: 0.0596s/38,492/210,709/0ms
          execute time(s)/globals/cmds/disk: 0.0002s/6/919/0ms
                                query class: %sqlcq.SAMPLES.cls5
---------------------------------------------------------------------------
[SQL]SAMPLES>>

Y la definición de la clase:

Class User.RCC Extends (%Persistent, %Populate) [ Final ]
{
Parameter MANAGEDEXTENT As INTEGER = 0;
Parameter DEFAULTGLOBAL As STRING = "@%rcc";
Property name As %String(POPSPEC = "Name()", TRUNCATE = 1);
Property city As %String(POPSPEC = "City()", TRUNCATE = 1);
Property dob As %Date;
Property score As %Integer(POPSPEC = "Integer(0,100)");
//
Index nameIDX On name [ Data = city ];
Index cityIDX On city As SQLSTRING [ Type = index ];
Index nsIDX On (name, score);
Index ncIDX On (name, city);
Index dobIDX On dob [ Data = name ];
//
ClassMethod %rcc(%rcc = "") As %Integer [ SqlName = %rcc, SqlProc ]
{
if '$l(%rcc) set %rcc="mtemp."_$job
set %rccD="^"_%rcc_"D"  
set %rccI="^"_%rcc_"I"  
set %rccS="^"_%rcc_"S"
quit $$$OK
}
//
Storage Default
{
<Data name="RCCDefaultData">
<Value name="1">
<Value>name</Value>
</Value>
<Value name="2">
<Value>city</Value>
</Value>
<Value name="3">
<Value>dob</Value>
</Value>
<Value name="4">
<Value>score</Value>
</Value>
</Data>
<DataLocation>@%rccD</DataLocation>
<DefaultData>RCCDefaultData</DefaultData>
<IdLocation>@%rccD</IdLocation>
<IndexLocation>@%rccI</IndexLocation>
<StreamLocation>@%rccS</StreamLocation>
<Type>%Storage.Persistent</Type>
}
}

Resumen:

Todo lo que necesitáis para usar este enfoque en ISOS:

;; estableced vuestros globals de acceso antes de usarlo
do ##class(RCC).%rcc("your.globalname")

Para SQL usad la condición WHERE estática correspondiente:

SELECT ........ from RCC WHERE %rcc('your.globalname')=1 ....

Y esto funciona para cualquier referencia válida a un Global,
incluyendo globals privados de proceso, globals temporales, referencias extendidas, etc.

Si no proporcionáis nada, se utiliza "mtemp."_$job

Comments