T3.3: Handles Nulls

Knowledge Review - InterSystems ObjectScript Specialist

1. Comprende $CHAR(0) vs cadena vacía vs SQL NULL

Puntos Clave

  • Cadena vacía `""`: El valor por defecto "indefinido" en ObjectScript; `$LENGTH = 0`; es la representación en ObjectScript de SQL NULL
  • `$CHAR(0)` (o `$C(0)`): Cadena de un solo carácter con ASCII 0; `$LENGTH = 1`; es la representación en ObjectScript de la cadena de longitud cero SQL `''`
  • SQL NULL: Significa "desconocido/ausente"; distinto de la cadena de longitud cero SQL `''`
  • IS NULL vs = '': `WHERE col IS NULL` coincide con valores almacenados como `""` en ObjectScript; `WHERE col = ''` coincide con valores almacenados como `$CHAR(0)`
  • Puente objeto-SQL: Cuando una propiedad no tiene valor (por defecto `""` en ObjectScript), se proyecta como SQL NULL, no como cadena vacía
  • Cadena vacía SQL explícita: Para almacenar la cadena SQL `''` (longitud cero, no-NULL) desde ObjectScript, establecer la propiedad a `$CHAR(0)`

Notas Detalladas

Descripción general

Uno de los temas más frecuentemente evaluados y malentendidos en InterSystems IRIS es la relación entre la cadena vacía de ObjectScript, $CHAR(0) y SQL NULL. Esta distinción de tres vías es única de InterSystems IRIS debido a su modelo de datos unificado objeto-SQL, y equivocarse en la dirección lleva a errores sutiles.

Las dos reglas a recordar (según la referencia SQL oficial de InterSystems):

1. ObjectScript "" ↔ SQL NULL — "Use NULL para representar la ausencia de un valor de datos, lo cual corresponde a la cadena vacía de ObjectScript ("")." 2. ObjectScript $CHAR(0) ↔ SQL '' — "La cadena de longitud cero de SQL (cadena vacía) se especifica con dos comillas simples. En ObjectScript, esto corresponde a una cadena de longitud uno que contiene el carácter $CHAR(0)."

La cadena vacía en ObjectScript es SQL NULL

En ObjectScript, la cadena vacía "" es el valor inicial por defecto de cualquier variable o propiedad no establecida. Es una cadena de longitud cero. Cuando se accede a una propiedad nunca establecida, se obtiene "":

SET obj = ##class(MyApp.Patient).%New()
WRITE obj.MiddleName            // "" (cadena vacía - valor por defecto)
WRITE $LENGTH(obj.MiddleName)   // 0
WRITE (obj.MiddleName = "")     // 1 (verdadero)

En SQL, cuando este objeto se guarda, la columna MiddleName es NULL, no una cadena de longitud cero:

-- Después de guardar el objeto anterior:
SELECT ID, MiddleName FROM MyApp.Patient WHERE ID = 1
-- Devuelve: MiddleName = NULL

SELECT ID FROM MyApp.Patient WHERE MiddleName IS NULL
-- SÍ devuelve esta fila

SELECT ID FROM MyApp.Patient WHERE MiddleName = ''
-- NO devuelve esta fila (= '' prueba la cadena de longitud cero, no NULL)

$CHAR(0) es la cadena de longitud cero SQL

$CHAR(0) (abreviado $C(0)) es un solo carácter con código ASCII 0. Es un valor distinto, no-NULL, de longitud 1. InterSystems IRIS usa $CHAR(0) como la representación en ObjectScript de la cadena de longitud cero SQL ''. Cuando una propiedad se establece a $C(0), se proyecta como '' (cadena vacía, no-NULL) en SQL — nunca como NULL:

// Almacenar una cadena de longitud cero SQL vía ObjectScript
SET obj.MiddleName = $CHAR(0)
SET sc = obj.%Save()

// Ahora en SQL:
// SELECT MiddleName FROM MyApp.Patient WHERE ID = 1
// Devuelve: '' (cadena de longitud cero), NO NULL

// SELECT ID FROM MyApp.Patient WHERE MiddleName IS NULL
// NO devuelve esta fila

// SELECT ID FROM MyApp.Patient WHERE MiddleName = ''
// SÍ devuelve esta fila

Verificación en ObjectScript

SET obj = ##class(MyApp.Patient).%OpenId(1)

// Verificar si el valor es SQL NULL
IF obj.MiddleName = "" {
    WRITE "El valor es SQL NULL (cadena vacía en ObjectScript)", !
}

// Verificar si el valor es la cadena de longitud cero SQL
IF obj.MiddleName = $C(0) {
    // Solo verdadero si la columna almacena '' (longitud cero, no-NULL)
    // FALSO para la cadena vacía "" — son cadenas diferentes
    WRITE "El valor es la cadena de longitud cero SQL ''", !
}

// Distinguir los tres casos:
IF obj.MiddleName = "" {
    WRITE "SQL NULL", !
} ELSEIF obj.MiddleName = $C(0) {
    WRITE "SQL '' (longitud cero, no-NULL)", !
} ELSE {
    WRITE "Valor real: ", obj.MiddleName, !
}

Comportamiento de SQL NULL en consultas

SQL NULL sigue la lógica estándar de tres valores de SQL (TRUE, FALSE, UNKNOWN):

-- Comparaciones con NULL
SELECT * FROM MyApp.Patient WHERE MiddleName = NULL   -- ¡INCORRECTO! Siempre devuelve cero filas
SELECT * FROM MyApp.Patient WHERE MiddleName IS NULL  -- Forma correcta de verificar NULL

-- NULL en expresiones
SELECT Name, MiddleName || ' ' || LastName FROM MyApp.Patient
-- Si MiddleName es NULL, el resultado de la concatenación es NULL (NULL se propaga)

-- COALESCE / IFNULL para manejar NULLs
SELECT Name, COALESCE(MiddleName, 'N/A') AS MiddleName FROM MyApp.Patient
-- Devuelve 'N/A' si MiddleName es NULL

-- NULL en agregados
SELECT COUNT(MiddleName) FROM MyApp.Patient
-- COUNT(column) excluye valores NULL
-- COUNT(*) cuenta todas las filas incluyendo aquellas con NULLs

Resumen comparativo

ConceptoValor en ObjectScript$LENGTH()Proyección SQLPrueba SQL
SQL NULL"" (también el valor por defecto)0NULLIS NULL
Cadena de longitud cero SQL$C(0)1''= ''
Espacio" "1' '= ' '
Propiedad no establecida"" (por defecto)0NULLIS NULL

Insertar vía SQL

-- Inserción explícita de NULL
INSERT INTO MyApp.Patient (Name, MiddleName) VALUES ('Smith', NULL)
-- MiddleName será "" (cadena vacía) cuando se acceda vía ObjectScript

-- Cadena de longitud cero explícita
INSERT INTO MyApp.Patient (Name, MiddleName) VALUES ('Smith', '')
-- MiddleName será $C(0) (longitud 1) cuando se acceda vía ObjectScript

-- Omitir una columna inserta el valor por defecto de ObjectScript (""), que es SQL NULL
INSERT INTO MyApp.Patient (Name) VALUES ('Jones')
-- MiddleName será NULL en SQL

Error común: diferencias en cláusula WHERE

// Esta consulta encuentra filas cuyo valor es la cadena de longitud cero '':
&sql(SELECT COUNT(*) INTO :cnt FROM MyApp.Patient WHERE MiddleName = '')
WRITE "'' longitud cero: ", cnt, !

// Esta consulta encuentra filas cuyo valor es SQL NULL (ObjectScript ""):
&sql(SELECT COUNT(*) INTO :cnt FROM MyApp.Patient WHERE MiddleName IS NULL)
WRITE "NULLs: ", cnt, !

// Son conjuntos de resultados DIFERENTES.
// Un error común es usar = '' cuando se quería IS NULL, o viceversa.

Referencias de Documentación

Resumen de Preparación para el Examen

Conceptos críticos a dominar:

  1. `""` = SQL NULL: Este es el hecho más importante. La cadena vacía de ObjectScript es cómo IRIS representa SQL NULL, y es el valor por defecto de las propiedades no establecidas.
  2. `$CHAR(0)` = SQL `''`: La cadena NUL de un carácter en ObjectScript es cómo IRIS representa la cadena SQL de longitud cero (no-NULL). Es un valor no-NULL válido.
  3. IS NULL vs = '': `IS NULL` coincide con `""` en ObjectScript; `= ''` coincide con `$CHAR(0)`. Prueban valores almacenados completamente distintos.
  4. Propagación de NULL: NULL en cualquier expresión aritmética o de cadena se propaga (el resultado es NULL).
  5. Comportamiento de COUNT: `COUNT(*)` cuenta todas las filas; `COUNT(column)` excluye NULLs.
  6. Lógica de tres valores: Las comparaciones que involucran NULL devuelven UNKNOWN, no TRUE ni FALSE.

Escenarios comunes de examen:

  • Dada una asignación de propiedad en ObjectScript, predecir si SQL ve NULL o la cadena de longitud cero `''`
  • Escribir cláusulas WHERE correctas para encontrar valores NULL (`IS NULL`) vs longitud cero (`= ''`)
  • Identificar errores donde se usa `= ''` en lugar de `IS NULL` (o viceversa)
  • Entender qué sucede cuando se hace INSERT sin especificar columna (por defecto `""` en ObjectScript → SQL NULL)
  • Predecir resultados de COUNT cuando los datos contienen una mezcla de NULLs y cadenas de longitud cero
  • Establecer una propiedad a `$C(0)` para crear una cadena SQL de longitud cero, o dejarla en `""` para crear SQL NULL

Recomendaciones de práctica:

  • Crear una clase de prueba con propiedades de cadena; guardar objetos con `""`, `$C(0)` y valores reales
  • Consultar la tabla con tanto `IS NULL` como `= ''` para ver qué fila coincide con cuál
  • Usar `SELECT $LENGTH(col), $ASCII(col) FROM ...` para inspeccionar valores almacenados
  • Practicar COALESCE e IFNULL para manejar NULLs en consultas
  • Experimentar con propagación de NULL en expresiones: `NULL + 5`, `NULL || 'text'`
  • Probar COUNT(*) vs COUNT(column) con datos que contienen NULLs
  • Verificar el mapeo empíricamente: `obj.Email = ""` → la fila coincide con `IS NULL`; `obj.Email = $C(0)` → la fila coincide con `= ''`

Report an Issue