1. ObjectScript como lenguaje débilmente tipado
Puntos Clave
- Débilmente tipado: Las variables no tienen tipo declarado; la misma variable puede contener una cadena, número o referencia a objeto
- Coerción automática: ObjectScript convierte entre cadenas y números basándose en el operador utilizado
- Regla de cadena a número: Se extrae la porción numérica inicial; las cadenas no numéricas evalúan a 0
- Coerción aritmética: `"3" + 2` evalúa a `5`; `"abc" + 1` evalúa a `1`; `"7dwarfs" + 3` evalúa a `10`
- Concatenación de cadenas: Usar el operador `_` para concatenar; `3 _ 2` produce `"32"`
- Evaluación booleana: La cadena vacía `""` y `0` son falsos; cualquier valor numérico distinto de cero o cadena no vacía con porción numérica inicial distinta de cero es verdadero
Notas Detalladas
Descripción general
ObjectScript es un lenguaje débilmente (o laxamente) tipado, lo que significa que las variables no tienen tipos fijos. Una variable que contiene un número puede luego contener una cadena o una referencia a objeto sin ninguna declaración o conversión. La coerción de tipos ocurre automáticamente basándose en el contexto en que se usa un valor, particularmente el operador que se aplica.
Reglas de coerción numérica
Cuando ObjectScript encuentra un operador aritmético (+, -, *, /, \, #, **), intenta interpretar los operandos como números. La regla de conversión extrae la porción numérica inicial de una cadena:
WRITE "3" + 2 // 5 (la cadena "3" se convierte al número 3)
WRITE "abc" + 1 // 1 ("abc" no tiene número inicial, se convierte a 0)
WRITE "7dwarfs" + 3 // 10 ("7dwarfs" porción numérica inicial = 7)
WRITE "3.14xyz" + 0 // 3.14 (se extrae la porción numérica inicial)
WRITE "" + 5 // 5 (la cadena vacía se convierte a 0)
WRITE " 42" + 0 // 0 (el espacio inicial NO es un dígito, así que el valor numérico es 0)
WRITE "1E2" + 0 // 100 (la notación científica se reconoce)
WRITE "--5" + 0 // 0 (doble negativo no es numérico inicial válido)
Concatenación de cadenas
El operador de concatenación _ fuerza el contexto de cadena. Los números se convierten a su representación como cadena:
WRITE 3 _ 2 // "32"
WRITE "Hello" _ " " _ "World" // "Hello World"
WRITE 3.14 _ " pi" // "3.14 pi"
Operadores de comparación
ObjectScript proporciona operadores separados para comparación de cadenas y numérica:
- Comparación numérica:
=,<,>,'=,'<,'>(los operandos se convierten a números cuando ambos son cadenas numéricas) - Operadores de cadena:
[(contiene),](sigue/ordena después),]](ordena después en colación) - El operador
=realiza comparación de cadena si cualquier operando es no numérico:"01" = 1es TRUE (numérico), pero"01" = "1"es FALSE (comparación de cadena cuando se prueba con]])
WRITE ("3" = 3) // 1 (verdadero - comparación numérica)
WRITE ("03" = 3) // 1 (verdadero - comparación numérica, cero inicial eliminado)
WRITE ("abc" = 0) // 1 (verdadero - "abc" convertido a 0, 0 = 0)
WRITE ("abc" = "") // 0 (falso - comparación de cadena ya que ninguno es puramente numérico en contexto de cadena)
Contexto booleano
En contexto booleano (IF, WHILE, etc.), ObjectScript evalúa valores como: 0 y "" son falsos; todo lo demás que evalúe a distinto de cero es verdadero:
IF "abc" { WRITE "truthy" } // "abc" convertido a 0, así que esto es FALSE
IF "1abc" { WRITE "truthy" } // "1abc" convertido a 1, así que esto es TRUE
IF " " { WRITE "truthy" } // " " convertido a 0, así que esto es FALSE
Referencias de Documentación
2. Macros del sistema y archivos de inclusión
Puntos Clave
- Directiva #Include: Incluye definiciones de macros desde archivos `.inc` en tiempo de compilación
- Sintaxis de macros: `$$$macroName` para macros de expresión, `$$$macroName(args)` para macros parametrizadas
- Macros comunes: `$$$OK`, `$$$ISERR(sc)`, `$$$ThrowOnError(sc)`, `$$$ThrowStatus(sc)`, `$$$ISOK(sc)`
- Patrón %Status: Los métodos devuelven valores `%Status`; verificar con macros `$$$ISERR` / `$$$ISOK`
- Directiva #Define: Crea macros personalizadas dentro de clases o archivos de inclusión
- Ubicación de archivos de inclusión: Almacenados como rutinas con extensión `.inc`, típicamente en `%occStatus.inc`, `%occErrors.inc`
Notas Detalladas
Descripción general
ObjectScript usa un preprocesador estilo C que procesa directivas en tiempo de compilación. Las macros proporcionan fragmentos de código reutilizables, constantes y patrones estandarizados. El mecanismo de archivos de inclusión permite compartir definiciones de macros entre múltiples clases y rutinas.
Archivos de inclusión y directiva #Include
Los archivos de inclusión tienen la extensión .inc y contienen definiciones de macros. Se incluyen en tiempo de compilación usando la directiva #Include, que debe aparecer antes de la definición de clase o al inicio de una rutina:
#Include %occStatus
#Include %occErrors
Class MyApp.Service Extends %RegisteredObject
{
// $$$OK, $$$ISERR, etc. están ahora disponibles
}
Se pueden incluir múltiples archivos de inclusión, y los archivos de inclusión pueden a su vez incluir otros archivos. La directiva #IncludeGenerator se usa para macros necesarias en generadores de métodos.
Definir macros
Use #Define para crear macros. Las macros de expresión devuelven valores; las macros de código se expanden a sentencias:
#Define AppName "MyApplication"
#Define MaxRetries 3
#Define Log(%msg) DO ##class(%SYS.System).WriteToConsoleLog(%msg)
#Define ThrowIfError(%sc) IF $$$ISERR(%sc) { THROW ##class(%Exception.StatusException).CreateFromStatus(%sc) }
Macros esenciales de %Status
El sistema %Status es central para el manejo de errores en InterSystems IRIS. La mayoría de los métodos del sistema devuelven un valor %Status:
#Include %occStatus
// Verificar valores de estado
SET sc = obj.%Save()
// Usando $$$ISOK / $$$ISERR
IF $$$ISERR(sc) {
// Manejar error
SET errorText = $SYSTEM.Status.GetErrorText(sc)
WRITE "Error: ", errorText, !
}
// $$$ThrowOnError - lanza excepción si el estado es error
SET sc = obj.%Save()
$$$ThrowOnError(sc)
// $$$OK - representa un estado exitoso
RETURN $$$OK
// Crear estado de error
SET sc = $$$ERROR($$$GeneralError, "Something went wrong")
// Combinar estados
SET combinedSc = $SYSTEM.Status.AppendStatus(sc1, sc2)
Referencia de macros comunes
| Macro | Fuente | Propósito |
|---|---|---|
$$$OK | %occStatus.inc | Valor %Status exitoso (literal 1) |
$$$ISOK(sc) | %occStatus.inc | Devuelve verdadero si el estado es éxito |
$$$ISERR(sc) | %occStatus.inc | Devuelve verdadero si el estado es error |
$$$ThrowOnError(sc) | %occStatus.inc | Lanza excepción si el estado es error |
$$$ThrowStatus(sc) | %occStatus.inc | Lanza un %Status como excepción incondicionalmente |
$$$ERROR(code,args...) | %occErrors.inc | Crea un %Status de error |
$$$GeneralError | %occErrors.inc | Constante de código de error genérico |
$$$NULLOREF | %occStatus.inc | Referencia de objeto vacía ("") |
Condicionales del preprocesador
El preprocesador también soporta compilación condicional:
#IfDef DEBUG
WRITE "Debug mode: variable = ", var, !
#Else
// Código de producción - sin salida de depuración
#EndIf
Referencias de Documentación
3. Proyección de objeto a SQL
Puntos Clave
- Clase se mapea a tabla: Una clase `%Persistent` se proyecta automáticamente como una tabla SQL
- Propiedad se mapea a columna: Cada propiedad se convierte en una columna en la tabla proyectada
- Paquete se mapea a esquema: El nombre del paquete se convierte en el nombre del esquema SQL (`.` reemplazado por `_`)
- Se requiere %Persistent: Solo las clases que extienden `%Persistent` obtienen proyección de tabla SQL
- Columna ID: Cada tabla tiene una columna `ID` implícita del `%ID()` del objeto (OID)
- Nomenclatura: `Package.ClassName` se proyecta como `Package.ClassName` en SQL (con `_` para subpaquetes)
Notas Detalladas
Descripción general
InterSystems IRIS implementa una arquitectura de datos unificada donde los mismos datos son accesibles simultáneamente a través de interfaces de objeto y relacionales (SQL). Cuando define una clase persistente, el sistema genera automáticamente una proyección de tabla SQL sin ninguna configuración adicional. Este modelo de doble acceso es un diferenciador fundamental de InterSystems IRIS.
Mapeo de clase a tabla
Cada clase que extiende %Persistent (directa o indirectamente) se proyecta como una tabla SQL. La definición de clase sirve como la única fuente de verdad tanto para el modelo de objetos como para el esquema relacional:
Class MyApp.Data.Patient Extends %Persistent
{
Property Name As %String(MAXLEN = 100);
Property DOB As %Date;
Property SSN As %String(MAXLEN = 11);
Property Active As %Boolean;
Index SSNIdx On SSN [ Unique ];
}
Esta clase se proyecta automáticamente como:
- Tabla SQL:
MyApp_Data.Patient - Columnas:
ID,Name,DOB,SSN,Active - Índice único:
SSNIdxsobre la columnaSSN
Convención de mapeo de nombres
| Concepto de Objeto | Concepto SQL | Ejemplo |
|---|---|---|
| Paquete | Esquema | MyApp.Data -> MyApp_Data |
| Clase | Tabla | Patient -> Patient |
| Propiedad | Columna | Name -> Name |
| Clase %Persistent | Tabla con almacenamiento | Proyección completa |
| Clase %RegisteredObject | Sin tabla | No se proyecta |
| Relación | Clave foránea | Generada automáticamente |
| Índice | Índice SQL | Mismo nombre |
| Método de clase (SqlProc) | Procedimiento almacenado | Invocable desde SQL |
Acceso por objeto vs SQL a los mismos datos
// Acceso por objeto - crear y guardar
SET patient = ##class(MyApp.Data.Patient).%New()
SET patient.Name = "Smith, John"
SET patient.DOB = $HOROLOG
SET patient.Active = 1
SET sc = patient.%Save()
// Los datos guardados son inmediatamente accesibles vía SQL
&sql(SELECT Name, DOB INTO :name, :dob
FROM MyApp_Data.Patient
WHERE ID = 1)
// Acceso SQL - mismos datos subyacentes
&sql(INSERT INTO MyApp_Data.Patient (Name, DOB, Active)
VALUES ('Jones, Mary', CURRENT_DATE, 1))
// La fila insertada es accesible como objeto
SET patient2 = ##class(MyApp.Data.Patient).%OpenId(2)
WRITE patient2.Name // "Jones, Mary"
La columna ID
Cada tabla proyectada tiene una columna ID implícita que corresponde al ID interno del objeto. Este es el valor devuelto por %Id() y utilizado por %OpenId(). Por defecto, el sistema asigna IDs enteros auto-incrementales, pero esto se puede personalizar con definiciones de índice IdKey.
Referencias de Documentación
4. SQL embebido vs SQL dinámico
Puntos Clave
- SQL embebido: Sintaxis `&sql()`, **compilación diferida** (se compila en la primera ejecución, no en tiempo de compilación de la clase), estructura de consulta fija
- SQL dinámico: Clase `%SQL.Statement`, se prepara y ejecuta en **tiempo de ejecución**, construcción flexible de consultas
- Variables host: El SQL embebido usa `:variable` para entrada/salida; el SQL dinámico usa parámetros `?`
- Rendimiento: Ambos usan compilación diferida/en tiempo de ejecución; el SQL embebido tiene una sintaxis de invocación más simple
- Flexibilidad: El SQL dinámico permite construir consultas desde entrada del usuario o configuración en tiempo de ejecución
- SQLCODE: Ambos establecen SQLCODE; el SQL embebido lo establece directamente, el SQL dinámico a través de métodos del conjunto de resultados
Notas Detalladas
Descripción general
InterSystems IRIS soporta dos métodos principales de ejecución de SQL desde ObjectScript: SQL Embebido y SQL Dinámico. Ambos usan compilación diferida (el SQL se compila en la primera ejecución, no en tiempo de compilación de la clase). Entender sus diferencias, ventajas y casos de uso apropiados es esencial para el examen.
SQL Embebido (&sql)
El SQL Embebido se escribe directamente en código ObjectScript usando la sintaxis &sql(). La sentencia SQL usa compilación diferida: no se compila cuando se compila la clase/rutina. En su lugar, el SQL se compila en su primera ejecución en tiempo de ejecución. Esto significa que los errores de sintaxis SQL solo se descubren en tiempo de ejecución, no durante la compilación de la clase.
// SELECT simple con variables host
SET patientId = 12345
&sql(SELECT Name, DOB INTO :name, :dob
FROM MyApp_Data.Patient
WHERE ID = :patientId)
IF SQLCODE = 0 {
WRITE "Name: ", name, !
WRITE "DOB: ", dob, !
} ELSEIF SQLCODE = 100 {
WRITE "Patient not found", !
} ELSE {
WRITE "SQL Error: ", SQLCODE, " - ", %msg, !
}
// Consulta basada en cursor para múltiples filas
&sql(DECLARE PatCursor CURSOR FOR
SELECT Name, DOB FROM MyApp_Data.Patient
WHERE Active = 1)
&sql(OPEN PatCursor)
FOR {
&sql(FETCH PatCursor INTO :name, :dob)
QUIT:SQLCODE'=0
WRITE name, " - ", dob, !
}
&sql(CLOSE PatCursor)
Ventajas del SQL Embebido:
- Sintaxis más simple y concisa para consultas directas
- La estructura de la consulta es fija y clara en el código
- Las variables host (
:var) proporcionan acceso directo a variables de ObjectScript
Limitaciones del SQL Embebido:
- La estructura de la consulta debe conocerse en tiempo de compilación
- No se pueden cambiar dinámicamente nombres de tabla, listas de columnas o condiciones WHERE
- Los cambios requieren recompilación
SQL Dinámico (%SQL.Statement)
El SQL Dinámico usa la clase %SQL.Statement para preparar y ejecutar sentencias SQL en tiempo de ejecución. Esto proporciona máxima flexibilidad para construir consultas dinámicamente.
// SQL Dinámico básico
SET stmt = ##class(%SQL.Statement).%New()
SET sc = stmt.%Prepare("SELECT Name, DOB FROM MyApp_Data.Patient WHERE ID = ?")
IF $$$ISERR(sc) {
WRITE "Prepare error: ", $SYSTEM.Status.GetErrorText(sc), !
QUIT
}
SET rs = stmt.%Execute(12345)
WHILE rs.%Next() {
WRITE "Name: ", rs.%Get("Name"), !
WRITE "DOB: ", rs.%Get("DOB"), !
}
IF rs.%SQLCODE < 0 {
WRITE "SQL Error: ", rs.%SQLCODE, " - ", rs.%Message, !
}
// Construcción dinámica de consultas
SET sql = "SELECT Name, DOB FROM MyApp_Data.Patient WHERE 1=1"
SET params = 0
IF activeOnly {
SET sql = sql _ " AND Active = ?"
SET params = params + 1
SET params(params) = 1
}
IF nameFilter '= "" {
SET sql = sql _ " AND Name LIKE ?"
SET params = params + 1
SET params(params) = nameFilter _ "%"
}
SET sc = stmt.%Prepare(sql)
// Ejecutar con número variable de parámetros
SET rs = stmt.%Execute(params...)
Ventajas del SQL Dinámico:
- Estructura de consulta determinada en tiempo de ejecución
- Puede construir consultas dinámicamente basándose en entrada del usuario
- No requiere recompilación cuando la lógica de consulta cambia
- Mejor para reportes ad-hoc e interfaces de búsqueda
Limitaciones del SQL Dinámico:
- La preparación en tiempo de ejecución agrega sobrecarga
- Los errores SQL solo se descubren en tiempo de ejecución
- Sintaxis más detallada
Tabla comparativa
| Característica | SQL Embebido | SQL Dinámico |
|---|---|---|
| Sintaxis | &sql(...) | %SQL.Statement |
| Validación | Tiempo de ejecución (primera ejecución) | Tiempo de ejecución (%Prepare) |
| Compilación | Diferida (primera ejecución) | Tiempo de ejecución (%Prepare, cacheada) |
| Variables host | :varName | Marcadores de posición ? |
| Flexibilidad | Estructura fija | Estructura dinámica |
| Rendimiento | Ligeramente más rápido | Sobrecarga de preparación |
| Detección de errores | Temprana (compilación) | Tardía (tiempo de ejecución) |
Referencias de Documentación
5. Modo de ejecución y modos de selección (Logical/ODBC/Display)
Puntos Clave
- Tres modos: Logical (almacenamiento interno), ODBC (intercambio estándar), Display (legible por humanos)
- Modo Logical: Por defecto para SQL Embebido; valores como se almacenan internamente (ej., `%Date` como entero `$HOROLOG`)
- Modo ODBC: Convierte a formatos estándar (ej., fechas como `YYYY-MM-DD`); por defecto para conexiones ODBC/JDBC
- Modo Display: Convierte a formato de visualización específico del locale (ej., `MM/DD/YYYY`)
- %SQL.Statement.%New(selectMode): Establecer modo al crear: 0=Logical, 1=ODBC, 2=Display
- Funciones de conversión: `%EXTERNAL()` (Display), `%ODBCOUT()` (ODBC), `%INTERNAL()` (Logical) para conversión en línea
Notas Detalladas
Descripción general
InterSystems IRIS almacena datos internamente en formato logical para eficiencia y los procesa en uno de tres modos de selección dependiendo del consumidor de los datos. Entender estos modos es crítico porque devolver datos en el modo incorrecto puede causar errores de visualización, mala interpretación de datos o fallos de aplicación.
Los tres modos
Modo Logical (Modo 0): Datos como se almacenan internamente. Las fechas son enteros $HOROLOG, los booleanos son 0/1, y los tipos de datos personalizados usan su representación interna. Este es el modo por defecto para SQL Embebido.
Modo ODBC (Modo 1): Datos convertidos a formato de intercambio estándar. Las fechas se convierten a YYYY-MM-DD, las horas a HH:MM:SS, y los valores %List a cadenas separadas por comas. Este es el modo por defecto para conexiones ODBC/JDBC. El SQL Dinámico (%SQL.Statement.%New()) usa por defecto el modo Runtime, que resuelve a Logical (modo 0) en contexto de ObjectScript.
Modo Display (Modo 2): Datos convertidos a formato legible por humanos, específico del locale. Las fechas podrían convertirse a 03/05/2026 dependiendo de la configuración del locale.
// SQL Embebido usa por defecto el modo Logical
&sql(SELECT DOB INTO :dob FROM MyApp_Data.Patient WHERE ID = 1)
WRITE dob // 66841 (entero $HOROLOG)
// SQL Dinámico usa por defecto el modo ODBC (modo 1)
SET stmt = ##class(%SQL.Statement).%New()
SET sc = stmt.%Prepare("SELECT DOB FROM MyApp_Data.Patient WHERE ID = 1")
SET rs = stmt.%Execute()
IF rs.%Next() {
WRITE rs.%Get("DOB") // 2026-03-05 (formato ODBC)
}
// Establecer explícitamente el modo a Logical (0)
SET stmt = ##class(%SQL.Statement).%New(0)
SET sc = stmt.%Prepare("SELECT DOB FROM MyApp_Data.Patient WHERE ID = 1")
SET rs = stmt.%Execute()
IF rs.%Next() {
WRITE rs.%Get("DOB") // 66841 (formato Logical)
}
// Establecer explícitamente el modo a Display (2)
SET stmt = ##class(%SQL.Statement).%New(2)
SET sc = stmt.%Prepare("SELECT DOB FROM MyApp_Data.Patient WHERE ID = 1")
SET rs = stmt.%Execute()
IF rs.%Next() {
WRITE rs.%Get("DOB") // 03/05/2026 (formato Display)
}
Funciones de conversión en línea
Puede convertir columnas individuales dentro de una consulta independientemente del modo de selección:
// En cualquier modo, forzar conversión específica para una columna
&sql(SELECT Name, %EXTERNAL(DOB), %ODBCOUT(DOB), DOB
INTO :name, :dobDisplay, :dobODBC, :dobLogical
FROM MyApp_Data.Patient WHERE ID = 1)
WRITE "Display: ", dobDisplay, ! // 03/05/2026
WRITE "ODBC: ", dobODBC, ! // 2026-03-05
WRITE "Logical: ", dobLogical, ! // 66841
| Función | Modo de salida | Descripción |
|---|---|---|
%EXTERNAL(col) | Display | Formato de visualización específico del locale |
%ODBCOUT(col) | ODBC | Formato de intercambio estándar |
%INTERNAL(col) | Logical | Formato de almacenamiento interno |
Errores comunes
- Olvidar que el SQL Embebido usa por defecto el modo Logical e intentar mostrar valores
$HOROLOGsin procesar a los usuarios - Olvidar que el SQL Dinámico usa por defecto el modo ODBC y pasar fechas en formato ODBC a funciones de ObjectScript que esperan
$HOROLOG - Usar el modo incorrecto al comparar valores de fecha entre resultados de SQL Embebido y Dinámico
Referencias de Documentación
6. Variables SQL: SQLCODE, %ROWCOUNT, %ROWID
Puntos Clave
- SQLCODE = 0: Ejecución exitosa con datos devueltos
- SQLCODE = 100: Sin más datos (fin del cursor, sin fila coincidente para SELECT INTO, INSERT/UPDATE/DELETE exitoso)
- SQLCODE < 0: Ocurrió un error; verificar `%msg` o `%Message` para detalles
- %ROWCOUNT: Número de filas afectadas por las últimas operaciones INSERT, UPDATE, DELETE o FETCH
- %ROWID: ID de fila de la última fila afectada por INSERT, UPDATE o DELETE
- Embebido vs Dinámico: El SQL embebido establece `SQLCODE` y `%ROWCOUNT` como variables locales; el SQL dinámico usa `rs.%SQLCODE` y `rs.%ROWCOUNT`
Notas Detalladas
Descripción general
Después de ejecutar cualquier sentencia SQL en InterSystems IRIS, se establecen varias variables de estado para indicar el resultado. Entender estas variables y sus valores es crítico para escribir código correcto que consuma SQL.
Valores de SQLCODE
// SQLCODE = 0: Éxito con datos
&sql(SELECT Name INTO :name FROM MyApp_Data.Patient WHERE ID = 1)
IF SQLCODE = 0 {
WRITE "Found: ", name, !
}
// SQLCODE = 100: No se encontraron datos (SELECT INTO)
&sql(SELECT Name INTO :name FROM MyApp_Data.Patient WHERE ID = 99999)
IF SQLCODE = 100 {
WRITE "Patient not found", !
}
// SQLCODE = 100 con cursores: No hay más filas para obtener
&sql(DECLARE C1 CURSOR FOR SELECT Name FROM MyApp_Data.Patient)
&sql(OPEN C1)
FOR {
&sql(FETCH C1 INTO :name)
QUIT:SQLCODE'=0 // SQLCODE=100 significa no hay más filas
WRITE name, !
}
&sql(CLOSE C1)
// SQLCODE < 0: Error
&sql(SELECT BadColumn INTO :x FROM MyApp_Data.Patient)
IF SQLCODE < 0 {
WRITE "Error ", SQLCODE, ": ", %msg, !
}
SQLCODE para sentencias DML
Para sentencias INSERT, UPDATE y DELETE:
- SQLCODE = 0: La sentencia se ejecutó exitosamente, se afectaron filas
- SQLCODE = 100: La sentencia se ejecutó exitosamente, pero no se afectaron filas (ej., UPDATE sin cláusula WHERE coincidente)
- SQLCODE < 0: Ocurrió un error
// %ROWCOUNT después de DML
&sql(UPDATE MyApp_Data.Patient SET Active = 1 WHERE DOB > 50000)
WRITE "Rows updated: ", %ROWCOUNT, !
WRITE "SQLCODE: ", SQLCODE, ! // 0 si se actualizaron filas, 100 si no hubo coincidencias
// %ROWID después de INSERT
&sql(INSERT INTO MyApp_Data.Patient (Name, Active) VALUES ('New Patient', 1))
IF SQLCODE = 0 {
WRITE "New patient ID: ", %ROWID, !
WRITE "Rows inserted: ", %ROWCOUNT, ! // 1
}
Variables de estado en SQL dinámico
SET stmt = ##class(%SQL.Statement).%New()
SET sc = stmt.%Prepare("SELECT Name FROM MyApp_Data.Patient WHERE Active = ?")
SET rs = stmt.%Execute(1)
WHILE rs.%Next() {
WRITE rs.%Get("Name"), !
}
// Después de que la iteración completa
WRITE "SQLCODE: ", rs.%SQLCODE, ! // 100 (no hay más filas)
WRITE "Rows: ", rs.%ROWCOUNT, ! // número de filas obtenidas
// Para DML vía SQL dinámico
SET sc = stmt.%Prepare("UPDATE MyApp_Data.Patient SET Active = 0 WHERE ID = ?")
SET rs = stmt.%Execute(12345)
WRITE "SQLCODE: ", rs.%SQLCODE, !
WRITE "Rows affected: ", rs.%ROWCOUNT, !
Tabla resumen
| Variable | Contexto | Valores | Propósito |
|---|---|---|---|
SQLCODE | SQL embebido | 0=éxito, 100=sin datos, <0=error | Estado de ejecución |
rs.%SQLCODE | SQL dinámico | Igual que arriba | Estado de ejecución |
%ROWCOUNT | SQL embebido | Entero >= 0 | Filas afectadas/obtenidas |
rs.%ROWCOUNT | SQL dinámico | Entero >= 0 | Filas afectadas/obtenidas |
%ROWID | SQL embebido | Valor de ID de fila | ID de última fila afectada |
%msg | SQL embebido | Texto de error | Descripción del error |
rs.%Message | SQL dinámico | Texto de error | Descripción del error |
Referencias de Documentación
Resumen de Preparación para el Examen
Conceptos críticos a dominar:
- Reglas de coerción de tipado débil: Saber exactamente a qué evalúa `"7abc" + 3` (10) y por qué
- Macros esenciales: `$$$OK`, `$$$ISERR`, `$$$ThrowOnError`, `$$$ERROR` - conocer el propósito y sintaxis de cada una
- Mapeo objeto-SQL: Paquete->Esquema, Clase->Tabla, Propiedad->Columna, `%Persistent`->tabla SQL
- SQL embebido vs dinámico: Tiempo de compilación vs tiempo de ejecución, `:var` vs `?`, compensaciones de rendimiento vs flexibilidad
- Modos de selección: Logical (0), ODBC (1), Display (2); conocer los valores por defecto para Embebido (Logical) y Dinámico (ODBC)
- Valores de SQLCODE: 0=éxito, 100=sin datos, negativo=error - esto aparece en casi todos los exámenes
- %ROWCOUNT y %ROWID: Saber cuándo se establece cada uno y qué contiene
Escenarios comunes de examen:
- Predecir la salida de operaciones aritméticas que involucran coerción de cadena a número
- Elegir entre SQL Embebido y Dinámico para un caso de uso dado
- Identificar el modo de selección correcto para un consumidor particular (UI, cliente ODBC, lógica interna)
- Interpretar valores de SQLCODE después de operaciones SELECT, INSERT, UPDATE, DELETE
- Usar macros correctamente para patrones de manejo de errores %Status
- Mapear definiciones de clase/propiedad a sus proyecciones de tabla/columna SQL
Recomendaciones de práctica:
- Experimentar con coerción de tipos: probar varias combinaciones de cadena+número en la Terminal
- Escribir clases que extiendan %Persistent y verificar la proyección SQL en la pestaña SQL del Portal de Administración
- Ejecutar la misma consulta usando tanto SQL Embebido como Dinámico y comparar resultados
- Probar los tres modos de selección con propiedades de fecha y booleanas para ver diferencias de formato
- Practicar patrones de verificación de SQLCODE tanto para consultas de una sola fila como basadas en cursor
- Crear y usar macros personalizadas en archivos de inclusión
- Escribir código usando patrones `$$$ThrowOnError` e `$$$ISERR` con retornos %Status