1. Diferencia tipos de datos de fecha y hora (%Date, %Time, %TimeStamp, %PosixTime)
Puntos Clave
- %Date: Almacena solo fecha, valor lógico es días enteros desde el 31 de diciembre de 1840
- %Time: Almacena solo hora, valor lógico es segundos pasados desde medianoche (0-86399)
- %TimeStamp: Fecha y hora combinadas en formato YYYY-MM-DD HH:MM:SS.nnnnnnnnn
- %PosixTime: Marca de tiempo codificada de 64 bits, segundos transcurridos desde 1970-01-01 00:00:00
- Cada tipo tiene valores SqlCategory distintos: DATE, TIME, TIMESTAMP y POSIXTS
Notas Detalladas
Tipo de Datos %Date
El tipo de datos %Date almacena valores solo de fecha con una representación lógica como un entero que representa el número de días desde el 31 de diciembre de 1840 (día 0).
- Formato de almacenamiento: Representación entera compacta
- Rango de fechas: Soporta fechas desde 0001-01-01 hasta 9999-12-31
- Fechas anteriores a 1840: La época (día 0) es 1840-12-31, pero el entero tiene signo — las fechas anteriores a 1840-12-31 se almacenan como número de días negativo. Por ejemplo, 0001-01-01 corresponde aproximadamente al día -671.861. Por eso el rango soportado se extiende mucho antes de 1840.
- Formato de visualización: Formatos específicos de configuración regional o formato ODBC (YYYY-MM-DD)
- SqlCategory: DATE
Tipo de Datos %Time
El tipo de datos %Time representa valores de hora del día, almacenando el valor lógico como el número de segundos pasados desde medianoche.
- Rango de valores: 0 a 86399 segundos
- Formato de visualización: HH:MM:SS
- SqlCategory: TIME
- Caso de uso: Aritmética y comparaciones de tiempo eficientes
Tipo de Datos %TimeStamp
El tipo de datos %TimeStamp combina componentes de fecha y hora en un solo valor.
- Formato: YYYY-MM-DD HH:MM:SS.nnnnnnnnn
- Precisión: Hasta nueve dígitos de segundos fraccionarios
- Conversión: Valor lógico obtenido del formato $HOROLOG usando $ZDATETIME(h,3)
- SqlCategory: TIMESTAMP
- Caso de uso: Registrar momentos exactos en el tiempo (formato de marca de tiempo ODBC estándar)
Tipo de Datos %PosixTime
El tipo de datos %PosixTime ofrece una representación alternativa de marca de tiempo usando codificación de época Unix.
- Almacenamiento: Entero con signo codificado de 64 bits que representa segundos transcurridos con precisión de microsegundos
- Época: 1970-01-01 00:00:00 (época Unix, NO la época 1840 de %Date). La forma codificada en la época es 1152921504606846976; la codificación empaqueta el signo, los segundos y los microsegundos en 64 bits.
- Rango de fechas: 0001-01-01 a 9999-12-31 (el mismo rango que otros tipos temporales, logrado mediante el rango del entero de 64 bits con signo)
- Precisión: Exactamente 6 dígitos de segundos fraccionarios (microsegundos)
- SqlCategory: POSIXTS
- Nota: Las fechas anteriores a 1970 se codifican con valores con signo negativo. %PosixTime y %Date usan épocas diferentes, por lo que no se debe convertir entre ellas restando valores; usar
TO_POSIXTIME()/TO_DATE()para conversiones correctas.
SqlCategory y Conversión de Tipos
Cada tipo de datos tiene una clasificación SqlCategory distinta que controla operaciones de comparación.
- Funciones de conversión: Usar DATE(), TO_TIMESTAMP(), o TO_POSIXTIME() al mezclar tipos temporales en comparaciones o índices
- Impacto en diseño de esquema: La elección del tipo de datos temporal afecta la eficiencia de almacenamiento, rendimiento de consultas e interoperabilidad con sistemas externos
- Recomendaciones:
- %Date y %Time: Almacenamiento más compacto para valores solo de fecha u hora
- %TimeStamp: Marcas de tiempo de precisión legibles por humanos
- %PosixTime: Compatibilidad con marcas de tiempo Unix y cálculos temporales de alta precisión
Referencias de Documentación
2. Propiedades de Colección (list Of, array Of)
Puntos Clave
- Dos formas integradas de propiedades de colección: `Property X As list Of %Type` y `Property X As array Of %Type`.
- `list Of`: se almacena en línea en la fila del padre como una única columna codificada con `$LISTBUILD`. No se crea tabla hija.
- `array Of`: se almacena como tabla hija separada con una clave foránea hacia el padre — cada elemento del array es una fila.
- Buscar dentro de un `list Of` en SQL: usar el predicado `FOR SOME %ELEMENT(list) (%Value = 'X')`. No usar `LIKE '%X%'` contra el blob $LISTBUILD — buscaría bytes crudos y daría resultados erróneos.
- Indexar dentro de colecciones: usar un índice de colección (`Index i On Allergies(ELEMENTS)` para list, o bitmap sobre la tabla hija para array).
Notas Detalladas
list Of %String — almacenamiento en línea
Class App.Patient Extends %Persistent {
Property Name As %String;
Property Allergies As list Of %String;
}
Vista SQL de la tabla:
| ID | Name | Allergies |
|---|---|---|
| 1 | Smith | $LB("Penicilina","Maní") |
La columna Allergies contiene el blob codificado con $LISTBUILD directamente; no hay tabla hija. Para buscar:
SELECT ID, Name FROM App.Patient
WHERE FOR SOME %ELEMENT(Allergies) (%Value = 'Penicilina')
array Of %String — almacenamiento en tabla hija
Class App.Patient Extends %Persistent {
Property Name As %String;
Property Phones As array Of %String;
}
IRIS crea una tabla hija App.Patient_Phones con columnas (Patient, element_key, Phones). Cada teléfono es una fila. JOIN/WHERE estándar funcionan sobre la tabla hija.
Comparación
| Aspecto | list Of | array Of |
|---|---|---|
| Almacenamiento SQL | columna en línea, codificada $LISTBUILD | tabla hija separada |
| Tiene clave | posicional (ordinal) | con clave (arbitraria por elemento) |
| Uso típico | multi-valor pequeño y fijo (alergias, etiquetas) | atributos con clave (teléfonos por tipo) |
| Búsqueda | FOR SOME %ELEMENT | JOIN sobre tabla hija |
| Indexado | índice de elemento de colección sobre el padre | índice sobre la tabla hija |
Referencias de Documentación
3. Objetos Seriales (%SerialObject)
Puntos Clave
- Una clase `%SerialObject` define una estructura reutilizable (p. ej. `Address`) que se embebe en la fila padre en lugar de almacenarse como objeto persistente separado.
- Las propiedades de un %SerialObject se serializan con `$LISTBUILD` y se almacenan como una única columna en el padre.
- SQL proyecta la estructura como columnas aplanadas con guiones bajos: `HomeAddress_Street`, `HomeAddress_City`, etc.
- Sin tabla separada; sin clave foránea. No se puede `SELECT * FROM App.Address` — un %SerialObject no tiene extent propio.
- Usar cuando la estructura embebida no tiene identidad propia y siempre pertenece a su padre (direcciones, dinero-con-moneda, teléfono-con-extensión).
Notas Detalladas
Class App.Address Extends %SerialObject {
Property Street As %String;
Property City As %String;
}
Class App.Person Extends %Persistent {
Property Name As %String;
Property HomeAddress As App.Address;
}
Proyección SQL de App.Person:
| ID | Name | HomeAddress_Street | HomeAddress_City |
|---|---|---|---|
| 1 | Smith | Calle Mayor 42 | Madrid |
Para consultar un campo interno: SELECT Name, HomeAddress_City FROM App.Person. Las columnas aplanadas con guión bajo son direccionables desde SQL; la clase App.Address en sí no es una tabla.
Referencias de Documentación
4. Propiedades de Stream (%Stream.GlobalCharacter, %Stream.GlobalBinary)
Puntos Clave
- Las propiedades de stream (`%Stream.GlobalCharacter`, `%Stream.GlobalBinary`) están diseñadas para valores mayores que el límite de 3,6 MB de cadena — documentos, imágenes, blobs serializados.
- Se almacenan separadamente de los datos principales de la fila; la fila contiene solo un localizador.
- Limitaciones en SQL: los streams no pueden usarse directamente en `WHERE`, `ORDER BY`, `GROUP BY`, ni en la mayoría de expresiones. NO son indexables en su totalidad.
- Soportado en SQL: `SELECT` (recupera el stream), `INSERT`/`UPDATE` con un valor de stream, funciones de longitud/posición donde aplican.
- Para buscar DENTRO de un stream, usar `%CONTAINS` con un índice iFind/texto configurado (`Index ci On (Content) As %iFind.Index.Basic`), no `LIKE`.
Notas Detalladas
Class App.Document Extends %Persistent {
Property Title As %String;
Property Content As %Stream.GlobalCharacter;
Index ci On (Content) As %iFind.Index.Basic;
}
Operaciones típicas:
-- OK: recuperar stream (el cliente ensambla el stream desde el cursor)
SELECT Title, Content FROM App.Document WHERE ID = 1
-- NO soportado: WHERE sobre columna stream directamente
SELECT * FROM App.Document WHERE Content LIKE '%confidencial%' -- error o resultados incorrectos
-- Forma correcta: %CONTAINS con índice iFind
SELECT ID, Title FROM App.Document WHERE %ID %FIND search_index(ci, 'confidencial')
Referencias de Documentación
5. Propiedades Computadas y Calculadas
Puntos Clave
- `Calculated`: el valor se computa cada vez que se lee; no hay almacenamiento. Requiere un accesor `
Get()` (o `SqlComputeCode` del que IRIS genera uno automáticamente). - `SqlComputed`: IRIS genera el valor almacenado a partir de `SqlComputeCode`. Combinado con `SqlComputeOnChange` (listando las propiedades dependientes), el valor se recalcula automáticamente cuando cambian esas propiedades — y se persiste en la columna, de modo que las consultas SQL son rápidas.
- `Transient`: solo en memoria; no se almacena, no se proyecta a SQL.
- Regla práctica: usar `Calculated` para derivaciones siempre-frescas que NO necesitan consultarse; usar `SqlComputed` + `SqlComputeOnChange` cuando el valor derivado deba ser indexable / filtrable con WHERE en tablas grandes.
- Importante: una propiedad `Calculated` NO participa en índices SQL (no hay nada que indexar). Si `WHERE Age = 30` debe usar un índice en una tabla de 10M filas, usar `SqlComputed`, no `Calculated`.
Notas Detalladas
// FullName — siempre fresco, recomputado en lectura, no indexable
Property FullName As %String [ Calculated,
SqlComputeCode = {Set {*} = {FirstName}_" "_{LastName}},
SqlComputed ];
// Age — almacenado, auto-actualizado cuando cambia DateOfBirth, indexable
Property Age As %Integer [
SqlComputed,
SqlComputeCode = {Set {*} = $SYSTEM.SQL.Functions.DATEDIFF("yy",{DateOfBirth},$HOROLOG)},
SqlComputeOnChange = DateOfBirth ];
Index AgeIdx On Age;
Para una tabla de 10M filas consultada frecuentemente con WHERE Age = 30, la segunda forma es correcta — el valor computado se materializa en la fila, y un índice sobre Age hace que la búsqueda sea O(log n) en lugar de full table scan.
Referencias de Documentación
6. Almacenamiento Columnar y Cargas OLTP vs Analíticas
Puntos Clave
- El almacenamiento por defecto en IRIS es orientado a filas — ideal para OLTP (inserts rápidos, lookups por clave primaria, recuperación de filas completas).
- Para cargas analíticas (agregaciones sobre millones de filas en pocas columnas), el almacenamiento columnar es mucho más eficiente: compresión, escaneos vectorizados y layout de memoria amigable para CPU.
- Habilitar almacenamiento columnar por clase (`Storage Columnar`) o por columna (`[ Columnar ]` sobre una propiedad) según versión/necesidad.
- Cargas mixtas** (inserts OLTP rápidos + informes analíticos periódicos) suelen beneficiarse de **almacenamiento en capas: row-store en el camino caliente, columnar en columnas específicas para agregación.
- El almacenamiento columnar paga dividendos en consultas como `SELECT SUM(Amount) FROM Sales WHERE RegionId = ...` sobre una tabla grande. NO ayuda mucho en `SELECT * FROM Sales WHERE ID = 1`.
Notas Detalladas
El almacenamiento columnar se configura directamente en DDL — no hace falta una definición de clase.
-- Mixto: tabla row-storage con columnas en columnar para las que dirigen la agregación
CREATE TABLE App.Sale (
OrderId INTEGER PRIMARY KEY,
RegionId INTEGER,
Amount NUMERIC(18,2) WITH STORAGETYPE = COLUMNAR,
Quantity INTEGER WITH STORAGETYPE = COLUMNAR
);
-- Tabla totalmente columnar — cargas analíticas puras
CREATE TABLE App.SalesFact (
Id BIGINT PRIMARY KEY,
RegionId INTEGER,
Amount NUMERIC(18,2),
Quantity INTEGER
) WITH STORAGETYPE = COLUMNAR;
-- Añadir una nueva columna columnar a una tabla existente
ALTER TABLE App.Sale ADD COLUMN Discount NUMERIC(18,2) WITH STORAGETYPE = COLUMNAR;
El mismo efecto se puede expresar en una definición de clase ObjectScript (Property Amount As %Numeric(SCALE=2) [ Columnar ] para una columna, o el flag [ StorageStrategy = Columnar ] a nivel de clase para una tabla totalmente columnar) — ambos caminos producen el mismo layout físico.
Referencias de Documentación
7. Control de Concurrencia Optimista
Puntos Clave
- Concurrencia optimista: los lectores no bloquean — en el update, se verifica que la fila no haya cambiado desde la lectura.
- Añadir una columna de versión (`%Integer`/`%TimeStamp`) y declararla vía el parámetro de clase `VERSIONPROPERTY` (o la palabra clave de clase `VersionProperty`). IRIS la incrementa automáticamente en cada save exitoso.
- La lógica de UPDATE usa entonces `WHERE ID = :id AND Version = :readVersion` — si cero filas se actualizan, otro usuario se adelantó, y la aplicación reintenta/mezcla/lanza error.
- Alternativa: calcular un hash MD5/SHA de la fila y revalidar en el update. Más pesado; usar VersionProperty cuando se pueda.
- El bloqueo pesimista (`SELECT ... FOR UPDATE`) es la estrategia opuesta — más simple para secciones críticas cortas pero bloquea a otros lectores.
Notas Detalladas
Class App.Order Extends %Persistent [ VersionProperty = Version ] {
Property Quantity As %Integer;
Property Version As %Integer [ InitialExpression = 0 ];
}
Patrón de aplicación:
-- Lectura
SELECT Quantity, Version INTO :qty, :ver FROM App.Order WHERE ID = :id
-- ... el usuario edita ...
-- Update protegido por la versión
UPDATE App.Order SET Quantity = :newQty WHERE ID = :id AND Version = :ver
IF %ROWCOUNT = 0 THEN
-- lost-update: otra sesión modificó la fila
END
Referencias de Documentación
Resumen de Preparación para el Examen
Conceptos Críticos a Dominar:
- Formatos de Almacenamiento Lógico: Saber que %Date almacena días desde el 31 de dic de 1840, %Time almacena segundos desde medianoche, %TimeStamp usa formato ODBC, %PosixTime usa marca de tiempo Unix codificada
- Valores SqlCategory: Memorizar las cuatro categorías distintas (DATE, TIME, TIMESTAMP, POSIXTS) y su impacto en comparaciones
- Diferencias de Precisión: %TimeStamp soporta hasta 9 dígitos de segundos fraccionarios, %PosixTime codifica exactamente 6 dígitos, %Time no tiene segundos fraccionarios
- Funciones de Conversión: Comprender cuándo usar DATE(), TO_TIMESTAMP(), TO_POSIXTIME() para conversión de tipos
- Selección de Caso de Uso: Saber cuándo elegir cada tipo - %Date para solo fechas, %Time para solo horas, %TimeStamp para marcas de tiempo generales, %PosixTime para compatibilidad Unix
Escenarios Comunes del Examen:
- Seleccionar el tipo de datos temporal apropiado para un requisito de aplicación dado
- Convertir entre diferentes formatos temporales usando funciones SQL
- Comprender las limitaciones de rango y precisión de cada tipo
- Reconocer la naturaleza codificada de valores %PosixTime
- Trabajar con variables especiales $HOROLOG y $ZTIMESTAMP en contexto SQL
Recomendaciones de Práctica Práctica:
- Crear tablas con columnas de cada tipo temporal e insertar datos de muestra
- Practicar conversiones entre tipos usando DATE(), TO_TIMESTAMP() y TO_POSIXTIME()
- Experimentar con precisión de segundos fraccionarios en %TimeStamp y %PosixTime
- Consultar datos temporales con diferentes modos de visualización (Logical, ODBC, Display)
- Comparar rendimiento y características de almacenamiento de cada tipo
- Trabajar con fechas antes de 1970 para comprender valores negativos de %PosixTime