Síguenos

Tecnología

Cómo saber cuándo es par e impar en PL/SQL usando MOD sin errores

PL/SQL permite detectar pares e impares con MOD, pero NULL, negativos y decimales pueden romper el código si no se miran antes de clasificar.

Publicado

el

PLSQL usando MOD

En Oracle, un número es par cuando al dividirlo entre 2 el resto es 0. Es impar cuando ese resto no es 0. En PL/SQL, la forma limpia de comprobarlo es con MOD(numero, 2): si devuelve 0, el valor es par; si devuelve cualquier otro resultado, es impar. Parece una tontería de clase de primero, casi una migaja de programación, pero ahí se esconden varios tropiezos habituales: los valores NULL, los números negativos, los decimales y esa costumbre tan humana de copiar una línea de código sin preguntarse qué demonios hace.

La comprobación básica cabe en una servilleta: MOD(8, 2) devuelve 0, así que 8 es par; MOD(7, 2) devuelve 1, así que 7 es impar. Hasta ahí, paz en la base de datos. El matiz llega cuando el dato no es un entero perfecto o cuando aparece un número negativo. En Oracle, por ejemplo, no conviene comprobar los impares solo con MOD(numero, 2) = 1, porque un negativo impar puede devolver -1. Mejor decirlo sin adornos: para detectar pares usa MOD(numero, 2) = 0; para detectar impares usa MOD(numero, 2) <> 0, siempre que ya hayas tratado antes los NULL.

La regla sencilla: el resto manda

PL/SQL no necesita una ceremonia especial para decidir si un número es par o impar. La función MOD hace el trabajo ingrato: calcula el resto de una división. Ese resto es la huella que deja un número cuando pasa por el filtro del 2. Si no sobra nada, el número encaja en parejas. Si sobra algo, hay una unidad bailando sola. Así de simple.

Un bloque anónimo básico puede escribirse así:

SET SERVEROUTPUT ON;

DECLARE
  v_numero NUMBER := 17;
BEGIN
  IF MOD(v_numero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es par');
  ELSE
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es impar');
  END IF;
END;
/

El resultado será claro: 17 es impar. Si cambiamos v_numero por 18, el mensaje dirá que es par. No hay magia negra. Hay división entera, resto y una condición. Para quien venía buscando como saber cuando es par e impar en pl/sql, la respuesta útil está justamente ahí: MOD(n, 2) = 0 para par; MOD(n, 2) <> 0 para impar.

Ahora bien, el ejemplo anterior tiene una debilidad. Funciona bien cuando el valor existe, es numérico y se comporta como un entero. Pero las bases de datos reales no son ejercicios de pizarra. Las bases de datos reales tienen ausencias, valores heredados, cargas mal hechas, formularios generosos con el desastre y usuarios que escriben lo que pueden. Ahí aparece el primer invitado incómodo: NULL.

El caso NULL: cuando no hay número, no hay paridad

En Oracle, NULL no significa cero. Tampoco significa cadena vacía con nostalgia. Significa ausencia de valor, dato desconocido, hueco. Y un hueco no puede ser par ni impar, igual que una silla vacía no puede llegar tarde. Por eso, antes de aplicar MOD, conviene decidir qué se hará cuando el número no existe.

Un bloque más serio quedaría así:

SET SERVEROUTPUT ON;

DECLARE
  v_numero NUMBER := NULL;
BEGIN
  IF v_numero IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('No hay número para evaluar');
  ELSIF MOD(v_numero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es par');
  ELSE
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es impar');
  END IF;
END;
/

Este pequeño cambio evita una confusión muy común. Si no se trata NULL, el programa puede no entrar donde se espera o devolver una salida poco útil. En PL/SQL, una condición que depende de un valor desconocido no se comporta como una afirmación falsa normal y corriente; se mueve en ese terreno gris donde la lógica de base de datos tiene más de notaría que de intuición doméstica.

La decisión editorial del código, por decirlo así, debe tomarse antes: un valor nulo puede mostrarse como “sin dato”, puede rechazarse, puede convertirse a cero mediante NVL, o puede disparar una validación. Pero no debería colarse silenciosamente en la misma categoría que los números reales. No hay paridad sin número.

Este enfoque también ayuda a escribir procedimientos más mantenibles. Quien lea el bloque dentro de seis meses entenderá que el nulo no fue olvidado, sino tratado. Eso, en programación corporativa, ya es casi una cortesía cívica.

El error típico: usar igual a 1 para los impares

En muchos ejemplos rápidos se ve esta estructura:

IF MOD(v_numero, 2) = 1 THEN
  DBMS_OUTPUT.PUT_LINE('Impar');
END IF;

Con números positivos funciona. Con 7, perfecto. Con 13, también. Pero esa línea tiene una trampa: no cubre bien todos los casos si entran números negativos. En Oracle, MOD(-7, 2) puede devolver -1, no 1. El número sigue siendo impar, por supuesto, pero la condición = 1 no lo reconoce. Y ahí el programa se queda mirando al infinito, como si el -7 fuera un fenómeno paranormal.

La forma más robusta es esta:

DECLARE
  v_numero NUMBER := -7;
BEGIN
  IF v_numero IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('Sin dato');
  ELSIF MOD(v_numero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es par');
  ELSE
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es impar');
  END IF;
END;
/

Aquí no importa si el impar es positivo o negativo. Si el resto no es cero, cae en el ELSE. Se acabó la discusión. Otra opción sería usar ABS, que devuelve el valor absoluto, y comprobar MOD(ABS(v_numero), 2) = 1. También funciona, pero no siempre hace falta. La condición más sobria suele ser la mejor: primero descartas NULL, luego preguntas si el resto es cero, y todo lo demás es impar.

La diferencia parece pequeña, casi maniática. Pero en bases de datos, las pequeñas diferencias son esas grietas por donde luego entra el agua. Un informe que clasifica mal los identificadores negativos, una rutina de reparto que alterna mal filas, una validación que deja pasar lo que no debía. La informática empresarial está llena de incendios que empezaron como una línea más o menos correcta.

En una consulta SQL también funciona

Aunque el tema se piense desde PL/SQL, muchas veces la comprobación se hace directamente en una consulta SQL. No siempre hace falta abrir un bloque DECLARE, declarar variables y sacar mensajes por pantalla. Si los números ya están en una tabla, lo natural es clasificarlos con CASE.

Imaginemos una tabla llamada numeros_prueba con una columna numero. La consulta podría ser:

SELECT
  numero,
  CASE
    WHEN numero IS NULL THEN 'SIN DATO'
    WHEN MOD(numero, 2) = 0 THEN 'PAR'
    ELSE 'IMPAR'
  END AS tipo_numero
FROM numeros_prueba;

Este patrón es especialmente útil en informes, vistas, exportaciones o validaciones rápidas. La consulta no modifica nada; solo etiqueta. Y lo hace con una claridad agradecida. A los pares les pone PAR, a los impares IMPAR, y a los nulos les evita el falso bautizo.

También puede filtrarse directamente:

SELECT numero
FROM numeros_prueba
WHERE MOD(numero, 2) = 0;

Eso devuelve solo los pares. Para los impares:

SELECT numero
FROM numeros_prueba
WHERE numero IS NOT NULL
  AND MOD(numero, 2) <> 0;

La condición numero IS NOT NULL no está ahí por decoración. Está para que el resultado sea explícito y legible. El programador que llegue después no tendrá que adivinar si los nulos fueron excluidos por accidente o por criterio. El código claro no grita; simplemente no esconde cosas.

En tablas grandes, conviene recordar otro detalle: aplicar funciones sobre columnas en filtros puede afectar al uso de índices tradicionales. Si esta comprobación forma parte de una consulta crítica y repetida, quizá interese revisar el plan de ejecución, usar columnas calculadas, índices basados en función o rediseñar el criterio. Pero para validaciones normales, pruebas, informes pequeños o lógica de negocio puntual, MOD es la herramienta natural.

Decimales: antes de preguntar, decide qué estás midiendo

La paridad pertenece a los enteros. Dos, cuatro, seis, ocho: pares. Uno, tres, cinco, siete: impares. ¿Pero qué hacemos con 7,5? Aquí no hay que ponerse lírico. Un decimal no es par ni impar en el sentido clásico de la aritmética elemental. Si el dato puede traer decimales, el programa debe decidir antes si los acepta, los rechaza o los transforma.

Una versión prudente puede validar que el número sea entero antes de clasificarlo:

DECLARE
  v_numero NUMBER := 7.5;
BEGIN
  IF v_numero IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('Sin dato');
  ELSIF v_numero <> TRUNC(v_numero) THEN
    DBMS_OUTPUT.PUT_LINE('El valor no es un entero');
  ELSIF MOD(v_numero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es par');
  ELSE
    DBMS_OUTPUT.PUT_LINE(v_numero || ' es impar');
  END IF;
END;
/

TRUNC corta la parte decimal. Si v_numero no coincide con TRUNC(v_numero), entonces no estamos ante un entero. Esta validación evita que un dato como 8,2 sea tratado como si pudiera entrar alegremente en el reparto de pares e impares. A veces se quiere redondear; otras, truncar; otras, rechazar. Son decisiones distintas. No las mezclemos, que luego vienen los disgustos.

Si el negocio exige redondear antes de clasificar, el código debe decirlo con todas las letras:

DECLARE
  v_numero NUMBER := 7.6;
  v_entero NUMBER;
BEGIN
  v_entero := ROUND(v_numero);

  IF MOD(v_entero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE(v_entero || ' es par tras redondear');
  ELSE
    DBMS_OUTPUT.PUT_LINE(v_entero || ' es impar tras redondear');
  END IF;
END;
/

Ese “tras redondear” no es un capricho. Es una advertencia honrada. El valor original no era 8; el programa lo convirtió en 8. En sistemas contables, sanitarios, logísticos o administrativos, esa diferencia puede tener peso. Clasificar bien empieza por nombrar bien lo que se está clasificando.

Una función reutilizable para no repetir la misma rueda

Cuando la comprobación aparece en muchos puntos del sistema, repetir el mismo IF una y otra vez acaba siendo una forma elegante de sembrar errores. Un sitio trata NULL, otro no. Uno usa = 1 para impares. Otro redondea sin avisar. Otro imprime mensajes en vez de devolver valores. Y al final nadie sabe cuál es la versión buena.

Una función reutilizable puede ordenar el jardín:

CREATE OR REPLACE FUNCTION obtener_paridad (
  p_numero IN NUMBER
) RETURN VARCHAR2
IS
BEGIN
  IF p_numero IS NULL THEN
    RETURN 'SIN DATO';
  ELSIF p_numero <> TRUNC(p_numero) THEN
    RETURN 'NO ENTERO';
  ELSIF MOD(p_numero, 2) = 0 THEN
    RETURN 'PAR';
  ELSE
    RETURN 'IMPAR';
  END IF;
END;
/

Después se puede usar desde PL/SQL:

BEGIN
  DBMS_OUTPUT.PUT_LINE(obtener_paridad(24));
  DBMS_OUTPUT.PUT_LINE(obtener_paridad(25));
  DBMS_OUTPUT.PUT_LINE(obtener_paridad(NULL));
  DBMS_OUTPUT.PUT_LINE(obtener_paridad(7.5));
END;
/

Y también desde una consulta SQL:

SELECT numero, obtener_paridad(numero) AS paridad
FROM numeros_prueba;

Este tipo de función tiene una ventaja evidente: concentra la norma. Si mañana el criterio cambia y los decimales deben redondearse, se toca una pieza, no quince. Eso sí, no todo debe convertirse en función por deporte. Si solo vas a clasificar una columna una vez en una consulta sencilla, CASE basta. Si esa lógica forma parte del corazón de una aplicación, una función bien escrita puede ahorrar tiempo, errores y algún correo pasivo-agresivo a las seis de la tarde.

También hay que tener cuidado con el rendimiento. Invocar funciones fila a fila en consultas grandes puede salir caro si se hace sin pensar. La elegancia del código no paga las facturas del servidor. En operaciones masivas, conviene medir. Siempre. La base de datos tiene memoria, pero no paciencia infinita.

Paridad para alternar filas, repartir cargas o pintar resultados

La comprobación de pares e impares no solo sirve para decir si un número concreto entra en una categoría escolar. En bases de datos se usa para alternar filas, repartir registros, separar cargas por lotes o crear patrones de presentación. Un ejemplo típico aparece con ROWNUM o con funciones analíticas como ROW_NUMBER, cuando se quiere dividir un conjunto en dos grupos.

Con ROW_NUMBER, la idea queda bastante limpia:

SELECT
  empleado_id,
  nombre,
  CASE
    WHEN MOD(ROW_NUMBER() OVER (ORDER BY empleado_id), 2) = 0 THEN 'FILA PAR'
    ELSE 'FILA IMPAR'
  END AS posicion
FROM empleados;

Aquí no estamos diciendo que el identificador del empleado sea par o impar. Estamos clasificando la posición que ocupa cada fila después de ordenar. La diferencia importa. Mucho. Un empleado_id puede ser impar, pero aparecer en una fila par si antes hay otro registro. El dato y la posición no son la misma cosa, aunque a veces se disfracen con el mismo número.

Este patrón puede usarse para repartir trabajo entre dos procesos, revisar muestras alternas o preparar salidas donde interese distinguir registros de manera mecánica. No es glamour tecnológico; es fontanería útil. Y buena parte del software serio vive de esa fontanería que nadie aplaude cuando funciona, pero todo el mundo maldice cuando falla.

Cómo escribirlo sin convertir el código en barro

Un buen bloque PL/SQL para pares e impares debería cumplir tres cosas: tratar NULL, no fallar con negativos y dejar claro qué ocurre con decimales. Lo demás es decoración. La versión más equilibrada, para un entero esperado, podría ser esta:

DECLARE
  v_numero NUMBER := -12;
BEGIN
  IF v_numero IS NULL THEN
    DBMS_OUTPUT.PUT_LINE('Sin dato');
  ELSIF v_numero <> TRUNC(v_numero) THEN
    DBMS_OUTPUT.PUT_LINE('No es un entero');
  ELSIF MOD(v_numero, 2) = 0 THEN
    DBMS_OUTPUT.PUT_LINE('Par');
  ELSE
    DBMS_OUTPUT.PUT_LINE('Impar');
  END IF;
END;
/

No impresiona en una conferencia. No lleva inteligencia artificial, ni arquitectura hexagonal, ni un nombre absurdo acabado en “Ops”. Pero hace lo que tiene que hacer. Lee un número, comprueba sus bordes y responde. Eso, en programación, ya es bastante.

Si se quiere devolver el resultado dentro de un procedimiento o guardarlo en una variable, se puede sustituir DBMS_OUTPUT.PUT_LINE por una asignación:

DECLARE
  v_numero    NUMBER := 31;
  v_resultado VARCHAR2(20);
BEGIN
  IF v_numero IS NULL THEN
    v_resultado := 'SIN DATO';
  ELSIF v_numero <> TRUNC(v_numero) THEN
    v_resultado := 'NO ENTERO';
  ELSIF MOD(v_numero, 2) = 0 THEN
    v_resultado := 'PAR';
  ELSE
    v_resultado := 'IMPAR';
  END IF;

  DBMS_OUTPUT.PUT_LINE(v_resultado);
END;
/

Esta forma se parece más a la vida real. Muchas veces no queremos imprimir nada, sino decidir algo: guardar una etiqueta, activar una rama del proceso, validar una entrada, rechazar un registro, repartir una carga. DBMS_OUTPUT está bien para probar y depurar, pero no debería ser el centro de una lógica de negocio. Es una linterna, no el edificio.

Otro detalle de higiene: usa nombres de variables que respiren. v_numero se entiende. n puede valer en un ejemplo corto, pero en código de producción conviene no jugar a los jeroglíficos. La claridad no es infantil; es barata. Y el mantenimiento, en cambio, cuesta.

La respuesta que aguanta mejor en producción

La forma más segura de resolver la paridad en PL/SQL es separar los casos: primero mirar si el valor es NULL, después confirmar si debe tratarse como entero, luego aplicar MOD(numero, 2) = 0 para los pares y dejar los impares como todo valor cuyo resto no sea cero. Esa secuencia evita los fallos habituales sin convertir una operación sencilla en una novela rusa.

La fórmula esencial no cambia: el resto de dividir entre 2 decide. Lo importante es no olvidar que Oracle no trabaja con intenciones, sino con valores. Un NULL no es cero. Un decimal no es un entero por simpatía. Un negativo impar no tiene por qué devolver 1 al aplicar MOD. Y un ejemplo copiado de internet puede funcionar justo hasta el día en que deja de hacerlo, normalmente delante de alguien con prisa.

En PL/SQL, lo elegante no es escribir mucho. Es escribir lo justo para que el programa no tenga que adivinar. Para números enteros, la comprobación queda reducida a una línea de criterio: MOD(numero, 2) = 0. Para todo lo demás, criterio, validación y nombres claros. La vieja aritmética, con traje de base de datos. Y todavía funciona.

Gracias por leerme y por pasarte por Don Porqué. Si te apetece seguir curioseando, arriba tienes la lupa para buscar más temas. Y si esto te ha gustado, compártelo: así la historia llegará un poco más lejos.

Lo más leído