SAS9

El duelo IF vs WHERE y la trampa de la función SUBSTR

Simon 7 vistas

A veces sucede que un programa SAS© falla con una instrucción IF, pero funciona perfectamente si reemplazamos ese IF por un WHERE. ¿Es magia? No, es una diferencia arquitectónica.

Un usuario del foro se encontró con un error críptico: "INVALID THIRD ARGUMENT TO FUNCTION SUBSTR". Analicemos este caso de estudio para comprender la mecánica interna de SAS©.

1. El Código Culpable

El usuario quiere dividir sus datos en dos tablas según la provincia. Utiliza una lógica compleja que involucra SUBSTR, TRIM y LEFT.

1DATA bundrpt regulert;
2 SET combine;
3 /* La condition qui plante */
4 IF (substr(trim(left(brprov)),1,7) = 'ALBERTA') OR
5 (substr(trim(left(brprov)),1,12) = 'SASKATCHEWAN' and put(cidkey,sask.) = 'Y')
6 THEN OUTPUT bundrpt;
7 ELSE OUTPUT regulert;
8RUN;

El error:

NOTE: INVALID THIRD ARGUMENT TO FUNCTION SUBSTR AT LINE 302 BRPROV=ALBERTA

¿Por qué SAS© se queja del 3er argumento de SUBSTR (la longitud solicitada) cuando la variable BRPROV tiene 18 caracteres de largo?

2. El Diagnóstico: La trampa de TRIM

El error no proviene de la variable original, sino de lo que el código hace con ella antes de llamar a SUBSTR.

Veamos la secuencia de funciones para el valor "ALBERTA":

  1. LEFT(BRPROV): Alinea el texto a la izquierda.

  2. TRIM(...): Elimina los espacios a la derecha.

    • Resultado temporal: La cadena se convierte en "ALBERTA" (longitud = 7).

  3. SUBSTR(..., 1, 12): El código intenta extraer 12 caracteres para verificar si es "SASKATCHEWAN".

El Fallo: Le pide a SAS© que extraiga 12 caracteres de una cadena que ahora solo tiene 7 (debido a TRIM). Esto es matemáticamente imposible, por lo que el Data Step se detiene.

3. La Pregunta Clave: ¿Por qué WHERE funciona?

El usuario señala: "Si pongo WHERE en lugar de IF, no recibo ningún mensaje de error."

Aquí radica la diferencia fundamental:

IF (Data Step)

  • Momento: Se ejecuta durante el procesamiento de cada fila, en el PDV (Program Data Vector).

  • Comportamiento: Manipula los datos después de la transformación. Aquí, ve el resultado de TRIM (la cadena acortada) y falla. Además, según las reglas de evaluación lógica, SAS© puede intentar evaluar la segunda parte del OR incluso si la primera parece suficiente, o simplemente falla durante la compilación de los argumentos.

WHERE (Pre-Filtro)

  • Momento: Se ejecuta antes de que los datos ingresen al Data Step.

  • Comportamiento: Actúa como un filtro SQL en la fuente de datos.

  • ¿Por qué pasa? La instrucción WHERE a menudo es más "inteligente" o más "restrictiva" en las funciones que autoriza. A menudo, ignorará funciones cosméticas como TRIM para basarse en los metadatos de la columna de origen (que tiene 18 caracteres), u optimizará la consulta de manera diferente.

4. Las Soluciones (Mejores Prácticas)

En lugar de confiar en la tolerancia de WHERE, debemos corregir la lógica del código.

Solución A: Eliminar el TRIM (El enfoque simple)

Como sugiere un usuario, el TRIM es innecesario aquí. SUBSTR funciona muy bien en una cadena con espacios, siempre que la variable original sea lo suficientemente larga.

1/* Pas de TRIM, donc on travaille sur les 18 caractères d'origine */
2IF substr(brprov, 1, 7) = 'ALBERTA' ...

Solución B: El operador "Starts With" =: (El enfoque Pro)

SAS© tiene un operador específico para decir "Empieza por", lo que evita tener que contar los caracteres y usar SUBSTR.

Es el operador =: (igual seguido de dos puntos).

Código optimizado:

1DATA bundrpt regulert;
2 SET combine;
3 /* Plus de SUBSTR, plus de TRIM, plus d'erreurs de longueur ! */
4 IF brprov =: 'ALBERTA' OR
5 (brprov =: 'SASKATCHEWAN' and put(cidkey,sask.) = 'Y')
6 THEN OUTPUT bundrpt;
7 ELSE OUTPUT regulert;
8RUN;
InstrucciónRolManejo de errores
IFLógica de programación. Se ejecuta línea por línea sobre los valores en memoria.Estricto. Si crea una cadena corta (TRIM) y solicita una lectura larga (SUBSTR), falla.
WHEREFiltro de entrada (E/S). Se ejecuta en el archivo fuente.Más robusto ya que a menudo trabaja con metadatos brutos o a través del motor SQL.

El consejo del experto: Si necesita verificar el inicio de una cadena de caracteres, olvídese de SUBSTR. Utilice siempre el operador =:. Es más legible, más rápido y menos propenso a errores de longitud.