Está en la página 1de 129

Se

Contenido
Sección 2 Fundamentos teóricos sobre RPG ILE.......................................................................1
Sección 3 SQL para IBMi-AS400 (Teoría)................................................................................69
Sección 4 SQL integrado en programas RPG ILE formato Free (Teoría)..................................86
Sección 5 Creación de pantallas en IBMi-AS400 (Teoría).....................................................109
Sección 6 Programación ILE en IBMi-AS400 (Teoría)............................................................112

Fundamentos teóricos sobre RPG ILE


Sección 2 Fundamentos teóricos sobre RPG ILE

Diseño estructurado

El diseño estructurado es una metodología de desarrollo ampliamente


aceptada que respalda el diseño de programas de calidad. Un aspecto
importante del diseño estructurado es limitar las estructuras de control dentro
de un programa a tres estructuras lógicas básicas:

· Secuencia indica a la computadora que ejecute operaciones en el orden en


que aparecen en el programa.

· Selección (también llamada decisión) le permite establecer rutas alternativas


de instrucciones dentro de un programa. La ruta opcional que ejecuta el
programa depende de los resultados de una prueba o condición dentro del
programa.

· Iteración (también llamada ciclo o bucle) permite que las instrucciones dentro


del programa se repitan hasta que se cumpla una condición o ya no se cumpla.

El flujo de control secuencial es inherente a RPG (y otros lenguajes de


programación) de forma predeterminada. El orden en el que se describen las
operaciones en las secciones de procedimientos en el programa determina el
orden en que la computadora las ejecuta. La computadora comienza con la
primera instrucción y luego continúa ejecutando las instrucciones del programa
en su orden de ocurrencia, a menos que encuentre una operación que transfiera
explícitamente el control a una ubicación diferente dentro del programa.

Para salirse de un flujo de control secuencial, el programa debe usar un código


de operación explícito que invoca la estructura de control deseada. Estos
códigos de operación invocan estructuras de selección (decisión):
 If, Else, Elseif (Else If)

 Select, When, Other (Otherwise)

Y estos códigos de operación invocan estructuras de iteración (repetición):

 Dow (Do While)


 Dou (Do Until)
 For

Cada una de estas estructuras de control tiene un único punto de entrada y un


único punto de salida. Juntas, las estructuras pueden servir como bloques de
construcción básicos para expresar la lógica compleja requerida para resolver
problemas de programación complicados mientras se mantiene un control
estricto sobre el flujo del programa que facilita el mantenimiento del programa.

Comparaciones relacionales

Las operaciones de decisión e iteración implican probar una condición para


determinar el curso de acción apropiado. Esta prueba implica una comparación
relacional entre dos valores. Para expresar la comparación, ILE RPG mantiene
seis símbolos relacionales que se utilizan con operaciones de decisión e
iteración:

· > (Mayor que)

· > = (Mayor o igual que)

· = (Igual a)

· <= (Menor o igual a)

· < (Menor que)

· <> (No es igual a, o diferente de)

La forma en que la computadora evalúa si una comparación es verdadera


depende del tipo de datos de los elementos que se comparan. Si está
comparando dos valores numéricos (ya sean variables, literales, constantes o
expresiones), el sistema los compara basándose en valores algebraicos,
alineados en el punto decimal. La longitud y el número de posiciones decimales
en los elementos que se comparan no afectan el resultado de la comparación.
También se pueden realizar pruebas relacionales entre valores de caracteres.
Este tipo de comparación se produce en una forma algo diferente a las
comparaciones numéricas. Cuando se comparan dos valores de caracteres, el
sistema realiza una comparación carácter por carácter, moviéndose de
izquierda a derecha, hasta que encuentra un par que no coincide o termina de
verificar.

Además de los valores numéricos y de caracteres, se pueden realizar pruebas


relacionales con fechas, horas y marcas de tiempo (timestamp). Si está
comparando dos valores relacionados con la fecha (ya sean variables, literales,
constantes o expresiones), el sistema los compara en función de su aparición
relativa en el calendario o el reloj (es decir, las fechas anteriores se consideran
inferiores a las posteriores). Las fechas u horas que se comparan no necesitan
estar en el mismo formato porque el sistema las compara con precisión,
independientemente del formato.

Declaración de archivos de pantalla

La instrucción Dcl-f (Declare file) describe cada archivo que usa el programa y
define como lo usa. El formato general de la instrucción Dcl-f es el siguiente:

Dcl-f file-name {device} {optional-keywords};

Cuando un programa declara un archivo todos los formatos de registro y los


campos de registro quedan disponibles para usarse dentro del programa. No se
necesitan más declaraciones para que el programa pueda utilizar estos
elementos. En el siguiente código el archivo PROD_MNT2 contiene los datos
que se requieren para su procesamiento.

Dcl-f PROD_MNT2 Workstn Indds(Indicators);

Nombre del archivo


La primera entrada que sigue a la instrucción Dcl-f nombra el archivo. El
nombre del archivo es la única entrada necesaria para la instrucción Dcl-f. Un
archivo descrito externamente debe existir (ya debe existir el objeto compilado)
en el momento de la compilación del programa.

Dispositivo

La entrada que sigue al nombre del archivo indica el dispositivo asociado con el
archivo. Los tres tipos de dispositivo más utilizados son las siguientes:

· Disco: Para archivos de bases de datos

· Impresora: Para reportes

· Workstn: Para pantallas interactivas

RPG asume que el archivo es un archivo descrito externamente. Sin embargo


se puede especificar

*EXT para un archivo descrito externamente:

Dcl-f Productos Printer (*Ext) Usage (*Output) Oflind (Endofpage);

Uso de archivos

La palabra clave Usage especifica cómo el programa utilizará el archivo. Por


ejemplo, un archivo de entrada contiene datos que serán leídos por el
programa, mientras que un archivo de salida es el destino para escribir
resultados de procesamiento del programa. Un programa puede utilizar un
archivo de muchas formas, por lo que varios valores pueden ser necesarios
para la misma palabra clave Usage. Las siguientes entradas Usage son válidas:
· Usage(*Input): El programa puede leer registros existentes de un archivo

· Usage(*Output) el programa puede escribir nuevos registros a un archivo

· Usage (*Input:*Output): el programa puede leer registros y agregar nuevos

· Usage(*Update): el programa puede cambiar (pero no eliminar) registros


existentes en un archivo.

· Usage(*Delete): el programa puede eliminar registros existentes de un archivo

· Usage (*Update:*Delete): el programa puede cambiar o eliminar registros


existentes.

· Usage (*Update:*Delete:*Output): el programa puede cambiar, eliminar o


agregar nuevos registros

Si no especifica una palabra clave Usage para el dispositivo. Cuando el


dispositivo es disco RGP asume Usage(*Input), y cuando el dispositivo es
Impresora, RPG asume Usage (*Output). Para el dispositivo Workstn, RPG tiene
como valor por defecto Usage (*Input:*Output). Las siguientes líneas son
equivalentes:

Dcl-f Pantalla Workstn Usage(*Input:*Output);

Dcl-f Pantalla Workstn;

Declaración de constantes nombradas

El concepto de constante nombrada está estrechamente asociado con el


concepto de una literal. Una literal es un medio de anotar un valor fijo (por
ejemplo, un número, una cadena de caracteres o una fecha). Por ejemplo, el
número 123 es una literal, al igual que la cadena de caracteres "setiembre". ILE
RPG permite asociar un nombre de datos con una literal, con lo que se podrá
hacer referencia al literal por su nombre en todo el programa. El elemento de
datos resultante es una constante identificada por un nombre (constante
nombrada).

Una vez que haya definido una constante con nombre, puede usarla con
cualquier procesamiento apropiado para su tipo. El valor de una constante
nombrada es fijo, no puede cambiarlo durante el curso de la ejecución del
programa. Las constantes con nombre facilitan la comprensión de los
programas al eliminar literales cuyo propósito no es evidente.

Las constantes identificadas por un nombre le permiten definir constantes en


un lugar cerca del comienzo del programa en lugar de codificarlas como
literales en la sección de cálculos. Esta práctica es un estándar de buena
programación porque facilita el mantenimiento de los programas. Si un
programador necesita cambiar un valor, como IMPUESTO por ejemplo, es
mucho más fácil y menos propenso a errores ubicar la constante nombrada y
cambiar su valor en ese lugar en lugar de tener que buscar en todo el programa
buscando y examinando cálculos en los que se use el valor literal 0.13.

Literales numéricos

Una literal numérica es un número y su valor permanece fijo durante todo el


programa (a diferencia de una variable, cuyo valor puede cambiar a lo largo del
programa). Una literal numérica puede contener un caracter base, un signo o
ambos. Si el literal numérico incluye un signo, el signo debe ser el caracter de
más a la izquierda del literal. Si el literal numérico no incluye un signo, se asume
que el literal representa un número positivo.

Aparte de un caracter de base y un signo, un literal numérico solo puede


contener los dígitos del 0 al 9. Nunca use espacios en blanco, símbolos de
moneda, signos de porcentaje o separadores de miles en literales numéricos, y
no los encierre en apóstrofos (‘). El valor numérico puede tener hasta 63 dígitos,
con hasta 63 posiciones decimales. A continuación se muestran algunos
ejemplos de literales numéricos válidos:

–999.20

0123

555

10

+5

–10
3500

.88888

Literales de caracteres

Con frecuencia, se necesitará trabajar con valores de caracteres. RPG permite


usar literales de caracteres para ese propósito. Los literales de caracteres son
cadenas de caracteres. Al igual que los literales numéricos, los literales de
caracter mantienen un valor constante durante la ejecución del programa. Para
indicar que un valor es un caracter literal (y no un nombre de variable),
simplemente enciérrelo entre apóstrofos. No se aplican restricciones sobre qué
caracteres pueden formar el literal. Cualquier caracter que se pueda representar
mediante el teclado, incluido un espacio en blanco, es aceptable. Si el literal va
a incluir un caracter apóstrofo, use dos apóstrofos para representarlo. Los
literales de caracteres pueden tener hasta 16,380 bytes. A continuación se
muestran algunos ejemplos de literales de caracteres :

'Juan Perez'

'Mno123 @15y'

'Valeria''s Pizza'

'12345'

'99%'

Literales tipificadas

Además de los literales numéricos y de caracteres, se pueden expresar otros


valores de datos, como fechas y horas, mediante el uso de literales tipificados.
Para codificar un literal tipificado, encierre el valor entre apóstrofos y precédalo
con un código de tipo de datos para indicar qué tipo de datos representa el
literal. Para hacer referencia a un valor del 28 de agosto de 1960, por ejemplo,
codificaría D’1960-08-28′ como el literal. Otros códigos de tipo de datos
comunes para literales son T (para horas), Z (para timestamps) y X (para
literales hexadecimales). A continuación, más ejemplos de este tipo de literales:
Tipo de dato

Literal tipificada

Date

D'2000-04-16'

Tipo de dato

Literal tipificada

Time

T'09.55.10'

Tipo de dato

Literal tipificada

Timestamp

Z'2005-04-10-07.46.21.000000'

Tipo de dato

Literal tipificada

Hexadecimal

X'F1F1F1'

Definición de constantes

Una constante nombrada asigna un nombre a un literal para que se pueda


hacer referencia al literal por su nombre en todo el programa. El valor de la
constante nombrada nunca cambia durante el procesamiento. La
instrucción Dcl-c (Declare Constant) define la constante nombrada, este es el
formato:

Dcl-c name value;

La instrucción Dcl-c requiere solo el nombre de la constante y su valor. La


constante se define sin una longitud o tipo específico; esos atributos están
implícitos en el valor. El nombre del elemento de datos debe comenzar con un
caracter alfabético, o los caracteres especiales $, # o @. Los caracteres
restantes pueden ser caracteres alfabéticos, números o cualquiera de los
cuatro caracteres especiales _, #, $ y @. El nombre de un elemento de datos no
puede contener espacios en blanco incrustados entre los caracteres
permitidos.

Se ingresa el valor literal de la constante después del nombre. Introduzca


valores constantes numéricos con un caracter base y un signo si corresponde,
pero nunca con miles como separadores. Incluya valores de constantes de
caracteres entre apóstrofos. Las constantes de otros tipos de datos deben
seguir las reglas indicadas anteriormente para literales tipificados. Estos son
algunos ejemplos de constantes válidas:

Dcl-c SUCCESS '00000';

Dcl-c Sflpag 13;

Dcl-c PRO_EXISTS 'PRODUCT ALREADY EXISTS';

Dcl-c SUCCESSOPE 'OPERATION SUCCESSFUL';

Dcl-c lower_bound -50;

Dcl-c max_count 200;

Dcl-c start_letter 'A';

Una constante de caracteres puede tener un máximo de 16.380 bytes y una


constante numérica puede contener hasta 63 dígitos, con hasta 63 posiciones
decimales. Para ingresar una constante nombrada demasiado larga que no
cabe en una sola línea, continúe el valor en la siguiente línea, usando un signo
más (+) para indicar que la constante continua con el primer caracter que no
está en blanco en la siguiente línea:
Dcl-c msgLargo 'La informacion aqui suministrada es de +

propiedad privada, no se permite su divulgacion, +

en caso de irrespetar esta restriccion se expone a una demanda.';

Uso de constantes figurativas

ILE RPG incluye un conjunto especial de palabras reservadas llamadas


constantes figurativas, las cuales son literales implícitas que se pueden usar
sin una longitud especificada. Las constantes figurativas asumen la longitud y
las posiciones decimales de las variables con las que están asociadas. Algunas
de las constantes figurativas de RPG son las siguientes:

*Blank (or *Blanks)

*Zero (or *Zeros)

*Off

*On

*Hival

*Loval

*All

*Null

RPG permite asignar *Blank o *Blanks para hacer que una variable tipo caracter
se rellene con espacios en blanco. Asignar *Zero o * Zeros a variables
numéricas y de caracteres rellena las variables con ceros.

Las constantes figurativas *Off y *On representan valores de caracter "0" y "1".
*Off equivale a "0" y *On equivale a “1". Aunque puede utilizar *Off y *On con
cualquier variable de caracteres de cualquier longitud, los programadores
suelen utilizar *Off y *On para cambiar o comparar el valor de un indicador.

La asignación de *Hival rellena una variable con el valor más alto posible
apropiado para su tipo de datos. Asignarle a una variable tipo caracter *Hival
establece todos los bytes en FF hexadecimal. Para una variable numérica,
*Hival es el valor positivo máximo permitido para la representación de datos,
generalmente serán 9’s y un signo más (+). La asignación de *Loval rellena una
variable con el valor de clasificación más bajo posible apropiado para su tipo de
datos, por ejemplo, 00 hexadecimal para las variables tipo caracter y el valor
mínimo negativo para las variables numéricas. Los programadores a menudo
asignan *Hival o *Loval a una variable para asegurarse de que el valor de la
variable sea el máximo o el mínimo posible.

Asignar la constante figurativa *All seguido inmediatamente por uno o más


caracteres dentro de apóstrofos repite la cadena dentro de los apóstrofos
cíclicamente a lo largo de toda la longitud de la variable de resultado. Por
ejemplo, asignar *All'Z 'a una variable de caracteres rellena la variable con Zs, y
asignar *All'7' a una variable numérica rellena la variable con 7s.

La constante *Null representa un valor nulo. Por lo general, se usa *Null para
representar la ausencia de cualquier valor, y esto no es lo mismo que usar
espacios en blanco o ceros. Por lo general, en ILE RPG se usa *Null solo en
situaciones inusuales.

Definición de variables independientes

Las variables independientes no forman parte de un registro de base de datos,


o de ningún otro tipo de estructura de datos. Se encuentran solas en el
programa, sin depender de ningún otro tipo de elemento de datos. Un uso típico
de una variable independiente podría ser como un contador para contar el
número de transacciones que se están procesando, como una variable
intermedia para mantener temporalmente un valor para su procesamiento
posterior, como un acumulador para realizar un seguimiento de los montos de
ventas del año hasta la fecha, o como un indicador para representar si una
condición es verdadera. A diferencia de una constante, el valor almacenado en
una variable independiente puede cambiar con frecuencia mientras se ejecuta
el programa. La instrucción Dcl-s (Declare Standalone Variable) define una
variable:

Dcl-s name {data-type} {optional-keywords};

A continuación, se muestran algunos ejemplos de definiciones de variables


independientes:

Dcl-s VX Uns(5);
Dcl-s WHERESQL CHAR(256);

Dcl-s FLAG_CURSO INT(5);

Dcl-s FECFACTUR DATE;

Dcl-s DEUDA PACKED(7:2);

Nombre del elemento de datos (variable)

El nombre debe ajustarse a las reglas que gobiernan los nombres de los
elementos de datos, son las mismas reglas que se usan para la declaración de
constantes. El nombre debe reflejar el contenido de la variable. Los nombres de
los elementos de datos pueden tener hasta 4096 caracteres, pero es mejor
restringirlos a una longitud manejable. Si la variable se va a utilizar para referir a
un archivo descrito externamente, su nombre debe tener 10 caracteres o
menos.

Tipo de datos

Los atributos de una variable se describen en una palabra clave de tipo de


datos, después del nombre de la variable. Las tres clases básicas de datos que
se utilizan en la mayoría de la programación empresarial son datos numéricos,
de caracteres y de fecha. RPG admite algunas variaciones de estas clases de
datos, junto con algunas otras que tienen usos especiales. Las siguientes son
las palabras clave de tipo de datos más utilizadas:

 Char, Varchar, Ind


 Zoned, Packed, Int, Uns
 Date, Time, Timestamp
 Pointer

Datos tipo caracter

Cuando una declaración define una variable como Char, está definiendo una
cadena de caracteres de longitud fija. El atributo de longitud se especifica entre
paréntesis. Todos los valores de la columna tienen la misma longitud en
memoria. Un valor que no ocupa toda la longitud se rellena con espacios en
blanco. La siguiente definición define NOMBRE, una variable de caracteres de
longitud fija de 75 caracteres (bytes) de longitud:

Dcl-s NOMBRE Char(75);

Si la variable se define con un tipo de datos Varchar, es una cadena de


caracteres de longitud variable. Este tipo de datos es apropiado cuando la
longitud es incierta. El tamaño máximo se especifica (hasta 16.773.100 bytes)
entre paréntesis. La siguiente declaración define AVISO, una variable de
longitud variable de 256 caracteres:

Dcl-s AVISO VARCHAR(256);

Un tercer tipo de datos corresponde a IND, el cual define un indicador. Un


programa usa un indicador (a veces llamado bandera o tipo de datos
booleanos), para indicar un estado verdadero, o falso. Un indicador es un
campo de caracteres de un solo byte que solo puede tener dos valores
posibles: *On (‘1’) u *Off (‘0’). El programa puede activar o desactivar los
indicadores, y luego el procesamiento posterior puede estar condicionado por
el estado del indicador. La siguiente declaración define la variable ESTADO de
tipo indicador:

Dcl-s ESTADO IND;

Debido a que todos los indicadores son de un byte, no se especifica una


longitud para un indicador.

Datos numéricos

Recordemos que las variables con zona (o con signo) almacenan datos
numéricos, donde cada dígito ocupa un byte, y que las variables empacadas
usan solo medio byte para cada dígito. RPG permite ambos tipos de datos
numéricos:

Dcl-s MES ZONED(2:0); // Numérico con zona

Dcl-s PAGO_MES PACKED(7:2); // Numérico empacado


Los valores de los parámetros entre paréntesis especifican la precisión (dígitos
totales) y la escala (dígitos decimales), respectivamente. La precisión máxima y
la escala son ambas de 63 dígitos; la precisión debe ser por lo menos tan
grande como la escala. Si no especifica una escala, el valor predeterminado es
cero. Las siguientes declaraciones son equivalentes:

Dcl-s MES Zoned(2:0);

Dcl-s MES Zoned(2);

Los números con zona son equivalentes al tipo de datos NUMERIC de SQL, y
los números empacados son los mismos que el tipo de datos DECIMAL en SQL.

Una variable numérica definida con INT es un entero con signo. Los enteros son
el medio más compacto de almacenar valores numéricos. La siguiente
declaración define un entero de cinco dígitos con signo, CONTROL:

Dcl-s CONTROL INT(5);

La longitud entre paréntesis (que puede ser literal o constante) debe ser de 3, 5,
10 o 20 dígitos:

Dígito

Bytes

Valor Menor

Valor Mayor

-128

127
Dígito

Bytes

Valor Menor

Valor Mayor

-32,768

32,767

Dígito

Bytes

Valor Menor

Valor Mayor

10

-2,147,483,648

2,147,483,647

Dígito

Bytes

Valor Menor

Valor Mayor

20
8

-9,223,372,036,854,775,808

9,223,372,036,854,775,807

Los enteros no tienen decimales y tienen rangos limitados. Sin embargo, son un
tipo de datos eficiente y útil para datos numéricos. Un entero de cinco dígitos
en RPG es equivalente a SMALLINT de SQL, un entero de 10 dígitos en RPG es
equivalente a INT en SQL y un entero de 20 dígitos en RPG corresponde a
BIGINT.

Uns es una variación de los enteros con signo, Uns, define un entero sin signo.
Los enteros sin signo siguen los mismos principios que los enteros con signo,
excepto que sus valores son siempre positivos. La siguiente declaración define
CONTROL, un entero sin signo de cinco dígitos:

Dcl-s CONTROL UNS(5);

Los enteros sin signo tienen un rango de valores diferente de los enteros con
signo:

Dígito

Bytes

Valor Menor

Valor Mayor

255
Dígito

Bytes

Valor Menor

Valor Mayor

65,535

Dígito

Bytes

Valor Menor

Valor Mayor

10

4,294,967,295

Dígito

Bytes

Valor Menor

Valor Mayor

20
8

18,446,744,073,709,551,615

Datos tipo fecha

Los tipos de datos relacionados con fecha en RPG corresponden a los tipos de
SQL. Las siguientes declaraciones definen variables de fecha y hora llamadas
FECPAGO y HORA:

Dcl-s FECPAGO DATE;

Dcl-s HORA TIME;

De forma predeterminada, los tipos de datos timestamp incluyen


microsegundos (seis dígitos para representar fracciones de segundo), el
siguiente es un ejemplo de una definición de una variable tipo timestamp:

Dcl-s ULTCAMBIO TIMESTAMP;

Asignación de valores iniciales a los datos

Además de definir elementos de datos, como variables independientes, se


puede asignar un valor inicial a esos elementos de datos. Si el elemento de
datos es una variable, su valor puede cambiar durante la ejecución del
programa, pero su valor inicial es el que contiene la variable cuando se inicia el
programa.

Para inicializar (es decir, asignar un valor inicial a) una variable, se especifica el
valor utilizando la palabra clave INZ (Inicializar) en la definición de la variable. El
valor inicial se indica mediante un literal, una constante o una constante
figurativa. En las siguientes definiciones se proporcionan valores iniciales a las
variables:

Dcl-s PRECIO PACKED(7:2) Inz(10500.50);

Dcl-s DIRECCION CHAR(200) Inz('SOLUCIONES DE SISTEMAS');


Dcl-s CONTROL CHAR(9) Inz(*Loval);

Dcl-s FECINIC DATE Inz(D'1899-12-30');

Puede utilizar algunos valores reservados especiales para inicializar


definiciones relacionadas con la fecha. Para inicializar un campo de fecha con
la fecha del trabajo, codifique Inz (*Job) en el área de palabras clave de la
definición del campo. También puede inicializar un campo de fecha, hora o
timestamp con la fecha actual del sistema en tiempo de ejecución codificando
Inz (*Sys). Puede pensar en la fecha del trabajo como la fecha de inicio que se
asigna a un trabajo cuando se ejecuta un programa, aunque puede ser o no, la
fecha real en la que se está ejecutando el programa. Sin embargo, la fecha del
sistema (*Sys) es siempre la fecha actual:

Dcl-s FECACTUAL DATE Inz(*Sys);

Otro valor de inicialización útil es *User, el cual se puede utilizar con campos de
caracteres si son al menos de 10 bytes de longitud. La codificación de Inz
(*User) para un campo de caracteres asigna el nombre del perfil de usuario
actual al campo de caracteres:

Dcl-s USUARIO CHAR(10) Inz(*User);

Entendamos que no siempre es necesario asignar un valor inicial a un elemento


de datos. RPG inicializa automáticamente los elementos de datos a los valores
por defecto cuando se inicia el programa, a menos que se use la palabra clave
Inz para inicializar la variable. Los valores por defecto suelen ser espacios en
blanco para las variables de caracteres y ceros para las variables numéricas. Si
con los valores por defecto es suficiente, no es necesario inicializar el elemento
de datos.

Definición de estructuras de datos

Además de las constantes con nombre y las variables independientes, puede


definir estructuras de datos, que son simplemente un medio para organizar
múltiples variables dentro de una sola sección de posiciones contiguas de
memoria. Las estructuras de datos pueden proporcionar flexibilidad en el
manejo de datos permitiendo agrupar variables dentro de una estructura lógica,
subdividir variables en subcampos y redefinir variables con diferentes tipos de
datos o nombres. Un programa de RPG puede procesar toda la estructura de
datos como una unidad o sus subcampos individualmente.

RPG usa tres instrucciones para declarar estructuras de datos:

● Dcl-ds (declarar una estructura de datos)

● Dcl-subf (declarar un subcampo)

● End-ds (Final de la estructura de datos)

Estas instrucciones tienen la siguiente forma:

Dcl-ds ds-name {optional-keywords};

{Dcl-subf} name {data-type} {optional-keywords};

End-ds {ds-name};

El siguiente código ilustra una definición de estructura de datos simple:

DCL-DS CLIENTE;

id INT(10);

nombre CHAR(40);

fecInicio DATE(*ISO);

credito ZONED(11:2);
END-DS;

La instrucción Dcl-ds (Declarar estructura de datos) señala el comienzo de una


estructura de datos. Después de Dcl-ds, se ingresa un nombre para la
estructura, o puede usar *N si la estructura de datos no va a tener nombre. El
nombre es opcional a menos que planee hacer referencia a la estructura de
datos como un todo en alguna parte del programa. RPG trata las estructuras de
datos con nombre como cadenas de caracteres abarcando todos sus
subcampos. Los nombres de las estructuras de datos siguen las mismas reglas
que los nombres de variables.

Definición de subcampos en estructuras de datos

Las líneas para describir los subcampos que componen la estructura de datos
continúan después de la instrucción Dcl-ds. Definen cada entrada de subcampo
dándole un nombre. Es una práctica normal indentar la definición de
subcampos para mostrar una jerarquía de la estructura de datos fácilmente
visible. El orden de los subcampos debe representar sus posiciones relativas en
la estructura de datos. Después del nombre del subcampo, especifique una
palabra clave de tipo de datos. Cualquier tipo de datos permitido para variables
independientes también está permitido para subcampos de estructura de
datos.

Los subcampos numéricos que aparecen en las estructuras de datos suelen ser
de tipo zona, no empacados, debido a que una estructura de datos se considera
fundamentalmente una variable tipo caracter, los números con zona son
generalmente más flexibles porque cada dígito también es un caracter. Los
tipos de datos Packed, Int y Uns son válidos para subcampos numéricos si el
programa no necesita procesar la estructura de datos o sus subcampos como
datos de tipo caracter.

Inicialización de estructuras de datos


Una vez que haya definido los subcampos de la estructura de datos, puede
inicializarlos utilizando la palabra clave Inz. La palabra clave Inz inicializa
estructuras de datos completas o subcampos de la estructura de datos. Si
codifica Inz (sin ningún valor a continuación) en la instrucción Dcl-ds, el
programa inicializa todos los subcampos en la estructura de datos con valores
iniciales apropiados para los tipos de datos de los subcampos.

Se inicializa una estructura de datos globalmente cuando se incluye la palabra


clave Inz en la línea de encabezado de la estructura de datos. Este uso de Inz
inicializa automáticamente todos los subcampos en toda la estructura de
datos.

Especificar Inz en la instrucción Dcl-ds inicializa todos los subcampos al valor


por defecto apropiado para sus tipos de datos (por ejemplo, todos los
subcampos numéricos se inicializan a cero, y todos los campos de caracteres
se inicializan con espacios):

Dcl-ds telefono Inz;

CodArea Zoned(3:0);

Central Zoned(3:0);

NumeroLoc Zoned(4:0);

End-ds telefono;

Sin la palabra clave Inz, todos los subcampos están inicialmente en blanco. El
programa finaliza de forma anormal si intenta realizar operaciones aritméticas
en CodArea, Central o NumeroLoc sin inicializarlas primero.

Estructuras de datos descritas externamente


Las estructuras de datos discutidas hasta ahora son estructuras de datos
descritas en el programa. La descripción completa de la estructura de datos (es
decir, todos los subcampos) se describe explícitamente en el programa. RPG
también admite estructuras de datos descritas externamente y las trata de
manera similar a la forma en que manipula archivos de datos descritos
externamente. Los subcampos en una estructura de datos descrita
externamente siguen el diseño de un archivo existente; los subcampos en la
estructura de datos tienen los mismos nombres, ubicaciones y atributos de
datos que el formato de registro del archivo. Las estructuras de datos descritas
externamente pueden ser útiles cuando desee utilizar una estructura de datos
en varios programas diferentes, o cuando necesite una estructura de datos
para imitar el diseño del formato de registro de un archivo existente.

A continuación, se muestra un ejemplo de una estructura de datos descrita


externamente basada en el diseño de registro un archivo, en este caso la tabla
PRODUCTS:

Dcl-ds PRODUCTS Ext End-ds;

A menudo, el nombre de una estructura de datos descrita externamente es el


mismo que el del archivo en el que se basa la estructura de datos. La palabra
clave Ext (Externo) le dice al compilador que esta estructura de datos se
describe externamente. Ext debe ser la primera palabra clave que sigue al
nombre de la estructura de datos. Debido a que el compilador adquiere
automáticamente los subcampos del archivo externo, no es necesario codificar
ningún subcampo para la estructura de datos; todos los campos del archivo se
convierten en subcampos en la estructura de datos. Observe que, debido a que
no necesita enumerar explícitamente ningún subcampo, la instrucción End-ds
puede aparecer en la misma línea que la instrucción Dcl-ds.

Si el nombre de la estructura de datos no coincide con el nombre del archivo en


el que se basa, la definición de la estructura de datos requiere la palabra clave
Extname (nombre externo) para nombrar explícitamente el archivo:

Dcl-ds PRODS Ext Extname (' PRODUCTS ') End-ds;


Estructuras de datos calificadas

Normalmente, un programa de RPG puede referirse a los subcampos


individuales en una estructura de datos por sus nombres definidos en la
estructura. Esos nombres deben ser únicos; es decir, normalmente no puede
tener varias estructuras de datos con subcampos con nombres idénticos. Una
estructura de datos calificada le permite ignorar esa regla. Cuando incluye la
palabra clave Qualified en la línea de encabezado de la estructura de datos,
crea una estructura de datos calificada.

La palabra clave Qualified le indica al compilador que se referirá a los


subcampos en la estructura de datos por su nombre calificado (es decir, el
nombre de la estructura de datos seguido de un punto y el nombre del
subcampo). Si no se utiliza la palabra clave Qualified, se referirá a los
subcampos por su nombre, y ese nombre debe ser único. Las estructuras de
datos calificadas pueden describirse en el programa o describirse
externamente, y deben denominarse estructuras de datos. El siguiente es un
ejemplo de una estructura de datos descrita externamente y calificada.

Dcl-ds PRODUCTS Ext Qualified End-ds;

//-------------------------------- LOAD SCREEN FIELDS

Dcl-proc loadScreen;

PIDPRODUCT = PRODUCTS.IDPRODUCT;

PDESCRIPT = PRODUCTS.descript;

PIDPRODTYP = PRODUCTS.idProdTyp;

PIDSUPPLR = PRODUCTS.idSupplr;

PUNITPRICE = PRODUCTS.UnitPrice;
PIDWREHOUS = PRODUCTS.idWrehous;

PIDBRAOFFI = PRODUCTS.idBraOffi;

PENTRYDTE = PRODUCTS.entrydte;

End-proc;

Con estructuras calificadas, los subcampos pueden tener nombres idénticos


incluso si no tienen atributos de datos idénticos.

Utilice estructuras de datos calificadas ampliamente para ayudar a documentar


los orígenes de un subcampo de estructura de datos. Son especialmente útiles
en programas grandes y complejos y en aquellos programas que comprenden
mucha programación modular, como subrutinas y procedimientos.

Manipulación de datos tipo Caracter

Asignación de caracteres simple

Al igual que en las operaciones de asignación numérica, se coloca el valor a


asignar (en este caso, un caracter literal) a la derecha del signo igual y coloca la
variable de destino a la izquierda. Si el literal es demasiado largo y no cabe en
sola una línea, o si necesita continuar un literal largo en otra línea, use un
caracter de continuación (+) para indicarle al compilador que el literal continúa
en la siguiente línea:

cadena01 = 'Select descript, idProduct ' +

' From PRODUCTS ' +

' Order by descript';


Cuando se usa el caracter de continuación +, la continuación comienza con el
primer caracter que no está en blanco en la línea siguiente. El literal completo
debe estar encerrado dentro de un par de apóstrofos (‘). Si el literal incluye un
caracter de apóstrofo, utilice dos apóstrofos (‘ ‘) para representarlo. La
operación Eval* realiza la asignación transfiriendo caracter por caracter,
comenzando con el caracter de más a la izquierda. Si la variable de resultado
que recibe la literal se define más larga que el literal de caracter a asignar, Eval
rellena la variable con espacios en blanco (es decir, rellena las posiciones no
utilizadas en el extremo derecho de la variable con espacios en blanco). Pero si
la variable de resultado es demasiado pequeña para almacenar la literal, Eval
trunca los caracteres adicionales que se encuentran más a la derecha, sin
advertencia del error.

Dcl-S ST1 Char(7);

Dcl-S ST2 Char(12);

Dcl-S ST3 Char(4);

ST1 = 'LACASA1'; //VALOR DE ST1 = 'LACASA1'

ST2 = 'LACASA1'; //VALOR DE ST2 = 'LACASA1 '

ST3 = 'LACASA1'; //VALOR DE ST3 = 'LACA'

Observe que cuando la variable de resultado es más larga que el caracter literal
a asignar, el resultado se rellena con espacios en blanco incluso si ya contiene
otros datos. También se puede utilizar Eval* para asignar el contenido de un
campo de caracteres a otro del mismo tipo de datos. Se aplican las mismas
reglas con respecto al relleno y el truncado.

Dcl-S MATERIA Char(5);

Dcl-S MATERIA02 Char(10);

Dcl-S ID Char(2);
MATERIA = 'MAT01'; // VALOR DE MATERIA = 'MAT01'

MATERIA02 = MATERIA; // VALOR DE MATERIA02 = 'MAT01 '

ID = MATERIA; // VALOR DE ID = 'MA'

*La palabra clave Eval de asignación se puede omitir para este tipo de
asignación.

Evalr (evaluar expresión, ajuste a la derecha)

La operación Eval asigna valores transfiriendo el resultado de la expresión


caracter por caracter, comenzando con el caracter más a la izquierda,
rellenando el resultado con espacios en blanco a la derecha si es necesario.
Una operación relacionada, Evalr (Evaluar expresión, ajuste a la derecha),
funciona con la asignación de caracteres cuando se desea ajustar el resultado
a la derecha. Evalr asigna el valor del caracter comenzando con el caracter de
más a la derecha. Si la variable de resultado que recibe el valor se define más
larga que el valor a asignar, Evalr rellena la variable con espacios en blanco (es
decir, rellena las posiciones no utilizadas en el extremo izquierdo de la variable
con espacios en blanco). Evalr no funciona con expresiones numéricas y, a
diferencia de Eval, siempre debe estar codificado explícitamente en el
programa. Para ver cómo funciona Evalr, compare el siguiente código con los
ejemplos de asignación de caracteres anteriores:

Dcl-S ST1 Char(7);

Dcl-S ST2 Char(12);

Dcl-S ST3 Char(4);

EVALR ST1 = 'LACASA1'; //VALOR DE ST1 = 'LACASA1'

EVALR ST2 = 'LACASA1'; //VALOR DE ST2 = ' LACASA1'


EVALR ST3 = 'LACASA1'; //VALOR DE ST3 = 'ASA1'

Asignación de valores con constantes figurativas

RPG le permite asignar constantes figurativas *Blank (o *Blanks) a variables de


caracteres para inicializar las variables con espacios en blanco. También se
puede asignar el valor especial *Zero (o *Zeros) para rellenar las variables de
caracteres con ceros.

Dcl-01 VAR01 Char(9);

VAR01 = *Blanks; // VAR01 = ' '

VAR01 = *Zeros; // VAR01 = '000000000'

Concatenación de valores de caracteres

Utilice el operador + para concatenar dos o más valores de caracteres, que


pueden ser variables, literales o constantes definidas, y asigne el nuevo valor de
caracteres resultante a una variable. Una sola expresión de concatenación
puede contener tantos operadores + y operandos de caracteres, como sea
necesario para lograr la concatenación deseada. El sistema ejecuta las
operaciones de concatenación de izquierda a derecha en la expresión. No hay
problemas de precedencia de operadores porque el único operador permitido
es el operador de concatenación +, si es necesario, simplemente continúe la
expresión en cualquier lugar de las líneas subsiguientes y luego use un punto y
coma (;) para finalizar la expresión.

El siguiente ejemplo demuestra cómo usar + para concatenar cadenas:

Dcl-s SQLSTRING CHAR(256);

SQLSTRING = 'Select descript, idProduct ' +

' From PRODUCTS ' +

' Order by descript';


El siguiente código ilustra el uso de una línea de continuación al concatenar
valores de caracteres. Esta expresión concatena un literal con una variable y
luego agrega otro literal al final de la cadena de caracteres:

Dcl-s SQLSTRING CHAR(256);

Dcl-s WHERECLAUSE CHAR(256);

WHERECLAUSE = ' where descript Like ' + '''%' + %tRIM(FIND) + '%''' ;

SQLSTRING = 'Select descript, idProduct ' +

' From PRODUCTS' + WHERECLAUSE +

' Order by descript';

Uso de variables de caracteres de longitud variable

Todas las variables de caracteres discutidas hasta ahora han sido variables de
caracteres de longitud fija con una longitud declarada específica. ILE RPG
también permite campos de caracteres de longitud variable. Al igual que una
variable de longitud fija, una variable de caracteres de longitud variable tiene
una longitud declarada, la cual es su longitud máxima. Pero las variables de
caracteres de longitud variable también tienen una longitud actual. La longitud
actual generalmente depende del valor de la variable, que puede cambiar
mientras se ejecuta el programa. Algunos procesos con cadenas de caracteres
son más eficientes con cadenas tipo Varchar que con cadenas tipo Char de
longitud fija.

Para definir una variable de caracteres de longitud variable, use la palabra clave
Varchar (o use la palabra clave Like para heredar las propiedades de una
variable de caracteres de longitud variable existente).

DCL-S CADENA01 Varchar(256);


Uso de funciones integradas con datos de caracteres

ILE RPG también admite muchas funciones integradas que puede utilizar con
expresiones de caracteres.

%Trim (Recortar caracteres en los extremos)

La función %Trim (Trim characters) devuelve un valor tipo caracter después de


eliminar todos los espacios en blanco iniciales y finales. La función %Trim tiene
tres variaciones: %Trim, %Triml, y %Trimr. %Trim elimina los espacios en blanco
iniciales y finales, %Triml elimina solo los espacios en blanco iniciales y %Trimr
elimina solo los espacios en blanco finales. El resultado final de todas las
funciones %Trim es un valor tipo caracter de longitud variable (es decir, no tiene
una longitud fija definida).

· %Trim(‘ MUY BIEN ‘) RESULTADO DE LA FUNCION ‘MUY BIEN’

· %Triml(‘ MUY BIEN ‘) RESULTADO DE LA FUNCION ‘MUY BIEN ‘

· %Trimr(‘ MUY BIEN ‘) RESULTADO DE LA FUNCION ‘ MUY BIEN’

Observe que la función %Trim elimina solo los espacios en blanco en los
extremos del valor tipo caracter, no los que están dentro del valor. Por lo tanto,
las funciones de %Trim anteriores retienen el espacio en blanco dentro de ‘MUY
BIEN‘ al recortar los espacios en blanco de los bordes.

Las funciones %Trim son útiles cuando se desea concatenar campos de


caracteres de longitud fija, que pueden contener espacios en blanco que desea
ignorar en el resultado final. Puede incluir las funciones %Trim dentro de una
expresión de concatenación para sustituir el valor tipo caracter de longitud
variable en la expresión:

WHERECLAUSE = ' where descript Like ' + '''%' + %TRIM(FIND) + '%''' ;

%Subst (obtener un Substring)


La función Subst (obtener un Substring) extrae una subcadena (una parte), de
una cadena de caracteres. Se codifican dos o tres argumentos entre paréntesis
inmediatamente después de la función %Subst:

· La cadena de la que se va a producir la extracción,

· La posición dentro de esa cadena donde la subcadena debe comenzar,

· Opcionalmente, la longitud de la subcadena.

Si no se declara el tercer argumento, la subcadena incluye todos los bytes


desde la posición inicial hasta el último byte de más a la derecha en la cadena.
Por lo tanto, el formato de la función de subcadena es el siguiente:

%Subst (string : start {:length})

Como con todas las funciones, se usan separadores de dos puntos entre
argumentos. Puede representar la posición inicial y la longitud opcional
mediante el uso de variables numéricas, constantes o expresiones que
representen números enteros mayores a cero.

Los siguientes ejemplos ilustran cómo obtener una parte de una cadena de
caracteres:

EL VALOR DE %subst('Hello World': 7) ES 'World'

EL VALOR DE %subst('Hello World':7:3) ES 'Wor'

EL VALOR DE %subst('abcd' + 'efgh':4:3) ES 'def'

%Replace (Reemplazar cadena de caracteres)


La función %Replace (Reemplazar cadena de caracteres) cambia un valor en
una cadena de caracteres reemplazando los caracteres existentes con nuevos
valores. El formato de la función %Replace es el siguiente:

%Replace (new-string : old-string {:start{:length}})

El primer argumento (que puede ser una variable tipo caracter, literal o
expresión), proporciona la cadena de reemplazo para insertar en la cadena de
caracteres original, la cual es el segundo argumento que debe ser una variable
de tipo caracter.

Opcionalmente, puede especificar una posición inicial. Si omite este tercer


argumento, el reemplazo ocurre al principio de la variable original. El cuarto
argumento también es opcional, especifica cuántos caracteres se van a
reemplazar. Los argumentos opcionales tercero y cuarto pueden ser variables
numéricas, literales o expresiones. Omitir el cuarto argumento hace que el
número de caracteres sea el mismo que la longitud de la cadena de reemplazo.

Dcl-s SOURCE Char(30);

SOURCE = 'Search string';

source = %replace ( 'word': source : 8:6);

DSPLY source; // SOURCE = 'Search word'

%Scanrpl (Escanear y reemplazar caracteres)

La función %Scanrpl (Escanear y reemplazar caracteres) cambia una cadena de


caracteres reemplazando cada ocurrencia existente de un valor en una cadena
con un nuevo valor. El resultado es una nueva cadena de caracteres con los
valores sustituidos. El formato de la función %Scanrpl es el siguiente:
%Scanrpl (old-value : new-value : string{:start{:length})

El primer argumento proporciona el valor tipo caracter original que se


reemplazara por el nuevo valor, que es el segundo argumento. El tercer
argumento es la cadena de caracteres a escanear.

Opcionalmente, se puede especificar un rango de escaneo con un cuarto y


quinto argumentos; es decir, se puede indicar una posición inicial (cuarto
argumento) y un número de caracteres para escanear (quinto argumento). Si no
se declaran estos argumentos, se escanea toda la cadena. Si se omite el quinto
argumento, la función escanea desde la posición inicial hasta el final de la
cadena. Si se especifica un rango de escaneo, el resultado puede ser mayor,
igual o menor que la cadena de origen. La longitud depende de las longitudes
de las cadenas originales y resultantes y del número de veces que se realiza el
reemplazo.

El siguiente ejemplo utiliza la función %Scanrpl:

Dcl-s STRING1 CHAR (43);

Dcl-s STRING2 CHAR (43);

Dcl-s STRING3 CHAR (43);

string1 = 'I love OS/400 on iSeries.';

string2 = %ScanRpl('iSeries.' : 'Power Systems!' : string1);

string3 = %ScanRpl('OS/400' : 'IBM i' : string2);

// string3 = 'I love IBM i on Power Systems!'

%Xlate (Traducir)

La función %Xlate (Traducir) permite traducir, o convertir caracteres dentro de


una cadena a otros caracteres. Tiene el siguiente formato:
%Xlate (from : to : string {:start})

Los dos primeros argumentos sirven como tablas de traducción: El primero


proporciona los caracteres a traducir y el segundo especifica a qué caracteres
traducirlos. El tercer argumento refiere a una variable tipo caracter que contiene
la cadena de origen; también puede ser una literal. Si no especifica una
ubicación inicial (cuarto argumento), la conversión comienza en la primera
posición de la cadena de origen. La función devuelve una versión traducida de
la cadena de origen, que se puede utilizar en una expresión de asignación.

La cadena del FROM y la cadena del TO deben tener el mismo número de


caracteres, con los caracteres ordenados de modo que cada caracter de la
cadena del FROM tenga un caracter correspondiente en la cadena del TO. Las
cadenas de traducción pueden ser variables, literales o constantes definidas.
Durante el procesamiento de la función %Xlate, cualquier caracter de la cadena
de origen que se encuentre en la cadena del FROM se convierte al caracter
correspondiente en la cadena del TO. Si un caracter de la cadena de origen no
aparece en la cadena del FROM, ese caracter no cambia en el valor de retorno.

El siguiente es un ejemplo en donde se usa la función %Xlate:

Dcl-c lowerCase 'abcdefghijklmnopqrstuvwxyz';

Dcl-c upperCase 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

Dcl-s inputString Char(40);

Dcl-s outputString Char(40);

inputString = 'Convert from lowerCase to upperCase';

outputString = %xlate(lowercase:uppercase:inputString);

// outputString = ‘CONVERT FROM LOWERCASE TO UPPERCASE’


%Scan (Escanear una cadena)

La función %Scan (Escanear una cadena) busca un caracter o cadena de


caracteres dentro de una cadena de caracteres que generalmente es una
variable, pero también puede ser una literal o una constante definida. La
dirección del escaneo es de izquierda a derecha. %Scan tiene la siguiente
forma:

%Scan(search-arg : string {:start{:length}})

El siguiente es un ejemplo en donde se usa %Scan:

Dcl-s posicion Uns(5);

Dcl-s correo Char(20) Inz('juan@gamil.com');

posicion = %Scan('@':correo); // posicion = 5

%Char (Convertir a datos tipo caracter)

La función %Char (Convertir a datos tipo caracter) convierte el resultado de una


variable numérica, literal, o constante definida en un valor tipo caracter. %Char
es especialmente útil cuando necesita incluir datos numéricos en una expresión
de cadena, tal vez para concatenar un valor numérico y una cadena de
caracteres. Todos los operandos en una expresión de cadena deben estar en
formato de caracteres, por lo que la función %Char es necesaria para realizar la
conversión.

Dcl-s msg CHAR (40);

Dcl-s Balance Packed(9:2) Inz(-999.10);

msg = 'Su estado de cuenta es: ' + %Char(Balance);

// msg = 'Su estado de cuenta es: -999.10'


Operaciones numéricas

Asignación numérica.

La operación de asignación principal de ILE RPG es Eval (Evaluar expresión).


Asignar un valor a un campo simplemente significa darle al campo un valor.
Eval siempre trabaja junto con una expresión de asignación, que consiste de
una variable de resultado (objetivo), seguida del operador de asignación (=),
seguida de una expresión. Una declaración Eval dice, en efecto, "Evaluar la
expresión a la derecha del signo igual (=) y almacenar su valor en la variable de
resultado a la izquierda del signo igual". La declaración Eval aparece en la
sección de cálculos del programa. Este es el formato general para una
declaración Eval:

{Eval} result = expression;

Los siguientes ejemplos demuestran cómo utilizar Eval para una asignación
numérica simple. En cada caso, el campo numérico que aparece a la izquierda
del signo igual recibe el valor que aparece a la derecha del signo. El valor de la
derecha puede ser un campo, literal o constante con nombre, pero debe estar
definido como numérico. No puede definir el campo de resultado dentro de la
declaración Eval; debe declararlo previamente en otra parte en el programa:

Eval TOTAL = 0; // SE INICIALIZA UNA VARIABLE NUMERICA

Eval IMPUESTO = .013; // SE ASIGNA UN VALOR POSITIVO

Eval VALOR = -273.16; // SE ASIGNA UN VALOR NEGATIVO

Eval CAMPO1 = CAMPO2; // SE ASIGNA UNA VARIABLE A OTRA VARIABLE


(AMBAS NUMERICAS)

La codificación explícita de la operación Eval en cálculos de formato libre es


opcional, a menos que usted necesite utilizar una función especial (como el
redondeo). Simplemente codifique la expresión de asignación y el compilador
ILE RPG asume que debe usar Eval para realizar la asignación. En este curso,
los ejemplos no codificarán explícitamente la operación Eval a menos que sea
necesario.

El ejemplo anterior se puede implementar sin el uso de Eval:

TOTAL = 0; // SE INICIALIZA UNA VARIABLE NUMERICA

IMPUESTO = .013; // SE ASIGNA UN VALOR POSITIVO

VALOR = -273.16; // SE ASIGNA UN VALOR NEGATIVO

CAMPO1 = CAMPO2; // SE ASIGNA UNA VARIABLE A OTRA VARIABLE


(AMBAS NUMERICAS)

Asignar valores con constantes figurativas

RPG permite asignar una constante figurativa *Zero (o *Zeros) a variables


numéricas para llenar las variables con ceros. También se pueden asignar los
valores especiales *Hival y *Loval a variables numéricas. Para una variable
numérica, *Hival es el máximo valor positivo permitido para la representación
de datos por ejemplo *Hival está compuesto de 9s y con signo más (+) para
variables numéricas empacadas o con zona. Asignar *Loval llena una variable
numérica con el mínimo valor posible; por ejemplo, *Loval está compuesto de
9s y con signo menos (-) para variables numéricas empacadas o con zona.

COMPRAS = *Zeros;

VALOR = *Loval;

Uso de expresiones aritméticas

Además de la asignación numérica simple, la operación Eval también


proporciona un método flexible y poderoso para asignar a campos numéricos
los resultados de cálculos aritméticos simples o complejos en un solo paso. La
expresión para la evaluación puede contener los operadores aritméticos +
(suma), - (resta), * (multiplicación), / (división) y ** (exponenciación o elevar un
valor a una potencia), así como paréntesis, símbolos relaciones (ej. <,>),
operadores lógicos (ej. AND, OR, IN), y funciones integradas.
Una sola expresión puede contener tantos operadores aritméticos, literales
numéricos y campos numéricos como sea necesario para lograr un cálculo
deseado:

IVA = PRECIO * .13;

TOTAL1 = PRECIO + IVA;

DESCUENTO = PRECIO * .10;

TOTAL2 = TOTAL1 - DESCUENTO;

VAR01 = VALOR ** 3;

Todos los valores de la expresión aritmética a la derecha del signo igual deben
ser, por supuesto, campos numéricos, literales o constantes. Otra restricción
surge cuando se utiliza la división en una expresión. un error de tiempo de
ejecución ocurre si en el momento de la división, el divisor (la parte de una
expresión inmediatamente a la derecha del signo de división) se evalúa como
cero.

Cuando la expresión aritmética contiene más de un operador, se usan las reglas


de precedencia de las matemáticas para determinar el orden en el cual realizar
las operaciones. La exponenciación tiene la precedencia más alta, seguida de la
multiplicación y la división, y luego la suma y la resta. Cuando una expresión
contiene operaciones de igual precedencia, el sistema las ejecuta en orden de
izquierda a derecha. Se pueden utilizar paréntesis para cambiar el orden en el
que el programa ejecuta las operaciones; las operaciones entre paréntesis se
realizan antes que las operaciones fuera de los paréntesis.

RESULTADO = VAR1 * VAR2 -1; // EN ESTA OPERACION, PRIMERO SE


MULTIPLICA Y LUEGO SE RESTA

RESULTADO = VAR1 * (VAR2 -1); // EN ESTA OPERACION, PRIMERO SE


RESTA Y LUEGO SE MULTIPLICA

La expresión puede incluir tantos espacios en blanco entre campos, literales y


operaciones como desee para hacer que la expresión sea legible y fácil de
entender. Si es necesario, simplemente puede continuar la expresión en
cualquier lugar de las líneas subsiguientes y terminarla con un punto y coma (;)
cuando finalice la expresión.
El siguiente código ilustra el uso de una línea de continuación con la operación
Eval:

horaExtra = ((Salario / 30) /

hrsDiarias) * 1.5 ;

Para los tipos de expresiones, que utilizan la variable de resultado como primer
operando de la expresión, RPG ofrece varios operadores compuestos: + =, - =, *
=, / = y ** =. Estos operadores le permiten codificar este tipo de expresión
común de manera más concisa. Realizan la función aritmética solicitada,
utilizando la variable de resultado como primer operando de la operación. Por
ejemplo, con + =, la expresión es agregada a la variable de resultado. Las
siguientes expresiones son equivalentes:

VALOR01 = VALOR01 + 1;

// EQUIVALE A

VALOR01 += 1;

VALOR02 = VALOR02 -1;

// EQUIVALE A

VALOR02 -= 1;

Redondeo

Cuando se almacena un valor en una variable de resultado que tiene menos


posiciones decimales que la respuesta calculada, la práctica comercial común
dicta que siempre redondee su respuesta en lugar de permitir que el sistema la
trunque.

La sintaxis que usa ILE RPG para especificar que el redondeo toma lugar es
simple: solo ingrese una H entre paréntesis inmediatamente después del
código de operación Eval de la expresión cuyo resultado desea redondear. Los
siguientes ejemplos muestran el uso de la (H) con Eval:

Eval(h) horaExtra = ((Salario / 30) /


hrsDiarias) * 1.5 ;

Eval(h) DESCUENTO = PRECIO * .10;

Eval(h) HORAEXTRA = VALOR ** 3 ;

Función numérica

%Rem(residuo)

La función incorporada %Rem (residuo) devuelve el residuo entero al dividir dos


números (literales, campos o expresiones). Los dos números se pasan como
dos argumentos, primero el dividendo y luego el divisor, inmediatamente
después de la notación %Rem. Aunque los programadores suelen utilizar %Rem
con la función %Div, no es necesario realizar la operación de división para
obtener el residuo, a continuación un ejemplo con la función %Rem:

A= 123;

B= 27;

DIV = %DIV(A:B); // DIV VALE 4

REM = %REM(A:B); // REM VALE 15

Procesamiento de fechas

Definición de datos relacionados con fechas

ILE RPG soporta tres tipos de datos que pueden almacenar y procesar valores
relacionados con fechas:
· Date

· Time

· Timestamp

La fecha y la hora son tipos de datos que se utilizan para almacenar valores
relacionados con un momento en el año (fecha) y un momento en el tiempo
(hora). Un timestamp es una combinación de una fecha y una hora.
Generalmente, ILE RPG aplica los mismos principios y reglas a los tres tipos de
datos.

Definición de fechas en ILE RPG

Dentro del código de un programa RPG, las variables independientes y los


subcampos de estructura de datos también pueden ser fechas, horas o
timestamps. Algunos ejemplos de estas declaraciones son:

Dcl-s fechaFact Date(*Iso);

Dcl-s horaVenta Time(*Iso);

Dcl-s ultimCamb Timestamp(0);

Dcl-ds datosFact;

fechaVenta Date(*Iso);

a#no Zoned(4:0) Overlay(fechaVenta);

mes Zoned(2:0) Overlay(fechaVenta:6);

dia Zoned(2:0) Overlay(fechaVenta:9);

End-ds;
Comprensión de los formatos de fecha

Cada uno de los tipos de datos relacionados con la fecha tiene un tamaño y
formato preestablecidos, y cada uno tiene un formato externo predeterminado
(*ISO), basado en los estándares de la Organización Internacional de
Normalización (ISO). El formato externo predeterminado para fechas es un
campo de 10 bytes con formato AAAA-MM-DD. La hora tienen una longitud
predeterminada de ocho bytes con formato hh.mm.ss. El formato externo
predeterminado para timestamp (Z) tiene una longitud de 26 bytes con formato
AAAA-MM-DD-hh.mm.ss.uuuuuu.

Asignación de fecha simple

Hasta el momento hemos usado la operación Eval para evaluar expresiones


numéricas y de caracteres, pero ILE RPG también usa Eval con fechas. Para
asignar un valor a una variable de fecha, hora o timestamp, se codifica una
expresión de asignación simple.

En el siguiente ejemplo se asigna el valor de una variable de fecha a otra


variable de fecha. Al igual que en las operaciones de asignación anteriores, se
coloca el valor a asignar a la derecha del signo igual y la variable receptora a la
izquierda. Las dos variables de fecha deben ser compatibles (es decir, no se
puede asignar un número a una fecha). Pero las dos fechas no necesitan estar
en el mismo formato, ni ambas necesitan usar los mismos caracteres de
separación:

Dcl-s fechaIng Date;

Dcl-s fecActual Date Inz(*Sys);

fechaIng = fecActual; // a fechaIng se le asigna la fecha del sistema


Asignación de valores con constantes figurativas

ILE RPG permite asignar valores de constantes con nombre o constantes


figurativas *Hival y *Loval a fechas, horas y timestamps, ya sea con la palabra
clave Inz o durante el procesamiento. *Hival y *Loval pueden tener valores
diferentes, esto dependerá del formato de fecha que se esté usando. Los
formatos que permiten años de cuatro dígitos pueden almacenar cualquier
valor desde el 1 de enero de 0001 hasta el 31 de diciembre de 9999. Las fechas
con formato de año de dos dígitos están restringidas a un rango de 100 años
desde el 1 de enero de 1940 hasta el 31 de diciembre de 2039.

Cálculos con fechas

La operación Eval (asignación) también proporciona un método flexible y


poderoso para realizar fácilmente aritmética de fechas mediante el uso de
expresiones en formato libre. Por lo general, una expresión de fecha incluye los
operadores aritméticos + (suma) y - (resta), así como una o más funciones que
ILE RPG usa para convertir un valor numérico en un tipo de datos interno
especial llamado duración. Duración es el término que utiliza RPG para
describir una unidad de tiempo entre dos fechas, horas o timestamps. ILE RPG
reconoce siete duraciones y proporciona siete funciones:

● %Years, para fechas o timestamps

● %Months, para fechas o timestamps

● %Days, para fechas o timestamps

● %Hours para horas o timestamps

● %Minutes, para horas o timestamps

● %Seconds, para horas o timestamps

● %Mseconds (microsegundos), para horas o timestamps


Cada una de estas funciones convierte un número en una duración que se
puede sumar (o restar) a una fecha, hora o timestamp en una expresión
aritmética:

Dcl-s myDate Date Inz(D'2004-05-01');

// myDate = '2004-05-01'

myDate = myDate + %days(3) ;

// myDate = '2004-05-04'

myDate = myDate + %months(1) ;

// myDate = '2004-06-04'

myDate = myDate - %years(2) ;

// myDate = '2002-06-04'

Uso de funciones integradas con fechas

RPG incluye dos funciones que permiten realizar el procesamiento de fechas


que no se puede hacer con expresiones aritméticas simples:

%Diff, para encontrar la diferencia entre dos fechas, horas o timestamps

%Subdt, para extraer una parte de una fecha, hora o timestamp

%Diff (diferencia)

La función %Diff (Diferencia) calcula la duración entre dos valores de fecha u


hora. Los dos primeros argumentos deben ser elementos de datos de tipos
compatibles, luego el tercer argumento representa el código de duración que
corresponde a la unidad de duración que desea determinar. El formato de la
función es el siguiente:

%Diff(date1 :date2: duration{:scale})

En este formato, la primera fecha debe ser más reciente que la segunda fecha
(u hora). El resultado es un número (no redondeado, hasta un límite de 15
dígitos) y se descarta el residuo. Puede utilizar la función% Diff para encontrar
la duración entre:

● Dos fechas

● Dos valores de tiempo

● Dos valores timestamps

● Una fecha y la parte de fecha de un timestamp

● Un valor de tiempo y la parte del tiempo de un timestamp

El tercer argumento debe ser un valor especial que corresponda a una de las
siete duraciones:

*Years (or *Y), para fechas o timestamps

*Months (or *M), para fechas o timestamps

*Days (or *D), para fechas o timestamps

*Hours (or *H), para valores de tiempo o timestamps

*Minutes (or *Mn), para valores de tiempo o timestamps

*Seconds (or *S), para valores de tiempo o timestamps


*Mseconds (or *Ms), para valores de tiempo o timestamps

Ejemplo con la función %Diff:

Dcl-s edad Uns(5);

Dcl-s fecNac Date Inz(D'1960-08-28'); //fecha de nacimiento

Dcl-s FecActual Date Inz(*Sys);

Age = %Diff(FecActual:fecNac:*Years); // edad actual

%Subdt (extracto de fecha / hora / Timestamp)

La función %Subdt (Extracto de fecha / hora / Timestamp) obtiene una


subcadena de una fecha; es decir, extrae una parte de una fecha, hora o
timestamp. El primer argumento es la variable de fecha, hora o timestamp,
seguido de un valor especial que especifica la parte del valor que desea extraer,
uno de los mismos valores especiales que utiliza la función %Diff. El formato
general es el siguiente:

%Subdt (date : duration {:precision{:scale}})

Este es un ejemplo del uso de esta función:

Dcl-s fecha Date Inz(D'1999-02-17');

Dcl-s hora Time Inz(t'01.23.45');

Dcl-s valor Uns(5);


valor = %subdt(fecha:*YEARS); // valor = 1999

dsply %char(valor);

valor = %subdt(hora:*MN); // valor = 23

dsply %char(valor);

Operaciones de iteración

Las operaciones de iteración permiten que un programa repita una serie de


instrucciones, una necesidad que es común en la programación.

Dow (hacer mientras)

La operación Dow (Do while) establece un ciclo basado en una expresión de


prueba condicional. Todas las operaciones codificadas entre este operador y su
correspondiente declaración final (Enddo) se repiten siempre que la condición
especificada en la prueba relacional permanezca verdadera.

Asumamos que queremos sumar los números entre 1 y 10. El uso de Dow
permite realizar fácilmente este caso, veamos el siguiente código:

dcl-s I INT(3);

I=0;

dow i < 10;

I += 1;

dsply %CHAR(I);

enddo;
// ESTE CICLO DESPLIEGA LOS VALORES DESDE 1 HASTA 10

Note que manualmente incrementamos el número de la variable dentro del


ciclo. Este procesamiento es necesario de modo que el programa pueda salirse
del ciclo cuando la condición ya no es verdadera. La expresión condicional es
testeada durante cada iteración antes que las instrucciones dentro del ciclo
sean ejecutas. Si la condición ya no es verdadera el control se pasa a la primera
declaración siguiendo la operación Enddo. Si no se realiza ningún
procesamiento para cambiar el estado de la condición, el ciclo entra en una
condición de sin fin, o sale hasta que se produzca un error.

Dou (hacer hasta)

Dou (hacer hasta) es una operación de iteración estructurada similar a Dow. Al


igual que Dow, Dou incluye una expresión de prueba condicional. Sin embargo,
existen dos diferencias importantes entre las dos operaciones. Primero, una
operación Dow se repite mientras la condición especificada permanece
verdadera, mientras que una operación Dou se repite hasta que la condición se
convierte en verdadera. Segundo, en un Dow la expresión condicional se prueba
antes de que las instrucciones se ejecuten dentro del ciclo. Si la condición es
falsa no se ejecutan las instrucciones dentro del ciclo. Dou, por el contrario, es
un ciclo de decisión final, debido a que la condición se prueba después de que
se hayan ejecutado las instrucciones dentro del ciclo, las instrucciones siempre
se ejecutan al menos una vez.

El siguiente código ilustra un ejemplo con Dou:

dcl-s I INT(3);

I = 0;

DOU I = 9;

dsply %CHAR(I);

I += 1;
ENDDO;

// ESTE CICLO DESPLIEGA LOS VALORES DESDE 0 HASTA 8

Ciclos y salidas anticipadas

A veces, se puede querer omitir las instrucciones restantes dentro de un ciclo


para comenzar la siguiente iteración o ciclo. En otros casos, es posible que
desee salir del ciclo por completo antes de que la comparación relacional
termine la repetición. Dos operaciones ILE RPG, Iter (Iterate) y Leave, le brindan
estas capacidades.

Cuando el programa encuentra una operación Iter, el control pasa por alto las
instrucciones restantes en el ciclo y hace que comience la siguiente repetición.
Leave termina el proceso del ciclo por completo y envía el control a la
declaración que sigue inmediatamente a la declaración End del ciclo. Puede
usar una o ambas declaraciones con todas las operaciones iterativas (Dow,
Dou y For), pero no con las operaciones de selección (If, Select, etc.).

Ejemplo:

Dou %Eof;

Readc PGMSFL;

Select;

When %Eof; // SI HAY FIN DE ARCHIVO SE FINALIZA EL CICLO

Leave;

When Option = '1';

VACTION = 'C' ;

PROCHG_SQL(PIDPRODUCT:VACTION:VRESPONSE); // LLAMADA A OTRO


PROGRAMA

MSG = VRESPONSE;

When Option = '2';


VACTION = 'D' ;

PRODEL_SQL(PIDPRODUCT:VACTION:VRESPONSE); // LLAMADA A OTRO


PROGRAMA

MSG = VRESPONSE;

When Option = '3';

Iter; // OPCION EN DESARROLLO

OTHER;

ERROR = ERROR01; // ERROR01 = 'OPCION INVALIDA

Leave;

Endsl;

Enddo;

For

ILE RPG ofrece la operación For diseñada específicamente para ciclos


controlados por recuento (es decir, para ejecutar ciclos un número específico
de veces). Como Dow y Dou, un operador final (Endfor) señala el final de un
grupo For. A diferencia de las operaciones DOW y DOU, la operación For
incrementa automáticamente su contador para garantizar que la repetición se
produzca el número de veces deseado. El formato de la operación For es un
poco más largo que el de Dow o Dou porque For ofrece más opciones y valores
predeterminados. El diseño general de un ciclo For es el siguiente:

For Counter {= Start_Value} {To Limit_Value} {By Increment_Value};

En general la operación For le permite especificar cuatro cosas:

· Una variable que sirve como contador

· El valor inicial del contador

· El valor máximo del contador para que el ciclo continúe


· La cantidad para añadir al contador al final de cada repetición del ciclo.

Aunque ILE RPG le permita especificar opcionalmente estos cuatro valores se


puede omitir cualquiera de estos menos el campo del contador.

La variable de contador debe definirse como una variable numérica


(preferiblemente un número entero) con cero posiciones decimales. Puede
omitir el valor inicial del contador, pero si lo hace, el contador comienza con el
mismo valor que tenía antes de que el programa entrara en el ciclo For.

En la cláusula To, especifique una variable numérica completa, constante o


literal como valor límite. Si su valor límite es una variable, su valor determina el
número de repeticiones.

La cláusula By especifica una variable numérica completa, constante o literal


como valor de incremento. Si su valor de incremento es una variable, su valor
determina el valor de incremento. También puede omitir la cláusula By. Si lo
hace, ILE RPG asume un valor de incremento de 1; es decir, agrega 1 al valor del
contador antes de comenzar cada pasada adicional a través del ciclo.

El siguiente ejemplo ilustra el ciclo For:

For VX = 1 to 13; // ESTE CICLO SE REPITE 13 VECES

Exec SQL Fetch PRODCURSOR Into :VDESCRIPT, :VIDPRODUCT;

Select;

When Sqlstate = Success; // Success = '02000'

Rrn += 1;

Option = *BLANKS;

PDESCRIPT = VDESCRIPT;

PIDPRODUCT = VIDPRODUCT;

Write PGMSFL; // WRITE RECORD IN SUB FILE SCREEN

When Sqlstate = Endoffile; // Endoffile = '02000'

Sflend = *On;
Leave;

Endsl;

Endfor

Operaciones de selección

IF

El operador de decisión principal de RPG es IF. El formato general de la


operación IF es el siguiente:

If conditional_expression;

...

Endif;

La expresión condicional debe ser verdadera o falsa. Si la expresión condicional


es verdadera, todos los cálculos entre la instrucción If y su correspondiente
Endif se ejecutan. Si la relación no es verdadera, esas declaraciones se omiten.
El grupo If tiene un punto de entrada (la declaración If) y un punto de salida (la
declaración Endif).

Este es un ejemplo con if.. Endif:

If Not Exit;

Exec SQL Select * Into :PRODUCTS From PRODUCTS

Where idProduct = :PIDPRODSEL;

Select;

When Sqlstate = Success;

loadScreen();
Exfmt PRODINQ;

When Sqlstate = EOF;

MSG = ERROR;

Other;

MSG = OPERUNSUCC;

Endsl;

Endif;

No es necesario que limite la comparación a variables simples o literales;


también puede incluir expresiones:

IF (HORAEXTRA - 100 ) > 200 ;

Dsply %CHAR(HORAEXTRA - 100);

ENDIF;

Si se requiere ejecutar una serie de instrucciones basadas en pruebas o


condiciones múltiples. ILE RPG incluye los operadores binarios And y Or para
permitir tales condiciones múltiples. Cuando se usa And para configurar una
condición compuesta, ambas relaciones deben ser verdaderas para que If se
evalúe como verdadero:

IF (HORAEXTRA - 100 ) > 200 AND EDAD > 59 ;

Dsply %CHAR(HORAEXTRA - 100);

ENDIF;
Cuando se usa Or para trabajar con dos pruebas relacionales, If se evalúa como
verdadero si una de las condiciones (o ambas) es verdadera:

IF (HORAEXTRA - 100 ) > 200000 OR EDAD = 60 ;

Dsply %CHAR(HORAEXTRA - 100);

ENDIF;

Puede combinar And y Or para crear pruebas condicionales más complejas.


Note que And se evalúa antes que Or. Sin embargo, se pueden utilizar
paréntesis para cambiar el orden de evaluación; los paréntesis siempre se
evalúan primero. Si una expresión condicional requiere más espacio que el que
ofrece una sola especificación, puede extender la expresión a líneas
adicionales.

Else

También puede incluir una operación Else dentro de un grupo If para configurar
una ruta alternativa de instrucciones que se ejecutarán en caso de que la
condición If sea falsa:

IF Sqlstate = Success;

PARM_MSG = SUCCESSOPE;

ELSE;

PARM_MSG = NOTSUCCOPE;

ENDIF

Anidamiento de grupos If

También se pueden anidar grupos If. Es decir, puede crear grupos If dentro de
otros grupos If, con o sin operaciones Else. Cada If requiere un Endif en el lugar
apropiado para indicar el punto final de la influencia de ese grupo de If. El
siguiente es un ejemplo con If anidados en ILE RPG:

IF PARM_ACT = 'A';

Addmode = *On; // WAY TO TURN ON AN INDICATOR

Chgmode = *Off;
Dltmode = *Off; // WAY TO TURN OFF AN INDICATOR

ELSE;

IF PARM_ACT = 'C';

Chgmode = *On;

Addmode = *Off;

Dltmode = *Off;

ELSE;

Dltmode = *On;

Chgmode = *Off;

Addmode = *Off;

ENDIF;

ENDIF;

Select (Seleccionar operaciones condicionalmente)

Aunque se pueden expresar decisiones de programación complejas con una


serie de operaciones If, Else, los If anidados pueden ser difíciles de configurar y
difíciles de interpretar para otros programadores. Para superar este problema,
ILE RPG utiliza la operación Select (Seleccionar operaciones condicionalmente)
para permitir simplificar la codificación de lógica de decisión.

La operación Select aparece sola en una línea para identificar el inicio de un


grupo Select, y la operación está compuesta de una o más líneas When, cada
una de las cuales especifica una condición que se debe comprobar y cada
When contiene uno o más cálculos que se realizan cuando se cumple esa
condición. Cuando se ejecuta el programa, se comprueban las condiciones
When secuencialmente, comenzando por la primera. Tan pronto como
encuentra una condición verdadera, el programa ejecuta la operación (u
operaciones) que siguen a la declaración When y luego envía el control al final
del grupo Select, señalado por una operación Endsl (End Select).

El siguiente código muestra un ejemplo usando Select:


Select;

When Exit;

Leave;

When AddProduct;

AddProd();

Clearsfl();

Loadsfl();

When Pagedown;

Loadsfl();

When Find <> *Blanks;

Clearsfl();

Loadsfl();

Other;

Readsfl();

Clearsfl();

Loadsfl();

Endsl;

Observe en este ejemplo el código de operación Other, si se usa, debe ser la


condición final del Select. Un grupo de selección que incluye una operación
Other hace que el programa siempre realice uno de los conjuntos de cálculos.
Cuando un grupo de selección consta solo de condiciones When, no se realiza
ninguna operación dentro del grupo de selección si no se cumplen las
condiciones.

Uso de LIKE y LIKEDS


Varias palabras clave permiten definir estructuras de datos, subcampos de
estructura de datos y variables independientes que heredan ciertas
características de otros elementos de datos en el programa. RPG utiliza las
palabras clave LIKE, LIKEDS para definir un nuevo elemento de datos en base a
un elemento de datos principal ya definido.

LIKE

La palabra clave Like define una variable independiente o un subcampo de


estructura de datos que adopta la longitud y el tipo de datos de otro elemento
de datos. Cuando define un elemento de datos con la palabra clave Like, el tipo
de datos, las posiciones decimales y, por lo general, las longitudes se dejan en
blanco porque el compilador recupera esos atributos del ítem padre al que se
hace referencia. A continuación, se muestran algunos ejemplos del uso de LIKE:

// DIRECCION Y NOMBRE TIENEN TAMAÑOS DE 50 CARACTERES

Dcl-s DIRECCION Char(50);

Dcl-s NOMBRE Like(DIRECCION);

// SALDO Y PRECIO SON CAMPOS NUMERICOS DE 7 ENTEROS CON 2


DECIMALES

Dcl-s SALDO Zoned(9:2);

Dcl-s PRECIO Like(SALDO);

En estos ejemplos, el elemento de datos recién definido utiliza el elemento de


datos principal existente (otra variable independiente, estructura de datos o
subcampo de estructura de datos) como referencia al asignar atributos de
datos.

LIKEDS

Otra palabra clave, LIKEDS (como estructura de datos), define una estructura de
datos en base a otra estructura de datos, con los mismos subcampos:

DCL-DS INFOCLI QUALIFIED;


ID INT(10);

NOMBRE VARCHAR(50);

CIUDAD VARCHAR(50);

NOORDENS INT(10);

END-DS INFOCLI;

DCL-DS PROVEEDOR LIKEDS (INFOCLI);

Los subcampos del nuevo elemento de datos son idénticos a la estructura de


datos principal. La nueva estructura de datos implícitamente es calificada,
incluso si la estructura de datos principal no está calificada. Esto significa que
debe hacer referencia a los subcampos en la nueva estructura de datos por su
nombre calificado, aunque no codifique explícitamente la palabra clave
Qualified. Tenga en cuenta que End-ds no se utiliza cuando la estructura de
datos se define con Likeds porque no se permiten subcampos adicionales.

Operaciones Monitor On-Error (Try Catch)

ILE RPG utiliza medios de captura de errores: Operaciones Monitor (Iniciar un


grupo Monitor) y On-error (En caso de error). Estas operaciones proporcionan
captura de errores dentro de un bloque de múltiples líneas de código en un
programa. El objetivo de Monitor y On-error es que se pueda aislar un bloque (o
una línea) de código en el momento de la ejecución. Si un error ocurre en ese
bloque se puede especificar un código de manejo de errores específicamente
para ese bloque. En algunos lenguajes de programación este concepto es
conocido como “Try Catch” porque el programa ejecuta un código de bloque y
atrapa cualquier error que ocurra en el bloque.

Las operaciones Monitor y Endmon forman grupos de monitoreo, y contienen


código para manipular el manejo de errores. Un grupo Monitor consiste de un
bloque Monitor, seguido de uno o más bloques On-error y, finalmente, una
operación Endmon. El bloque Monitor contiene el código que usted cree que
podría generar un error, y los bloques On-error contienen el código para
procesar el tipo de error.
Ejemplo:

Monitor;

// Code to monitor

On-Error statuscode1 : statuscode2 :statuscode3;

// Handle Error related to statuscode1 or statuscode2 or statuscode3

On-Error *FILE;

// Handles all file-error status codes, from 01000 to 09999

On-Error *PROGRAM;

// Handles all program-error status codes, from 00100 to 00999

On-Error *ALL;

// Handles both program-error and file-error codes, from 00100 to 09999. This
is the

default.

EndMon;

Si ocurre un error dentro de un bloque Monitor el control se pasa


inmediatamente al primer bloque On-error dentro del grupo Monitor. Cada
operación On-error enumera uno o más errores de los que es responsable.
Estos errores corresponden a códigos de estado que van desde 00100 hasta
09999 o usted puede especificar *File` para errores generales de archivo,
*Program para errores generales de programa o *All para cualquier error. El
código que sigue a una operación On-error forma un bloque On-error y se
ejecuta cuando el error que ocurrió coincide con la declaración de error. Solo la
primera declaración On-error coincidente se procesa. Si no existen
declaraciones On-Error coincidentes, no se procesa ninguna. Después de que
un bloque On-Error ha sido procesado o cuando el bloque Monitor se ejecuta
sin errores el control pasa a la declaración Endmon que termina el grupo.

Un grupo Monitor puede aparecer en cualquier lugar en un programa RPG y


también se pueden anidar grupos Monitor (los grupos más internos se ejecutan
primero).
Aunque se utiliza principalmente la operación Monitor para procesar errores
que ocurren en un bloque de código, Monitor también puede aplicarse a una
operación individual. El siguiente ejemplo muestra el uso de Monitor y On-error
para manejar errores que surgen para una operación de asignación:

Monitor;

Eval(H) Resultado = Var01 / var02;

On-error 00102; // Divide by zero

Resultado = 0;

Endmon;

Llamada de programas

Programación modular

ILE RPG proporciona la operación Callp (llamar a un procedimiento/método o


llamar a un programa por medio de un prototipo) para facilitar un enfoque
modular al desarrollo de programas. Cuando la ejecución de un programa
alcanza una sentencia Callp, el control se pasa a otro programa (otro objeto
tipo *PGM). El programa llamado se ejecuta hasta que éste llegue a una
sentencia Return, entonces el control se devuelve al programa que hizo la
llamada (programa llamante) y éste continua con la línea siguiente a la que hizo
la llamada.

El flujo de control con una llamada es como el de una operación RPG Exsr
(ejecutar Subrutina), excepto que la llamada invoca un programa externo (o un
subprocedimiento) en lugar de una subrutina interna del programa. A la lista de
los programas activos en un trabajo se le llama pila de llamadas. Cada entrada
en la pila de programas (programa o procedimiento) tiene un nivel de llamada
que depende de su posición relativa con respecto a otras llamadas en la pila. El
primer programa tiene un nivel de llamada de 1, ese programa puede llamar a
otros programas los cuales estarán en un nivel de llamada de 2. Si alguna
entrada de nivel de llamada 2 realiza llamadas adicionales, esas llamadas
estarán en un nivel 3 y así sucesivamente. Cuando un programa de nivel de
llamada 3 termina, éste devuelve control al nivel de llamada 2. Similarmente,
cuando una entrada de nivel de llamada 2 termina regresa el control al nivel de
llamada 1.Y cuando un nivel de llamada 1 termina, el trabajo termina. El sistema
utiliza la pila de llamadas para monitorear el punto en el cual cada programa
debería regresar el control cuando cada uno termina.

Una llamada podría ser de valor limitado si no le permitiera a los programas


(llamado y llamante) compartir datos. Un programa RPG puede normalmente
acceder a cualquier valor de las variables desde cualquier lugar dentro del
programa, pero esta característica de variables globales no se extiende fuera
del programa. El alcance de una variable (su nivel de influencia) se limita al
programa en el cual es definida. Eso significa que si se necesita que un
programa llamado procese datos y retorne resultados de procesamiento a un
programa llamante, se deben hacer ajustes para permitir el intercambio de
información.

La operación Callp pasa parámetros para comunicar valores entre el programa


de llamada y el programa llamado. Parámetros (a veces referido como
argumentos) son valores, que usualmente se almacenan en variables que un
programa puede pasar a otro programa. De esta forma el programa llamado
puede aceptar los parámetros y usar estos valores para controlar su
procesamiento.

Prototipo para la interfaz de llamada

Antes de que se pueda utilizar Callp para llamar un programa se debe definir
una interfaz para hacer la llamada. La interfaz básica de llamada incluye la
siguiente información:

· El nombre del programa que se va a llamar


· El número de parámetros por pasar, los atributos de datos de los parámetros y
el orden en el cual pasar los parámetros.

Para definir esta interfaz se define una estructura especial llamada prototipo. El


compilador utiliza el prototipo para asegurar que el programa que llama pasa
los parámetros en forma correcta al programa llamado. El prototipo se codifica
en la sección de declaraciones del programa que hace la llamada. Si el
programa de llamada llama más de un programa, debe incluir un prototipo para
cada llamada.

Para codificar un prototipo se define una estructura similar a una estructura de


datos. La definición de prototipo tiene dos partes:

· La definición de prototipo (encabezado del prototipo)

· La descripción de parámetros para compartirse entre los programas.

La instrucción Dcl-pr (Declare prototype) señala el comienzo de una interfaz de


llamada por medio de un prototipo. El formato es el siguiente:

Dcl-pr name {keywords};

El siguiente es un ejemplo de un prototipo para una interfaz de llamada a un


programa:

Dcl-pr PRODEL_SQL Extpgm('PRODEL_SQL');

PARM_PROD Char(5);

PARM_ACT Char(1);

PARM_MSG Char(40);
End-pr;

Al prototipo se le debe dar un nombre, el cual es el nombre que la operación de


Callp utilizará. Codifique el nombre externo del programa asociado con este
prototipo utilizando la palabra clave Extpgm; el programa es el objeto que se
ejecuta cuando se realiza la llamada. La entrada Extpgm suele ser una literal,
pero puede ser una constante o un campo que contenga el nombre del
programa. El nombre de Extpgm debe coincidir con el nombre del programa
llamado; es sensitivo a mayúsculas y minúsculas, y para este caso debe estar
todo en mayúsculas.

El nombre del prototipo no necesita coincidir con el nombre del programa


externo, aunque talvez sea conveniente utilizar nombres coincidentes. Si los
nombres coinciden no se necesita nombrar el programa dos veces.

Dcl-pr PRODEL_SQL Extpgm;

PARM_PROD Char(5);

PARM_ACT Char(1);

PARM_MSG Char(40);

End-pr;

Describa los parámetros en las líneas siguientes, siguiendo la instrucción Dcl-


pr, utilizando una codificación similar a la de los subcampos de estructura de
datos. Un prototipo puede enumerar un máximo de 255 parámetros para hacer
una llamada a un programa.

Observe que no se necesita nombrar los parámetros. Puede que encuentre


conveniente documentar el uso de parámetros nombrándolos, pero el
compilador trata estos nombres como comentarios. Si el programa necesita
esas variables, se deben definir por separado. Si usted decide no nombrar los
parámetros, puede usar el valor *N en su lugar:
Dcl-pr PRODEL_SQL Extpgm('PRODEL_SQL');

*N Char(5);

*N Char(1);

*N Char(40);

End-pr;

Cuando no hay parámetros para pasar a un programa, el prototipo puede


consistir de una sola línea:

Dcl-pr sinParms Extpgm End-pr;

Callp (Llamada a un procedimiento o programa usando un prototipo)

La operación Callp toma esta forma:

Callp prototype(parm1: parm2: … parmn)

Callp invoca al objeto del programa (tipo *PGM) asociado con el prototipo
declarado en el primer argumento requerido en la llamada y luego le pasa el
control. Enumere los parámetros entre paréntesis después del nombre del
prototipo:

Dcl-pr PRODEL_SQL Extpgm('PRODEL_SQL');

*N Char(5);

*N Char(1);

*N Char(40);

End-pr;
……………….

CALLP PRODEL_SQL(PIDPRODUCT:VACTION:VRESPONSE); // DELETE A


PRODUCT

Si los parámetros que pasa Callp no coinciden con el número, el orden y los
tipos de datos de los parámetros en la definición del prototipo, el programa no
se compilará. Si no hay parámetros, debe codificar paréntesis vacíos en su
lugar:

Dcl-pr sinParms Extpgm End-pr;

………….

CALLP sinParms ();

En realidad, codificar la operación Callp es opcional cuando se trabaja con


RPG en formato libre. El compilador puede inferir la operación Callp en lugar
de codificarla explícitamente, así como lo ilustran los siguientes ejemplos:

PRODEL_SQL(PIDPRODUCT:VACTION:VRESPONSE); // DELETE A PRODUCT

sinParms ();

Este código llama a los programas asociados con sus respectivos prototipos, al
igual que en los ejemplos anteriores, la mayoría de los programadores
prefieren omitir la codificación explícita de la operación Callp, ya que esta
sintaxis coincide con operaciones de llamada similares a otros lenguajes
informáticos, como C o Java.

Return y *INLR

El sistema devuelve el control al programa de llamada cuando el programa


llamado encuentra una operación Return. Si el indicador LR esta también
encendido cuando se ejecuta el Return, el sistema libera los recursos asignados
al programa llamado. Una llamada subsecuente al programa causa que
comience de nuevo como si fuera la primera vez. Sin embargo, si el indicador
LR no está encendido dentro del programa llamado cuando éste retorna control,
el programa llamado continua activo, Como resultado una llamada subsecuente
a ese programa encuentra todas sus variables (excepto las variables de los
parámetros) e indicadores con los valores que tenían en el momento del
retorno anterior. Además, cualquier archivo que el programa llamado utilizó
permanece abierto, como resultado de la llamada anterior.

Dcl-pr EJEMPLO01 Extpgm End-pr;

Dcl-s K Uns(2);

For K = 1 To 10;

EJEMPLO01 ();

Endfor;

*Inlr = *On;

Return;

La interfaz de procedimiento para comunicación de programas

En el programa de llamada, RPG usa la definición de prototipo para describir la


lista de parámetros que pasará al programa llamado. El programa llamado
también debe usar una definición de prototipo para describir la lista
correspondiente de parámetros que recibirá del programa llamante. Los
prototipos de cada programa deben coincidir entre sí, es decir, deben tener el
mismo número de parámetros y los parámetros correspondientes entre los dos
programas deben tener los mismos atributos de datos.
Por defecto, RPG pasa los argumentos de los parámetros pasando la dirección
de la ubicación en memoria representada por el parámetro, por lo que, de
hecho, estos parámetros correspondientes hacen referencia a la misma
ubicación de memoria dentro de la computadora. Los nombres de datos de los
parámetros que usa el programa llamante y el programa llamado no necesitan
ser los mismos. Para acceder a esas ubicaciones de memoria pasadas por el
programa llamante, el programa llamado debe incluir una definición de interfaz
de procedimiento (además del prototipo).

Al igual que el prototipo, la interfaz de procedimiento describe los parámetros


que el programa llamado debe compartir con el programa llamante. Sin
embargo, a diferencia del prototipo, la interfaz de procedimiento define los
nombres de las variables para almacenar los valores de los parámetros que el
programa llamado debe recibir, lo que le permite hacer referencia a esos
valores de los parámetros y manipularlos.

La interfaz de procedimiento se codifica de manera similar al prototipo


mediante la instrucción Dcl-pi (declarar interfaz de procedimiento). La interfaz
del procedimiento del programa debe aparecer después del prototipo del
programa principal. Muchos programadores prefieren codificar la interfaz del
procedimiento principal inmediatamente después del prototipo del
procedimiento principal, y hacer que estas dos definiciones sean las primeras
en el programa. La interfaz de procedimiento tiene dos partes:

· La interfaz del procedimiento (el encabezado)

· Las definiciones de los parámetros que el programa llamado va a recibir

El siguiente es un ejemplo de un prototipo del procedimiento principal y una


interfaz del procedimiento correspondiente:

// ------ Main Prototype/Interface

Dcl-pr PROVAL_SQL Extpgm; // Prototype

*N Char(10);

*N Char(5);

*N Char(1);

End-pr;
Dcl-pi PROVAL_SQL; // Interface

PARM_TABLE Char(10); // TABLE NAME

PARM_CODVA Char(5); // CODE VALUE

PARM_RESUL CHAR(1); // RESPONSE

End-pi;

La instrucción Dcl-pi señala el comienzo de una interfaz de llamada que


corresponde a un prototipo. Debe asignar un nombre a la interfaz del
procedimiento y el nombre debe coincidir con el nombre del prototipo del
programa llamado. Cuando la interfaz de llamada implica el pase de
parámetros, defina los parámetros en las líneas siguientes, siguiendo el
encabezado de Dcl-pi. Se deben asignar nombres a las variables de los
parámetros; el programa usa estos nombres para referirse a los parámetros.
Los atributos de datos y la longitud de los parámetros en la interfaz del
procedimiento deben coincidir con las entradas de parámetros
correspondientes en el prototipo. Algunos programadores prefieren dar siempre
a la interfaz del procedimiento principal un nombre coherente, como Main. En
ese caso, la palabra clave Extpgm debe hacer referencia al nombre real del
programa:

// ------ Main Prototype/Interface

Dcl-pr MAIN Extpgm ('PROVAL_SQL');

*N Char(10);

*N Char(5);

*N Char(1);

End-pr;

Dcl-pi MAIN;
PARM_TABLE Char(10); // TABLE NAME

PARM_CODVA Char(5); // CODE VALUE

PARM_RESUL CHAR(1); // RESPONSE

End-pi;

El compilador usa el prototipo para llamar al programa correctamente y para


asegurar la coherencia de los parámetros entre el programa llamante y el
programa llamado. Cuando no hay parámetros involucrados en la llamada de
un programa, el programa llamado no necesita tener ni prototipo ni interfaz de
procedimiento. Sin embargo, es posible que desee codificar cada uno de ellos
para mantener coherencia entre los programas y promover futuras mejoras en
la aplicación. Independientemente de cualquier consideración sobre el paso de
parámetros, Callp (llamada a un programa) siempre requiere que el programa
de llamada tenga un prototipo.

Pase de parámetros y cambio de valores

Los prototipos proporcionan un medio efectivo para describir los elementos de


datos que se pasan entre los programas. Una vez que las definiciones de
interfaz de procedimiento y prototipo están en su lugar tanto en el programa
que llama como en el programa llamado, el programa llamante puede pasar
parámetros al programa llamado, y el programa llamado puede usar los
parámetros durante el curso de su procesamiento.

Los prototipos también ofrecen cierta flexibilidad en el método que utiliza el


sistema para pasar esos parámetros. RPG emplea dos métodos para pasar
parámetros entre programas:

· Pase por referencia

· Pase por referencia de solo lectura

Pase de parámetros por referencia


Por defecto, RPG pasa un parámetro pasando la dirección de localización de
memoria representada por la variable (a esto se le llama pase por referencia). El
programa llamado después utiliza la dirección de memoria para recuperar y
procesar el valor del parámetro. De hecho, ambos programas comparten la
misma ubicación en memoria. Debido a que ambos comparten la misma
dirección de memoria, si el programa llamado cambia el valor de la variable del
parámetro, el programa llamante reconoce ese cambio cuando el programa
llamado le regresa el control. En el siguiente ejemplo la variable VRESULT está
en blanco antes de hacer una llamada a otro programa:

// PROGRAMA LLAMANTE

// ---- Prototypes

Dcl-pr PROVAL_SQL Extpgm('PROVAL_SQL');

*N Char(10);

*N Char(5);

*N Char(1);

End-pr;

......

VRESULT = *BLANKS;

PROVAL_SQL(VTABLE: PIDSUPPLR :VRESULT); // VALIDATE SUPPLIER CODE

IF VRESULT = '1';

.....

El programa llamado cambia el valor del tercer parámetro (PARM_RESUL):

// PROGRAMA LLAMADO

// ------ Main Prototype/Interface

Dcl-pr PROVAL_SQL Extpgm;


*N Char(10);

*N Char(5);

*N Char(1);

End-pr;

Dcl-pi PROVAL_SQL;

PARM_TABLE Char(10); // TABLE NAME

PARM_CODVA Char(5); // CODE VALUE

PARM_RESUL CHAR(1); // RESPONSE

End-pi;

......

IF Sqlstate = SUCCESS;

PARM_RESUL = '1';

ELSE;

PARM_RESUL = '0';

ENDIF;

Después del retorno la variable VRESULT vale '1' o '0' y esto se debe a que
VRESULT y PARM_RESUL comparten la misma dirección en memoria.

Cuando un programa pasa un parámetro por referencia, el valor del parámetro


debe existir dentro de una variable. Para pasar el parámetro, se nombra la
variable entre paréntesis después del nombre del prototipo durante la
operación de llamada al programa (Callp).
Pase de parámetros por referencia de solo lectura

Mientras que el pase por referencia es el predeterminado, los prototipos


ofrecen un método alternativo de pasar parámetros entre programas:
Referencia de solo lectura. Pasando parámetros por referencia de solo lectura
ofrece varias ventajas sobre el pase de parámetros por referencia:

· Los valores del parámetro no necesitan ser representados en una variable.

· Los tipos de datos del parámetro no necesitan precisamente coincidir con el


prototipo.

· El sistema ofrece protección con respecto al programa llamado porque éste


puede cambiar los valores de los parámetros.

En ocasiones, es posible que usted desee pasar expresiones o literales, en lugar


de variables, a los programas llamados. Esta capacidad le brinda más
flexibilidad a la hora de hacer una llamada a otro programa, porque no se limita
a solo pasar variables. Cuando RPG pasa un parámetro por referencia de solo
lectura, puede primero evaluar una expresión o una literal y luego hacer una
copia temporal de ese valor antes de invocar al programa llamado; después el
programa que llama pasa la dirección de almacenamiento de la copia temporal.
Para especificar una referencia de solo lectura, codifique la palabra clave Const
en el prototipo, en el siguiente ejemplo se llama a un programa pasándole 2
parámetros, el primero pasa la constante 'ABC' y el segundo pasa una variable
(VAR01):

Dcl-pr PRODACT Extpgm('PGM01');

*N Char(9) Const;

*N Packed(7:0) Const;

End-pr;

......

PRODACT('ABC' : VAR01);

......
También se puede codificar una expresión como parámetro al pasar por
referencia de solo lectura, en este ejemplo, el segundo parámetro es una
expresión:

Dcl-pr PRODACT Extpgm('PGM01');

*N Char(9) Const;

*N Packed(7:0) Const;

End-pr;

......

PRODACT('ABC' : VAR02 + 20);

......

Se pueden mezclar métodos de pase en el mismo prototipo, si es necesario.


Cuando se omite la palabra clave Const para un parámetro, el parámetro se
pasa por referencia.

La palabra clave Const le permite cierta flexibilidad al pasar parámetros de


formatos de datos ligeramente diferentes a los que especifica el prototipo. Por
ejemplo, se puede pasar un número entero cuando un prototipo solicita un
campo decimal empacado. Siempre que el valor del campo sea apropiado para
el tipo de datos, el prototipo puede manejar este pequeño desajuste.

Para mejorar la flexibilidad de su codificación y para ayudar a proteger la


integridad de los datos del programa que llama, use la referencia de solo
lectura como el método preferido para pasar parámetros entre programas. Si el
programa que llama necesita acceder a los cambios realizados por el programa
llamado, haga el pase por referencia. O si se requiere hacer un pase de una gran
cantidad de parámetros, se debe hacer el pase por referencia para mejorar el
rendimiento de la llamada.

Sección 3 SQL para IBMi-AS400 (Teoría)

SQL para IBM I - AS/400 con IBM Data Studio


Tipos de datos en SQL para IBM I - AS/400

Se debe asignar un tipo de datos a cada columna de una tabla. El tipo de datos de la
columna determina cómo se almacenan sus valores, cuánto almacenamiento ocupa la
columna y qué tipo de operaciones se pueden realizar en la columna cuando se usa
dentro de un programa. Los tipos de datos de uso común se dividen en tres categorías
generales:

· Carácter

· Numérico

· Fecha

IBM desarrolló un esquema de codificación para permitir que un caracter de datos,


numérico o no numérico, sea representado en una computadora. Este esquema de
codificación, basado en el alfabeto inglés, se llama Código de intercambio decimal
codificado en binario extendido, o EBCDIC (generalmente pronunciado como ibi-si-
dik). EBCDIC asigna un patrón binario único de ocho bits a cada caracter representable.
La A mayúscula, por ejemplo, es 11000001 en EBCDIC, y el dígito 1 se representa
como 11110001. Los cuatro bits situados más a la izquierda se denominan a menudo
bits de zona o de orden superior, y los cuatro bits situados más a la derecha son bits de
orden inferior o dígitos. Debido a que ocho bits constituyen un byte, se necesita un byte
de almacenamiento para almacenar cada caracter en EBCDIC. IBMi (AS/400) almacena
todos los valores de datos no numéricos o de caracteres de esta forma: Un byte
representa un caracter.

Todas las computadoras usan algún tipo de sistema de codificación para representar
caracteres como patrones de bits, pero no todos los sistemas usan EBCDIC, que es
único de algunos sistemas IBM. Es posible que ya esté familiarizado con el sistema de
codificación denominado ASCII (Código estándar americano para el intercambio de
información), generalmente pronunciado como as-kii. EBCDIC se desarrolló por
separado de ASCII. En consecuencia, los patrones de bits en ASCII no son los mismos
que en EBCDIC. Por ejemplo, la letra A en EBCDIC está representada por un hex C1,
pero en ASCII es un hex 41. Además, los dos esquemas utilizan diferentes secuencias
de clasificación; es decir, no clasifican todos los caracteres en la misma secuencia. En
EBCDIC, los caracteres en minúsculas se ordenan antes que los caracteres en
mayúsculas y las letras se ordenan antes de los números (por ejemplo, a, b, c,… A, B, C,
… 7, 8, 9). En ASCII es lo contrario (por ejemplo, 1, 2, 3,… A, B, C,… x, y, z).
Datos tipo carácter

Los datos tipo caracter pueden ser cualquier símbolo que el sistema soporte. Las letras
alfabéticas, los números y los caracteres especiales (por ejemplo, signos de puntuación,
símbolos de moneda) son todos datos de tipo caracter. Una cadena de caracteres es una
secuencia de caracteres. Un programa RPG normalmente procesa datos de tipo caracter
asignando valores de una variable tipo caracter a otra, concatenando (uniendo) cadenas
de caracteres o convirtiendo datos tipo caracter en otro tipo de datos.

Normalmente, cada caracter ocupa un solo byte en memoria. Pero algunos conjuntos de
caracteres usan dos bytes para almacenar cada caracter. Estos se conocen como
conjuntos de caracteres de doble byte y se utilizan con mayor frecuencia en idiomas
como el japonés o el chino que tienen más símbolos de los que se puede representar con
un byte. Las versiones extendidas de ASCII, llamadas Unicode y UCS (Conjunto de
Caracteres universal) también se han aceptado ampliamente. Estos conjuntos de
caracteres más nuevos, que se extienden más allá del alfabeto inglés, también son
conjuntos de caracteres de doble byte y tienen una gama mucho más amplia de
caracteres disponibles que EBCDIC o ASCII.

CHAR

Cuando SQL define una columna como CHAR (o CHARACTER), está definiendo una
cadena de caracteres de longitud fija. El atributo de longitud se especifica entre
paréntesis. Todos los valores de la columna tienen la misma longitud en memoria. Los
valores que no llenan toda la longitud se rellenan con espacios en blanco.

La mayoría de las columnas de caracteres en una base de datos de IBM i tienen una
longitud fija. Cuando no especifica una longitud, la columna es de un byte y la longitud
máxima es 32,766.

VARCHAR

Especificar un tipo de datos de VARCHAR (o CHAR VARYING o CHARACTER


VARYING) define una columna como una cadena de caracteres de longitud variable. El
tamaño máximo se especifica (hasta 32.740) entre paréntesis.

Datos numéricos
SQL define datos numéricos con precisión y escala. La precisión se refiere al número
total de dígitos disponibles en el número sin tener en cuenta ningún signo. La escala
indica cuántos de esos dígitos son fraccionarios (es decir, a la derecha del punto
decimal).

NUMERIC

Especificar un tipo de datos NUMERIC (o NUM) define una columna como un número
decimal con zona (o con signo). Los números decimales con zona toman un byte
completo para almacenar cada dígito de un valor numérico. Por tanto, un número de tres
dígitos ocupa tres bytes. La zona del dígito más a la derecha almacena el signo de los
datos: 1111 (F) representa un valor positivo, mientras que 1101 (D) representa un valor
negativo. La representación por zonas, entonces, es casi idéntica a la representación de
caracteres, excepto que el signo está incrustado en el byte del dígito más a la derecha.

Valor decimal a representar

Bits (3 bytes)

Hex

+156

11110001 11110101 11110110

F1 F5 F6

Valor decimal a representar

Bits (3 bytes)

Hex

–156

11110001 11110101 11010110

F1 F5 D6

DECIMAL
Especificar un tipo de datos DECIMAL (o DEC) define una columna como un número
decimal empacado. Los números empacados toman ventaja de la redundancia que se
crea en la representación de dígitos, en este caso no se almacenan las zonas para los
números. En formato empacado, solo el dígito, o los bits de nivel inferior de un número
se almacenan y el signo se representa con 4 bits adicionales. Estos bits de signo siempre
ocupan las últimas cuatro posiciones de bits de un valor decimal empacado.

Valor decimal a representar

Bits (2 bytes)

Hex

+156

00010101 01101111

156F

Valor decimal a representar

Bits (2 bytes)

Hex

–156

00010101 01101101

156D

Un número de tres dígitos ocupa dos bytes. Puede que esto no parezca una gran
diferencia, pero números más grandes generan un mayor ahorro de memoria: un número
de 63 dígitos en formato empacado ocupa solo 32 bytes.

La mayoría de las columnas numéricas en una base de datos de IBMi son números
decimales empacados. Aunque algunos programadores prefieren definir columnas
numéricas con zona, porque es más fácil imprimir o ver los datos en este formato, la
computadora funciona de manera más eficiente con números almacenados en formato
decimal empacado. Para la mayoría de los procesos numéricos, RPG implícitamente
convierte valores numéricos a formato decimal empacado antes de procesarlos.

INTEGER, SMALLINT, BIGINT

Ya se ha visto que los números decimales empacados son más compactos que los
números decimales con zona. La representación entera es incluso más compacta que la
representación decimal empacada. Los valores enteros se almacenan en formato binario.
A cada bit de una cadena se le asigna un valor decimal, y los valores de los bits se
suman para determinar el valor del signo del número. SQL admite tres tipos de datos
relacionados con números enteros. El que usted use dependerá del rango de valores que
se deben almacenar.

Tipo de Dato

Bytes

Digitos

Valor más bajo

Valor más alto

SMALLINT

-32,768

32,767

Tipo de Dato

Bytes

Digitos

Valor más bajo

Valor más alto


INT (o INTEGER)

10

-2,147,483,648

2,147,483,647

Tipo de Dato

Bytes

Digitos

Valor más bajo

Valor más alto

BIGINT

19

-9,223,372,036,854,775,808

9,223,372,036,854,775,807

Note que no es necesario indicar una precisión o escala. La precisión (10) se establece
por el tipo de datos INT y la escala para todos los enteros es 0.

Tipos de datos de DATE y TIME


Los tipos de datos de fecha y hora no son tipos de datos numéricos ni de caracteres; son
tipos de datos diferentes que la computadora puede reconocer, procesar y manipular
fácilmente como fechas sin requerir codificación o conversión especial.

DATE

Especificar un tipo de datos DATE define una columna como una fecha. Una fecha es
un valor de tres partes, que incorpora un mes, un día y año entre 0001 y 9999. Al definir
una fecha, no es necesario especificar una longitud porque el sistema la determina
automáticamente.

Un programa ILE RPG puede recuperar un valor de fecha y procesarlo. La presentación


de la fecha en el programa se ajusta a cualquiera de los ocho formatos diferentes
utilizados por un programa:

· * ISO utiliza el formato de fecha de la Organización Internacional de Normalización


(ISO)

(aaaa- mm-dd)

· *USA Utiliza el formato de fecha de Estados Unidos (mm / dd / aaaa)

· * EUR utiliza el formato de fecha europeo (dd.mm.aaaa)

· * JIS utiliza el formato de fecha estándar industrial japonés (aaaa-mm-dd)

· * MDY usa el formato de fecha mm / dd / aa

· * DMY usa el formato de fecha dd / mm / aa

· * YMD usa el formato de fecha aa / mm / dd

· * JUL usa el formato de fecha juliano aa / ddd

· *JOB usa el formato especificado para el trabajo (use el comando DSPJOB para
determinar el formato de fecha actual del trabajo).

TIME

La especificación de un tipo de datos Time define una columna como una hora del día,
integrando horas, minutos y segundos. Los datos tipo Time siguen muchos de los
mismos principios que el tipo de datos Date. Al definir un dato tipo Time, no es
necesario especificar una longitud.

Un programa ILE RPG puede recuperar un valor tipo Time y procesarlo. Al igual que
con las fechas, la presentación de la hora en el programa se ajusta a cualquiera de los
cinco formatos de hora diferentes que utiliza un programa:

· * ISO utiliza el formato de hora de la Organización Internacional de Normalización


(ISO)

(hh.mm.ss)

· *USA Utiliza el formato de hora de Estados Unidos (hh: mm xx), donde xx es AM o


PM

· * EUR utiliza el formato de hora europeo (hh.mm.ss)

· * JIS utiliza el formato de hora estándar industrial japonés (hh: mm: ss)

· * HMS usa el formato hh: mm: ss

TIMESTAMP

Un timestamp es una combinación de fecha y hora en una sola variable. La


especificación de un tipo de datos timestamp define una columna tipo timestamp (fecha
y hora en una sola variable). Los tipos de datos timestamp tienen seis o siete partes: año,
mes, día, hora, minuto, segundo y, opcionalmente, segundos fraccionarios.

SQL para programadores en IBM I - AS/400

Creación de tablas

Las declaraciones de lenguaje de definición de datos (DDL) se usan para crear objetos
nuevos, como tablas, y para alterar la estructura o propiedades de los objetos de base de
datos existentes.

Las tablas SQL contienen los datos de la base de datos. Cuando SQL crea una tabla
nueva, la base de datos crea un objeto de tipo archivo físico de un solo miembro en una
biblioteca. Las tablas están organizadas en filas (registros) y columnas (campos). SQL
usa la instrucción Create Table para crear una tabla:

Create table PRODUCTS

( idProduct char(5) Not Null Default primary key,

descript char (30) Default,

idProdTyp char (5) Default,

idSupplr char (5) Default,

UnitPrice numeric (10,2) Default,

idWrehous char (5) Default,

idBraOffi char (5) Default,

entrydte date Default )

Rcdfmt Prodrec

Esta declaración Create Table contiene tres cláusulas principales: (1) el nombre de la
tabla, (2) la estructura de sus columnas (es decir, sus campos) y (3) el nombre del
formato de registro.

Nombre de tabla

La primera cláusula nombra la tabla a crear. El identificador de la tabla debe comenzar


con un carácter alfabético o uno de los caracteres especiales $, # o @. Además de esos
caracteres, los caracteres restantes pueden ser números o un guión bajo (_). Un
identificador de tabla no puede incluir espacios en blanco incrustados. Los nombres de
los objetos IBMi están limitados a 10 caracteres, por lo que se debe limitar el
nombre de la tabla a 10 caracteres.

Definiciones de columna

La segunda cláusula de la declaración Create table enumera todas las columnas que


componen el diseño de la tabla. Las columnas se declaran individualmente dentro de un
único paréntesis. Cada definición de columna incluye al menos el nombre de la columna
y un tipo de datos. Si especifica un nombre de columna de más de 10 caracteres o con
caracteres no válidos, SQL asigna un nombre generado por el sistema.

Los siguientes son los tipos de datos más comunes que se utilizan con IBMi:

· CHAR (carácter de longitud fija)

· VARCHAR (carácter de longitud variable)

· DECIMAL (numérico empacado)

· NUMERIC (numérico con zona)

· SMALLINT (entero de dos bytes)

· INTEGER (entero de cuatro bytes)

· BIGINT (entero de ocho bytes)

· DATE

· TIME

· TIMESTAMP

Además del nombre y el tipo de datos, cada definición de columna puede incluir
cláusulas de restricción opcionales para definir mejor la columna. Una restricción
especifica una regla para los datos de una tabla. Dentro de las cláusulas de restricción de
uso común se incluyen las siguientes:

 NOT NULL
 DEFAULT
 UNIQUE
 PRIMARY KEY

La restricción NOT NULL evita que la columna contenga valores nulos. Un valor nulo
representa la ausencia de datos para una columna. Se le pueden asignar valores nulos a
una columna que permita nulos en lugar de un valor real. La mayoría de las tablas de
IBMi especifican NOT NULL para cada columna, lo que obliga a esa columna a tener
siempre un valor.

La restricción DEFAULT indica que la columna debe contener un valor por defecto. El
valor por defecto se asigna a esa columna para todas las filas nuevas (registros) si no se
especifica ningún otro valor. Por ejemplo, para una columna que incluye la restricción
DEFAULT 99999, SQL asigna un valor de 99999 a esa columna a menos que la
instrucción SQL indique específicamente otro valor. Si la restricción no especifica un
valor por defecto, las columnas numéricas se rellenan con ceros, las columnas de
caracteres se rellenan con blancos y las columnas relacionadas con fechas usan el valor
de la fecha actual. Una columna que permite nulos, independiente de su tipo de datos
tiene un valor por defecto de nulo. Las definiciones de columna que omiten las
cláusulas NOT NULL y DEFAULT implícitamente se consideran con capacidad de
permitir nulos con un valor por defecto.

La restricción UNIQUE garantiza que cada fila en las tablas tenga un valor único para
esa columna. Una regla relacionada, PRIMARY KEY (clave principal), combina las
restricciones NOT NULL y UNIQUE. PRIMARY KEY asegura que una columna (o
combinación de dos o más columnas) contiene un valor único que identifica una fila
específica en la tabla. Una tabla solo puede tener una clave principal. Si la clave
principal consta de una sola columna, puede especificar la restricción de clave principal
en la definición de columna:

Create table PRODUCTS

( idProduct char(5) Not Null Default primary key,

……..

entrydte date Default )

Rcdfmt Prodrec

Si la clave principal es una combinación de dos o más columnas, debe especificar la


restricción de clave principal por separado al final de las definiciones de columna:

Create Table PROD2KEYS

(idProduct char(5) Not Null Default,

idSupplr char (5) Not Null Default,

descript char (30) Default,

idProdTyp char (5) Default,

UnitPrice numeric (10,2) Default,

idWrehous char (5) Default,

idBraOffi char (5) Default,


entrydte date Default,

Primary Key (idProduct, idSupplr))

Rcdfmt Prodrec3

Nombres de formato de registro

Una característica única de la base de datos de IBMi es el requisito de que un formato


de registro (diseño) debe tener un nombre. Además, RPG requiere que el nombre del
formato de registro sea distinto del nombre de la tabla. Para adaptarse a este requisito, la
versión IBMi de SQL hace un cambio al SQL estándar al incluir una cláusula RCDFMT
para nombrar el formato:

Create Table PROD2KEYS

…….

Rcdfmt Prodrec3

Nombres calificados y convenciones de nombres

Uno puede referirse a los objetos por un nombre simple, como PRODUCTS, o por un
nombre calificado. Un nombre calificado de un objeto incluye el nombre del esquema
(biblioteca) en el que se almacena el objeto. Dependiendo de la convención de
nomenclatura que se utilice, un nombre calificado de SQL podría ser:

EGO1L1/PRODUCTS o EGO1L1.PRODUCTS para un objeto almacenado en un


esquema (biblioteca) con nombre EGO1L1. También puede declarar la sentencia Create
Table mostrada anteriormente como:

Create table EGO1L1/PRODUCTS

Create table EGO1L1.PRODUCTS


Para colocar explícitamente la tabla en el esquema (biblioteca) EGO1L1.

SQL - Insertar - Eliminar - Actualizar – Joins

Inserción de datos

Se usa la sentencia INSERT para crear nuevos registros:

INSERT INTO INVENTORY (IDPROD, QUANTITY, INCOINV , OUTINV )

VALUES ('A001',3000,600,200)

Analicemos la sintaxis. La primera parte es INSERT INTO, seguida de una lista de los
nombres de las columnas. La lista de columnas indica para qué columnas se estará
pasando información. En este caso, son las columnas IDPROD, QUANTITY,
INCOINV , OUTINV. La segunda parte de la declaración proporciona el valor para
cada columna. Esto se indica con VALUES seguidos de los datos de las columnas que
necesitan estar enumerados en el mismo orden que la lista de columnas. Tanto los
encabezados de columna como los valores de los datos deben estar entre paréntesis. Los
valores no numéricos deben ir entre apóstrofos. Tal vez desea utilizar la misma tabla
para una declaración de inserción, pero no desea incluir datos para cada columna; en
este caso, excluya la (s) columna (s) de la lista y SQL simplemente insertará un valor
nulo (o un valor por defecto si la columna no permite nulos) en cualquier columna de la
tabla que no esté en la lista del INSERT.

Borrado de datos

Para eliminar una fila de una tabla, use la sentencia DELETE:

DELETE FROM PRODUCTS WHERE IDPRODUCT = 'A8000'

La sintaxis es similar a una declaración SELECT. La declaración dice BORRAR de la


tabla PRODUCTS el registro con un ID de Producto igual a “ A8000". La cláusula
WHERE es muy importante aquí, si no especifica qué registro eliminar y en su lugar
ejecuta esta declaración SQL, todos los registros se eliminarán de la tabla PRODUCTS,
ya que no incluimos un WHERE para limitar qué registros se eliminan.

Nota: La consulta DELETE elimina datos de una tabla, pero no elimina la tabla en sí.
Para eliminar un objeto de base de datos y su contenido, se utiliza la declaración DROP,
por ejemplo, DROP TABLE PRODUCTS.

Actualización de datos
Hay casos en los que uno o más valores de columna necesitan ser actualizados en un
registro. Puede utilizar la sentencia UPDATE para modificar los valores de los registros
en una tabla:

Update INVENTORY

SET

QUANTITY = 5000

WHERE IDPROD = 'A001';

Este UPDATE establece el valor de 5000 para la columna QUANTITY solo para la fila
con un IDPROD cuyo contenido sea “A001". Si no se desea actualizar todos los
registros, asegúrese de incluir la cláusula WHERE. Pueden haber situaciones en las que
se desea actualizar cada registro de una tabla y, en esos casos, no se necesitaría una
cláusula WHERE.

Joins

Si necesita combinar o vincular datos de dos o más tablas diferentes en un solo conjunto
de resultados, puede usar JOIN para extraer datos de dos o más tablas usando un campo
común para vincularlos.

Hay múltiples tipos de JOIN disponibles. Un INNER JOIN devuelve todas las filas de
las tablas vinculadas donde se cumplen los criterios de coincidencia; las filas que no
coinciden no aparecen en el conjunto de resultados. Supongamos que desea obtener el
ID de Producto, la descripción del producto, el precio unitario del producto y el nombre
del proveedor que vende el producto. En el siguiente ejemplo, los campos idSupplr y
idSupp son campos para relacionar las tablas PRODUCTS y SUPPLIERS (llave
primaria y llave foránea), y se puede usar JOIN para vincular las tablas. Esta sería la
sintaxis SQL:

SELECT A.IDPRODUCT AS PROD_ID, -- PRODUCT ID

A.DESCRIPT AS DESCRIPTION, -- PRODUCT DESCRIPTION

A.UNITPRICE AS UNIT_PRICE, -- PRODUCT PRICE

B.SUPPNAME AS SUPPLIER_NAME -- PRODUCT SUPPLIER

FROM PRODUCTS A INNER JOIN SUPPLIERS B

ON A.idSupplr = B.idSupp ;
En la parte del SELECT para cada nombre de columna se tiene un prefijo. Esto indica
de qué tabla recuperar la columna. Por ejemplo, B.SUPPNAME indica que se desea
recuperar la columna SUPPNAME de la tabla SUPPLIERS en la selección de
columnas. Después de SELECT, viene la sintaxis JOIN, que dice: Tomar la tabla
PRODUCTS y unirla con la tabla SUPPLIERS. Las columnas para hacer la
coincidencia son idSupplr y idSupp para este caso y se usan en la cláusula ON en donde
se hace uso del prefijo para indicar la tabla a la que pertenecen.

Hay algunos tipos adicionales de JOINS disponibles:

· LEFT OUTER JOIN: incluye todas las filas de la tabla de la izquierda y las filas de
la tabla de la derecha que tengan coincidencia con filas en la tabla de la izquierda, si hay
filas de la tabla de la izquierda sin coincidencia con filas de la tabla de la derecha, las
columnas de esa tabla (derecha) vendrán con nulos.

· RIGHT OUTER JOIN: incluye todas las filas de la tabla de la derecha y las filas de
la tabla de la izquierda que tengan coincidencia con filas de la tabla de la derecha, si hay
filas de la tabla de la derecha sin coincidencia con filas de la tabla de la izquierda, las
columnas de esa tabla (izquierda) vendrán con nulos.

· FULL OUTER JOIN: incluye filas coincidentes y no coincidentes de ambas tablas.

Temas adicionales de SQL

Creación de vistas con SQL

Las vistas SQL proporcionan formas alternativas de acceder a los datos en una tabla.
Aunque un programa puede recuperar y procesar filas virtuales de una vista, la vista en
realidad no contiene datos. En cambio, almacena una ruta de acceso, una secuencia de
punteros a los datos reales en una o más tablas. Cuando SQL crea una vista nueva, la
base de datos construye un archivo lógico sin claves basado en un archivo físico. Un
programa RPG no hace distinción entre una tabla y una vista cuando procesa un
archivo. El programa puede leer y generalmente actualizar el archivo ya sea éste una
tabla o una vista. Los usos más comunes de una vista son seleccionar ciertas filas (un
subconjunto) de una tabla, seleccionar un subconjunto de columnas de una tabla o
seleccionar una combinación de ambas funciones. La declaración SQL Create View
crea una vista. La ruta de acceso se crea a partir de una sentencia SQL Select integrada
en la instrucción Create View:

CREATE VIEW VIEW01

AS

SELECT A.IDPRODUCT AS PROD_ID, -- PRODUCT ID


A.DESCRIPT AS DESCRIPTION, -- PRODUCT DESCRIPTION

A.UNITPRICE AS UNIT_PRICE, -- PRODUCT PRICE

B.SuppName AS SUPPLIER_NAME, -- PRODUCT SUPPLIER

C.descript AS BRANCH, -- PRODUCT BRANCH LOCATION

D.descript AS WAREHOUSE -- PRODUCT WAREHOUSE LOCATION

FROM PRODUCTS A INNER JOIN SUPPLIERS B

ON A.idSupplr = B.idSupp

INNER JOIN BRANCHES C

ON A.idBraOffi = C.idBraOffi

INNER JOIN WAREHOUSES D

ON A.idWrehous = D.idWrehous

WHERE A.idSupplr = '001'

RCDFMT RECVIEW

La sentencia Create View contiene tres cláusulas principales: (1) el nombre de la vista,
(2) una sentencia Select (más conocido como un fullselect) para seleccionar las filas y
columnas que estarán en la vista, y (3) el nombre de formato de registro de la vista. Un
fullselect es un componente de la instrucción Create View, especifica las filas y
columnas de una o más tablas que componen la vista. El fullselect declara las columnas
que se recuperarán, la tabla de la que se recuperarán y, opcionalmente, una cláusula
Where para especificar los criterios de selección de filas.

Tenga en cuenta que no se puede ordenar una vista, y tampoco el fullselect puede
contener la cláusula Order By.
Modificar una tabla (Alter Table)

La declaración SQL Alter Table realiza cambios estructurales en una tabla, cambiando
su formato. Los cambios que se pueden realizar con Alter Table son los siguientes:

· Agregar / eliminar columnas

· Agregar / eliminar restricciones

· Cambiar atributos de columna

Alter Table table-name

{Add Column column-name modifier}

{Alter Column column-name modifier}

{Drop Column column-name}

{Add Constraint constraint}

{Add Primary Key(column-name)}

{Drop Primary Key}

A continuación, se muestran algunos ejemplos del uso de Alter Table:

Cambio al tamaño de una columna

Alter Table PROD_EXAMP Alter Column UnitPrice

Set Data Type NUMERIC (12,2) Default

Se agrega una columna nueva

Alter Table PROD_EXAMP Add Column CREDIT NUMERIC (10,2) Default

Se elimina un constraint

ALTER TABLE PRODUCTS


DROP constraint fk_idwrehous;

Sección 4 SQL integrado en programas RPG ILE formato Free (Teoría)

SQL - Lenguaje de manipulación de datos

SQL categoriza una serie de declaraciones como lenguaje de manipulación de


datos (DML), mientras que las declaraciones DDL (lenguaje de definición de
datos) trabajan con los objetos de la base de datos en sí. Las declaraciones
DML recuperan y administran los datos dentro de las tablas (archivos físicos).
Las declaraciones DML apropiadas para SQL incrustado en programas ILE RPG
son las siguientes:

 Select
 Insert
 Update
 Delete

Select

La instrucción Select es la instrucción de consulta principal de SQL, se puede


comparar con la operación de Read de RPG, pero con varias mejoras. Una
instrucción Select básica toma la siguiente forma:

Select columns From table {Where conditions} {Order by sequence}

Los siguientes ejemplos ilustran la instrucción Select:

SELECT * FROM PRODUCTS order by IDPRODUCT;

SELECT idProduct AS ID_PRODUCTO, descript AS DESCRIPCION , UnitPrice AS


PRECIO_UNITARIO, idSupplr AS PROVEEDOR FROM PRODUCTS WHERE
idSupplr = '003';

SELECT idProduct AS ID_PRODUCTO, descript AS DESCRIPCION, idSupplr AS


PROVEEDOR

FROM PRODUCTS WHERE idSupplr = '003' ORDER BY descript;


La cláusula Select enumera las columnas que la declaración debe recuperar, o
especifica con un asterisco (*) para recuperar todas las columnas del registro.
La cláusula From indica los archivos (Tablas) de los que se recuperan las
columnas. Una cláusula Where (opcional) puede identificar una condición de
búsqueda para limitar la consulta a los registros que satisfacen una condición.
Finalmente, la instrucción Select puede usar la cláusula Order by para
secuenciar el conjunto de resultados, en un orden específico.

Insert

La instrucción SQL Insert agrega un registro (fila) a una tabla o vista. Se puede
comparar a la operación RPG Write. Una declaración de inserción simple tiene
la siguiente forma:

Insert Into table {(columns)} Values (values)

Este es un ejemplo de la instrucción Insert:

INSERT INTO PRODUCTS (idProduct, descript, idProdTyp , idSupplr ,


UnitPrice , idWrehous , idBraOffi , entrydte)

VALUES ('B002C','Large Desk','005','001',2500.89,'001','001','2017-09-08' );

La cláusula Insert Into nombra la tabla. Después del nombre de la tabla, una
lista de columnas entre paréntesis para las que se proporcionarán los valores.
La cláusula Values proporciona valores para cada una de las columnas de la
lista de columnas. Debe especificar los valores en el mismo orden que la lista
de columnas y debe tener el mismo número de valores que las columnas. La
lista de columnas es opcional. Si lo omite, la instrucción Insert asume que
todas las columnas en el diseño del registro tienen los valores
correspondientes especificados en la cláusula Values. Si una columna tiene
permite nulos o se crea con un valor predeterminado, puede omitirla de la lista.
En ese caso, se utiliza el valor predeterminado o un valor nulo.
Update

El uso de la instrucción Update permite modificar una o más filas en una tabla.
Esta declaración corresponde a la operación Update de RPG combinada con la
función %Fields. La declaración de actualización sigue esta forma:

Update table Set column = value Where conditions

Estos son algunos ejemplos:

UPDATE PRODUCTS SET

descript = 'OFFICE DESK'

WHERE idProduct = 'B002C';

UPDATE PRODUCTS SET

UnitPrice = 7777.75

WHERE idProduct = 'B002C';

Delete

La instrucción SQL Delete elimina uno o más registros (filas) de una tabla, de
forma análoga corresponde a la operación Delete de RPG, esta declaración
sigue esta forma:

Delete From table {Where conditions}

Estos son algunos ejemplos:

DELETE FROM PRODUCTS WHERE idProduct = 'B002C';

DELETE FROM PRODUCTS WHERE UnitPrice = 666.43;


Introducción al SQL incrustado

En los últimos años, se ha convertido en una práctica común insertar


sentencias SQL dentro de un programa RPG para acceder y administrar el
procesamiento de la base de datos. Generalmente, estas declaraciones son
declaraciones DML, pero pueden incluir DDL.

Estas declaraciones SQL pueden complementar o reemplazar por completo las


operaciones de archivos con instrucciones RPG (lectura, escritura,
actualización, eliminación, etc.). Debería considerar insertar sentencias SQL en
sus aplicaciones centradas en RPG por muchas razones. El SQL incrustado es
especialmente útil para el procesamiento de conjuntos a la vez, lo que permite
que un programa RPG actúe sobre muchos registros mediante el uso de una
sola declaración SQL en lugar de un ciclo de archivo iterativo. SQL proporciona
características de base de datos ( Ej., Seguridad a nivel de campo o columna)
que no están disponibles con DDS’s o mediante operaciones de archivos RPG
nativas. SQL tiene un conjunto amplio de funciones (por ejemplo, Avg, Count,
Sum) que las operaciones o funciones de RPG no pueden duplicar
fácilmente. RPG admite la creación dinámica de sentencias SQL en tiempo de
ejecución, lo que permite una gran flexibilidad en la forma en que el programa
procesa la base de datos. Como estándar de la industria, SQL proporciona
coherencia en todas las plataformas de software, formación, documentación
generalizada y colaboración organizada. En algunos casos, SQL puede ofrecer
beneficios de rendimiento sobre las operaciones nativas del lenguaje RPG.

Exec SQL

Para insertar sentencias SQL en un programa RPG, debe utilizar la directiva


Exec SQL para indicar que el resto de la línea es una sentencia SQL, y no una
operación RPG:

Exec SQL sql-statement;

Como ejemplo, la siguiente línea en un programa RPG ejecutará la instrucción


SQL Delete:

Exec SQL DELETE FROM PRODUCTS WHERE idProduct = 'B002C';


Exec SQL no es técnicamente una operación de RPG. En cambio, es una
directiva al compilador, una instrucción que se ejecuta una vez cuando se
compila el programa, no cada vez que se ejecuta el programa. Controla lo que
hace el compilador, no lo que hará el programa. En este caso, la instrucción
Exec SQL indica que se convierta automáticamente la instrucción SQL en
operaciones RPG apropiadas antes de que se cree el programa.

La directiva Exec SQL debe estar en una sola línea, pero la declaración SQL en
sí puede abarcar varias líneas. Al igual que con otras operaciones de RPG,
finaliza la instrucción SQL con un punto y coma (;):

Exec SQL Update PRODUCTS

SET

descript = 'OLD OFFICE DESK',

idProdTyp = '002',

idSupplr = '002',

UnitPrice = 999.99 ,

idWrehous = '002' ,

idBraOffi = '002' ,

entrydte = '2017-09-08'

WHERE idProduct = 'A001';

Si bien se pueden insertar la mayoría de las instrucciones SQL mediante Exec


SQL, no todas las instrucciones están permitidas y algunas instrucciones SQL
requieren modificaciones para funcionar. Sin embargo, hay sentencias SQL que
solo se permiten como sentencias integradas en un programa.

Uso de variables Host


La declaración SQL Update anterior especifica valores explícitos para usarse en
la actualización de la tabla, pero esta forma no es productiva, porque
dependiendo del escenario se tendría que escribir un nuevo programa, o
modificar el programa, cada vez que los valores de las columnas cambien. Para
que la instrucción SQL sea más flexible, se pueden reemplazar los valores
explícitos con variables del programa. Una variable Host es un elemento de
datos que utiliza una declaración SQL como instrumento para sincronizar las
variables del programa RPG con datos de tablas y vistas. Por lo tanto, en lugar
de codificar valores explícitos en la declaración de actualización del ejemplo
anterior, se pueden sustituir los valores con variables tipo host (variables de
programa):

Exec SQL UPDATE PRODUCTS

SET

descript = :PDESCRIPT,

idProdTyp = :PIDPRODTYP,

idSupplr = :PIDSUPPLR,

UnitPrice = :PUNITPRICE ,

idWrehous = :PIDWREHOUS ,

idBraOffi = :PIDBRAOFFI,

entrydte = :PENTRYDTE

WHERE idProduct = :PIDPRODUCT;

El nombre de la variable está precedido por dos puntos (:) y corresponde a una
variable de programa RPG. No es necesario definir la variable por separado
aparte de la variable RPG. Es el medio para permitir que la declaración SQL se
refiera a una variable RPG. Debe declarar la variable RPG con el mismo tipo de
datos y tamaño que la columna de base de datos asociada.

Las sentencias Insert y Delete también pueden usar variables Host para
sustituir valores de columnas:

Exec SQL Insert Into products


Values (:PIDPRODUCT, :PDESCRIPT, :PIDPRODTYP, :PIDSUPPLR,

:PUNITPRICE, :PIDWREHOUS, :PIDBRAOFFI, :PENTRYDTE);

Exec SQL DELETE FROM PRODUCTS WHERE IDPRODUCT = :PIDPRODUCT;

Los nombres de variables Host no pueden comenzar con SQ, SQL, RDI o DSN.
Estos nombres están reservados para el uso de la base de datos.

Select Into

Si bien las sentencias Insert, Update y Delete se pueden insertar en un


programa RPG y pueden utilizar variables Host sin requerir grandes cambios, el
SQL incrustado requiere una modificación en la sentencia Select para recuperar
un conjunto de datos usando variables Host. La variación Select Into recupera
una sola fila y coloca el resultado en variables Host declaradas en el programa:

Exec SQL Select columns Into :host-variables From table {Where conditions};

La cláusula Into enumera las variables del programa en las que se colocará el
resultado de la consulta. Las columnas de la consulta y la lista de variables
Host del programa comparten una correspondencia de uno a uno; es decir,
debe haber una variable Host para cada columna del resultado de la consulta y
deben seguir el mismo orden. La consulta solo puede generar una fila. En
consecuencia, la cláusula Where a menudo refiere a la clave principal de la
tabla. Si el resultado de la consulta incluye más de una fila, SQL devuelve un
código de excepción.

El siguiente ejemplo muestra el uso de Select Into:

Dcl-s PRO_ID Char(5);

Dcl-s PRO_DESCRI Char(30);

Exec SQL SELECT IDPRODUCT, DESCRIPT

INTO :PRO_ID , :PRO_DESCRI // PRO_ID y PRO_DESCRI TENDRAN LOS


VALORES DE LA CONSULTA
FROM PRODUCTS

WHERE IDPRODUCT = 'A001';

Uso de estructuras Host

En lugar de definir variables Host individuales en una declaración SQL, puede


resultarle útil nombrar una única estructura Host. Una estructura Host es una
estructura de datos que se puede usar con la cláusula Into:

DCL-DS PRODATA;

PRO_ID Char(5);

PRO_DESCRI Char(30);

END-DS;

Exec SQL SELECT IDPRODUCT, DESCRIPT

INTO :PRODATA // LOS SUB CAMPOS DE PRODATA TENDRAN LOS


RESULTADOS DE LA CONSULTA

FROM PRODUCTS

WHERE IDPRODUCT = 'A001';

Al igual que con las variables Host, debe existir una correspondencia uno a uno
entre las columnas de la consulta y los subcampos en la estructura Host.

Una estructura de datos Host es especialmente útil para recuperar todas las
columnas de un formato de registro. En este caso, es apropiada una estructura
de datos descrita externamente. Analice el siguiente ejemplo:

Dcl-ds PRODUCTS Ext Qualified End-ds;

// LOS SUB CAMPOS DE PRODUCTS TENDRAN LOS RESULTADOS DE LA


CONSULTA
Exec SQL Select * Into :PRODUCTS From PRODUCTS

Where idProduct = :PARM_PROD;

En este caso PRODUCTS es una estructura de datos descrita externamente,


siguiendo el diseño de registro de la tabla PRODUCTS. Al seleccionar todas las
columnas en la estructura Host PRODUCTS, este segmento de código ha
logrado efectivamente el mismo resultado que la operación nativa CHAIN del
RPG. La estructura de datos es una estructura de datos calificada. Aunque no
es necesario, convertirla en una estructura de datos calificada reduce la
posibilidad de que los nombres de variables en la estructura de datos entren en
conflicto con nombres en otras partes del programa.

Manipulación de valores nulos

Un valor nulo representa la ausencia de datos para una columna; es un valor


desconocido, ni un cero ni un blanco. A una columna con capacidad de valores
nulos se le puede asignar un nulo en lugar de un valor real. La mayoría de las
tablas en el IBMi especifican Not Null para cada columna, lo que obliga a esa
columna a tener siempre un valor. Sin embargo, si una columna acepta valores
nulos, el programa RPG puede detectar un valor nulo en una columna
recuperada a través de SQL.

Esta declaración no recupera ninguna fila con un valor nulo en la columna


DESCRIPT:

Exec SQL SELECT IDPRODUCT, DESCRIPT

INTO :PRO_ID , :PRO_DESCRI // PRO_ID Y PRO_DESCRI TENDRAN LOS


VALORES DE LA CONSULTA

FROM PRODUCTS

WHERE IDPRODUCT = 'A001' AND DESCRIPT IS NOT NULL ;

Manipulación de errores de SQL


Cuando RPG ejecuta una declaración SQL, SQL devuelve al programa varias
herramientas de retroalimentación que se pueden utilizar para diagnosticar los
resultados de la ejecución de la sentencia. Si la instrucción SQL termina con un
error, el programa no se detiene sino que continúa ejecutándose con la
siguiente instrucción, por lo tanto, es importante verificar la respuesta de
diagnóstico antes de continuar con el programa RPG. El Área de comunicación
SQL (SQLCA) es una estructura de datos que contiene códigos de retorno,
subcampos con información valiosa de diagnósticos. No se debe codificar
explícitamente una estructura de datos SQLCA en el programa porque el
precompilador SQL la inserta automáticamente:

Dcl-ds Sqlca;

Sqlcaid Char(8); // (Nulo)

Sqlcabc Int(10); // Longitud de la estructura

Sqlcode Int(10); // Código retorno de SQL

Sqlerrml Int(5); // Longitud de Sqlerrmc

Sqlerrmc Char(70); // Texto de reemplazo del mensaje

Sqlerrp Char(8); // Identificador de producto

Sqlerrd Int(10) Dim(6); // información de diagnostico

Sqlwarn Char(1) Dim(11); // Banderas Warning

Sqlstate Char(5); // Código de retorno de SQL estándar

End-ds;

Dos subcampos adicionales de SQLCA, Sqlwarn y Sqlerrd, son arreglos que


contienen indicaciones de diagnóstico específicas de advertencias o
excepciones que puede haber encontrado una declaración SQL. Sqlwarn es un
arreglo de 11 elementos de indicadores de warning, y cada elemento incluye un
valor que no está en blanco (generalmente una W) si SQL generó un warning.
Sqlerrd es un arreglo de seis elementos que contiene información de
diagnóstico, principalmente relacionada con conceptos avanzados de SQL,
incluidos los cursores. La documentación en línea de IBM proporciona una
explicación más detallada de Sqlwarn y Sqlerrd.

El Área de Comunicaciones SQL (SQLCA) se actualiza cada vez que el


programa ejecuta una declaración SQL. Dos subcampos importantes en esta
estructura de datos son Sqlcode y Sqlstate. Tanto Sqlcode como Sqlstate
señalan el éxito o el fracaso de una declaración SQL, y ambos prueban
condiciones o clases de condiciones específicas. Sqlcode devuelve uno de los
siguientes valores:

Sqlcode = 0 significa que la instrucción SQL se ejecutó correctamente (aunque


pueden haber warnings).

Sqlcode = 100 indica que no se encontró ninguna fila (fin de archivo).

Sqlcode> 0 (pero no 100) indica que la declaración se realizó correctamente,


pero se emitieron warnings.

Sqlcode <0 significa que la instrucción SQL no tuvo éxito.

Cada valor de Sqlcode corresponde a un mensaje de IBMi (AS/400), que se


puede buscar en la documentación en línea en IBM para obtener más detalles.
Por ejemplo, Sqlcode 100 se explica en el mensaje SQL0100 de IBMi, Sqlcode -
0313 corresponde al mensaje SQL0313 y Sqlcode -20447 corresponde al
mensaje SQL20447.

Sqlstate es similar a Sqlcode, pero mientras que los valores de Sqlcode pueden
ser exclusivos de IBMi, los valores de Sqlstate se establecen según los
estándares de la industria. Sqlstate tiene cinco caracteres; los dos primeros
caracteres indican una condición:

Sqlstate que comienza con 00 significa que la instrucción SQL se ejecutó


correctamente, sin errores ni warnings.

Sqlstate que comienza con 01 indica que la declaración fue exitosa, pero se
emitieron warnings.

Sqlstate que comienza con 02 significa que la instrucción SQL no encontró


ninguna fila (fin de archivo).

Cualquier otra clase Sqlstate significa que la instrucción SQL no tuvo éxito.
Los valores de Sqlstate también se detallan en la documentación en línea de
IBM.

Si ocurre un error durante la ejecución de una instrucción SQL, el programa RPG


continúa ejecutando la siguiente instrucción como si no hubiera ocurrido
ningún error. Siempre se debe probar explícitamente con Sqlcode o Sqlstate
para verificar los resultados de la ejecución de una instrucción SQL.

En la documentación anterior, discutimos la definición de constantes para


representar valores literales para apoyar el mantenimiento y mejorar la
documentación del programa. Esta técnica es especialmente útil para
documentar los valores de Sqlcode y Sqlstate de uso frecuente. El siguiente
ejemplo muestra la técnica, utilizando algunos valores específicos de Sqlstate:

//----- CONSTANTS

Dcl-c SUCCESS '00000'; // SUCCESSFUL SQL OPER

Dcl-c EOF '02000'; // SQL RECORD NOT FOUND

Dcl-c NOT_FOUND 'PRODUCT NOT FOUND';

Dcl-c SUCCESSOPE 'OPERATION SUCCESSFUL';

Dcl-c NOTSUCCOPE 'OPERATION UNSUCCESSFUL';

Dcl-c INVSUPPLI 'INVALID SUPPLIER CODE';

Exec SQL Select * Into :PRODUCTS From PRODUCTS

Where idProduct = :PARM_PROD;

IF Sqlstate = EOF;

PARM_MSG = NOT_FOUND;

.....

Exec SQL UPDATE PRODUCTS


SET

descript = :PDESCRIPT,

idProdTyp = :PIDPRODTYP,

idSupplr = :PIDSUPPLR,

UnitPrice = :PUNITPRICE ,

idWrehous = :PIDWREHOUS ,

idBraOffi = :PIDBRAOFFI,

entrydte = :PENTRYDTE

WHERE idProduct = :PIDPRODUCT;

IF Sqlstate = SUCCESS;

PARM_MSG = SUCCESSOPE;

ELSE;

PARM_MSG = NOTSUCCOPE;

ENDIF;

.....

Uso de cursores SQL

El SQL incrustado es una excelente opción para el procesamiento de consultas


SQL (es decir, tratar grupos enteros de filas en una declaración). Select Into
funciona bien para procesar una sola fila de una tabla o vista. Pero a veces, un
programa puede necesitar recuperar varias filas en una consulta y luego
procesar cada una de esas filas individualmente. Para esos casos, a veces
conocidos como procesamiento de una fila a la vez, SQL proporciona un
mecanismo llamado cursor.

Un cursor es una definición en un programa que SQL usa para apuntar y


procesar una sola fila de un grupo de filas de un resultado de una consulta.
Usando un cursor, se puede crear un ciclo para recorrer un grupo de registros,
recuperando y procesando los registros individualmente, de manera similar a la
forma en que la operación Read de RPG recupera un registro a la vez. RPG
requiere cuatro pasos para usar un cursor SQL:

1. Declarar el cursor.

2. Abrir el cursor.

3. Recuperar filas.

4. Cerrar el cursor.

Declarar el cursor (Declare Cursor)

La instrucción Declare Cursor SQL nombra un cursor y lo asocia con una


instrucción SQL Select. El formato básico es el siguiente:

Exec SQL Declare name Cursor For select-statement;

La instrucción Declare Cursor solo puede aparecer en SQL incrustado y es


similar a la instrucción RPG Dcl-f (Declarar archivo). Debe aparecer en el código
fuente antes de cualquier declaración que refiera al cursor. Es una declaración
no ejecutable y debería estar cerca del comienzo del programa. El siguiente
ejemplo muestra el uso de Declare Cursor:

Exec SQL Declare PRODCUR Cursor For

SELECT idProduct, descript, idSupplr

FROM PRODUCTS WHERE idSupplr = :VAR01;

Un cursor SQL siempre está asociado con una instrucción Select para
establecer las filas a recuperar. La instrucción Select no se ejecuta de
inmediato, sino hasta que se abre el cursor. La instrucción Select puede incluir
variables Host y variables tipo indicador, que se sustituyen cuando el programa
abre el cursor.

Las filas de la consulta asociadas con el cursor en el ejemplo siguiente se


pueden actualizar o eliminar. Excepto por algunas restricciones no muy
frecuentes, un cursor SQL permite actualizaciones a menos que se indique lo
contrario. Puede especificarse explícitamente que un cursor permite
actualizaciones con una cláusula de actualización:

Exec SQL Declare PRODCUR Cursor For

SELECT idProduct, descript, idSupplr

FROM PRODUCTS WHERE idSupplr = :VAR01 FOR UPDATE;

En el ejemplo anterior, cualquier columna de la tabla PRODUCTS se puede


actualizar, incluso si no aparece en la lista de columnas del cursor.

Si el programa no actualiza o elimina registros para el cursor, para mejorar el


rendimiento, especifique una cláusula de Read Only en la instrucción Select:

Exec SQL Declare PRODCUR Cursor For

SELECT idProduct, descript, idSupplr

FROM PRODUCTS WHERE idSupplr = :VAR01 FOR READ ONLY;

El siguiente ejemplo especifica un cursor Read Only como Insensitive:

Exec SQL Declare PRODCUR Insensitive Cursor For

SELECT idProduct, descript, idSupplr

FROM PRODUCTS WHERE idSupplr = :VAR01 FOR READ ONLY;

Cuando se abre un cursor insensitive, el sistema crea una copia temporal del
resultado de la consulta y el programa trabaja con esa copia. Un cursor
insensitive no reconoce las inserciones, actualizaciones o eliminaciones
posteriores de éste o cualquier otro programa. El uso de un cursor insensitive
puede mejorar el rendimiento, pero es posible que el programa no siempre lea
los datos más recientes.

Los cursores de los ejemplos anteriores son cursores seriales. La navegación


por el resultado de la consulta es secuencial, de principio a fin. Una vez que el
programa abre el cursor, el programa puede recuperar cada fila solo una vez. El
cursor debe cerrarse y volver a abrirse si se necesita acceder de nuevo a las
filas de la consulta. De forma predeterminada, los cursores son cursores
seriales. Si se Agrega la cláusula Scroll se crea un cursor desplazable:

Exec SQL Declare PRODCUR Scroll Cursor For

SELECT idProduct, descript, idSupplr

FROM PRODUCTS

WHERE idSupplr = :VAR01

FOR READ ONLY;

Un cursor desplazable permite la navegación en cualquier dirección a través de


la consulta (conjunto de registros) y permite recuperar una fila varias veces sin
cerrar y volver a abrir el cursor.

Abrir el cursor (Open)

Para acceder a las filas de la consulta de un cursor, el programa debe abrir el


cursor:

Exec SQL Open PRODCUR;

La apertura de un cursor procesa la instrucción Select incrustada en el cursor y


hace que las filas de la consulta estén disponibles para el programa. Si la
instrucción Select del cursor incluye variables Host o variables tipo indicador,
sus valores se sustituyen en la instrucción cuando se abre el cursor.

Recuperar filas (Fetch)

El equivalente de SQL incrustado a la operación de lectura Read de RPG es


Fetch. Recordemos que la instrucción Select Into recupera una sola fila de una
consulta en variables Host del programa. La instrucción Fetch recupera una o
más filas de una consulta (conjunto de registros) que contiene varias filas.
Luego, el programa puede crear un ciclo que procese las filas de manera similar
a la forma en que se procesan los archivos usando operaciones RPG. A la fila
que se está procesando se le llama fila actual.

Una instrucción Fetch lee la siguiente fila de un conjunto de registros y usa


variables Host. Este es el formato:

Exec SQL Fetch {Next} {From} cursor-name Into :host-variables;

Fetch requiere que nombre el cursor asociado con la consulta que desea
recuperar, y el cursor debe haber sido declarado y abierto previamente.
También debe nombrar las variables y estructuras que recibirán los valores de
las columnas. Este es un ejemplo:

Exec SQL Fetch PRODCURSOR Into :VDESCRIPT, :VIDPRODUCT;

La instrucción Select asociada con el cursor especificado dicta el contenido de


la consulta.

La instrucción Fetch también puede referirse a una estructura Host, utilizando


la misma sintaxis:

Dcl-ds PRODUCTS Ext Qualified End-ds;

Exec SQL Declare PRODCURSOR Cursor For

Select *

From PRODUCTS

Where idSupplr = :VAR02

For Read Only;

Exec SQL Fetch PRODCURSOR Into :PRODUCTS;


Así como la operación de lectura (Read) de RPG tiene varias formas (Read,
Readp, etc), la instrucción SQL Fetch tiene múltiples variaciones:

Fetch {Next} {From} cursor-name Into host-variables

Fetch Prior From cursor-name Into host-variables

Fetch First From cursor-name Into host-variables

Fetch Last From cursor-name Into host-variables

Fetch Current From cursor-name Into host-variables

Fetch Relative n From cursor-name Into host-variables

La opción predeterminada, y la única disponible para un cursor serial, es Fetch


Next. Para los cursores tipo Scroll, Fetch Prior recupera la fila anterior, Fetch
First y Fetch Last procesan la primera y la última fila, Fetch Current procesa la
fila actual nuevamente y Fetch Relative lee una fila que esta n filas antes o
después de la fila actual.

Otras dos variantes de Fetch colocan el cursor antes de la primera fila o


después de la última fila en el conjunto de la consulta.

Fetch Before From cursor-name

Fetch After From cursor-name

Fetch Before y Fetch After no mueven columnas a una variable Host. En su


lugar, colocan el cursor al principio o al final de la consulta. Para mover la
primera o la última fila a variables Host, después del Fetch Before debe usar
Fetch Next y en el caso del Fetch After debe usar Fetch Prior. En este sentido,
estas variaciones de Fetch son similares a Setll y Setgt de RPG.

Cerrar el cursor (Close)

La declaración Close cierra un cursor abierto:

Exec Sql Close PRODCURSOR;


Una vez que el programa ha procesado las filas en un cursor, debe cerrar
explícitamente el cursor. Cerrar un cursor descarta explícitamente el conjunto
de la consulta y libera bloqueos en tablas o vistas. Si el programa no cierra un
cursor, puede permanecer abierto para programas posteriores. Es necesario
cerrar y luego volver a abrir un cursor serial para volver a procesar el cursor.

Creación de sentencias SQL dinámicas

Hasta ahora, todas las declaraciones de SQL incrustado que hemos discutido
han sido SQL estático. Con SQL estático, la estructura básica de cada
instrucción SQL se conoce en el momento de la compilación del programa. La
declaración SQL puede usar variables Host para sustituir valores en tiempo de
ejecución, pero su propósito general y construcción no cambian una vez que se
crea el programa.

Alternativamente, un programa RPG puede construir una declaración SQL


completa como una cadena de caracteres usando datos del programa. La
instrucción SQL dinámica resultante se prepara en tiempo de ejecución, en
lugar de cuando se crea el programa. El SQL dinámico no utiliza variables Host.
En cambio, la sentencia sustituye valores de parámetros en posiciones
estratégicas en la declaración de la sentencia. El SQL estático generalmente
funciona más rápido, pero el SQL dinámico puede ser más flexible y puede
superar algunas limitaciones del SQL estático. Es posible que el programador
no siempre conozca la composición completa de las declaraciones SQL
requeridas, el programa puede aceptar entradas de usuario para determinar la
naturaleza de la estructura de la declaración SQL, o el programa puede requerir
acciones alternativas basadas en condiciones en tiempo de ejecución. En estas
situaciones, el SQL dinámico es la opción más recomendada.

SQL dinámico incrustado requiere dos pasos adicionales:

1. Preparar (Prepare) la declaración a partir de los datos del programa.

2. Ejecutar (Execute) la declaración preparada.


Los programadores utilizan con frecuencia SQL dinámico para construir un
cursor cuando se desconoce la naturaleza exacta del cursor en el momento de
la creación del programa. Por ejemplo, un cursor puede usar diferentes tablas
en diferentes días o meses, o el usuario puede ingresar datos en el programa
para seleccionar la tabla y las columnas a incluir.

Prepare

La instrucción SQL Prepare valida una cadena de texto y la traduce a una


instrucción SQL ejecutable. El programa puede construir la cadena de texto
utilizando técnicas de procesamiento de caracteres con RPG, como la
concatenación. La instrucción Prepare le permite nombrar la instrucción SQL
preparada. Posteriormente, el programa ejecuta la declaración preparada
refiriéndose a su nombre. La declaración Prepare toma esta forma:

Exec SQL Prepare statement-name From :text-string;

La cadena de texto no puede contener una instrucción Select a menos que la


instrucción se utilice para preparar un cursor SQL.

Los programadores suelen utilizar SQL dinámico junto con cursores SQL. En el
siguiente ejemplo, la instrucción Prepare traduce el contenido de la variable
SQLSTRING, creando SQLcursor, que luego se utiliza para proporcionar la
instrucción Select incrustada para el cursor PRODCURSOR:

Dcl-s SQLSTRING CHAR(256); // se usa en sentencias SQL dinámicas

Dcl-s WHERECLAUSE CHAR(256); // se usa en sentencias SQL dinámicas

......

IF Find <> *blanks;

WHERECLAUSE = ' where descript Like ' + '''%' + %tRIM(FIND) + '%''' ;

SQLSTRING = 'Select descript, idProduct ' +


' From PRODUCTS' + WHERECLAUSE +

' Order by descript';

FLAG_CURSO = 1;

ELSE;

SQLSTRING = 'Select descript, idProduct ' +

' From PRODUCTS ' +

' Order by descript';

FLAG_CURSO = FLAG_CURSO + 1;

ENDIF;

IF Find <> *blanks OR FLAG_CURSO < 3; // dependiendo de una condición se


abre y se vuelve a abrir el cursor

Exec Sql Close PRODCURSOR; // Close cursor

Exec SQL Prepare SQLcursor From :SQLSTRING;

Exec SQL Declare PRODCURSOR Scroll Cursor For SQLcursor;

Exec SQL Open PRODCURSOR; // Open Cursor

Find = *Blanks;

ELSE;

Exec Sql Fetch Before From PRODCURSOR; // posiciona el cursor en el inicio


del resultado de la consulta

ENDIF;
....................

Exec SQL Fetch PRODCURSOR Into :VDESCRIPT, :VIDPRODUCT;

En ejemplo anterior el programa RPG puede usar las variables VDESCRIPT y


VIDPRODUCT.

La cadena de texto no puede contener variables Host, pero puede incluir


marcadores de parámetros para designar dónde debe ocurrir la sustitución de
valores en la instrucción SQL; un marcador de parámetro se indica con un signo
de interrogación (?). El marcador de parámetro puede aparecer donde se
requiera que aparezca una variable Host si la sentencia SQL fuera una
sentencia SQL estática, por ejemplo, en una cláusula Where:

DELETE FROM PRODUCTS WHERE idProduct = ?;

Cuando el programa posteriormente ejecuta la instrucción preparada, sustituye


un valor para cada marcador de parámetro, de forma similar a como una
instrucción SQL estática sustituye el valor de las variables Host.

Execute

Una vez que se ha preparado una sentencia, el programa puede ejecutar esa
sentencia muchas veces utilizando la instrucción SQL Execute. Si la declaración
incluye marcadores de parámetros, el programa puede sustituir un valor
diferente cada vez que se ejecuta la sentencia. La instrucción Execute toma
esta forma:

Exec SQL Execute statement-name {Using :host-variables};


Veamos este ejemplo:

Dcl-s SQLSTRING Varchar(256);

........

SQLSTRING = 'DELETE FROM PRODUCTS WHERE idProduct = ?';

Exec SQL Prepare SQLBORRA from :SQLSTRING;

.......

Exec SQL Execute SQLBORRA Using :VAR01;

En este fragmento de código, la declaración SQLBORRA preparada incluye un


marcador de parámetro. Cada vez que el programa ejecuta SQLBORRA,
sustituye el valor actual de idProduct en lugar del marcador de parámetro.

Opciones de entorno SQL

Set Option

La instrucción SQL Set Option establece las opciones de proceso que se


pueden usar en un programa que utiliza SQL incrustado. La instrucción Set
Option tiene la siguiente forma:

Exec SQL Set Option option = value, …;

Si lo usa en un programa, la instrucción Set Option debe codificarse antes que


cualquier otra instrucción SQL (es decir, debe ser la primera directiva Exec SQL
en el programa).

Las siguientes son algunas opciones comunes:

Exec SQL Set Option Alwcpydta = *Yes,

Closqlcsr = *Endpgm,
Commit = *None;

Alwcpydta

La opción Alwcpydta (Permitir copia de datos) indica si se puede utilizar una


copia de los datos con una instrucción Select para mejorar el rendimiento. El
uso de una copia temporal puede mejorar el rendimiento, pero el programa
puede correr el riesgo de usar un conjunto de resultados obsoleto si alguien
realizó cambios mientras el programa se está ejecutando.

Estas son las opciones:

Alwcpydta (*Optimize) permite que el sistema decida si el uso de una copia


mejorará el rendimiento.

Alwcpydta (*Yes) permite usar una copia con declaraciones Select.

Alwcpydta (*No) prohíbe el uso de una copia con declaraciones Select.

Closqlcsr

Closqlcsr (Cerrar cursor de SQL) especifica cuándo cerrar los cursores de SQL
si el programa no los cierra explícitamente con una instrucción Close. Esta
opción también determina el alcance de las sentencias SQL preparadas y los
bloqueos de archivos. Estas son opciones comunes:

Closqlcsr (*Endpgm) cierra los cursores y los recursos relacionados cuando


finaliza el programa.

Closqlcsr (*Endsql) permite que los cursores permanezcan abiertos entre


instancias de programas en un trabajo, sin tener que volver a abrirlos cada vez
que se llama al programa, hasta que el programador los cierre explícitamente.

Closqlcsr (*Endjob) permite que los cursores permanezcan abiertos hasta que
finalice el trabajo en el IBMi (AS/400).

Para los programas modulares ILE, Closqlcsr (*Endmod) y Closqlcsr


(*Endactgrp) cierran a su vez los cursores cuando finaliza un módulo o un
grupo de activación.
Independientemente de la opción Closqlcsr, solo se puede hacer referencia a un
cursor mediante sentencias dentro del mismo programa en el que se declara.
Un programa no puede utilizar un cursor definido en otro programa.

Commit

El control de compromiso es una característica de la base de datos que permite


procesar transacciones complejas bajo el esquema de todo o nada (es decir, si
se requieren varias actualizaciones de la base de datos para una transacción,
entonces deben ocurrir todas las actualizaciones, o ninguna de ellas lo hará). El
control de compromiso también permite a un programa revertir transacciones
incompletas. SQL normalmente asume que el control de compromiso está en
efecto para cualquier declaración de SQL. Si la base de datos no usa control de
compromiso, entonces el programa debe especificar Commit (* None). Pero si
la base de datos usa el control de compromiso, entonces esta opción puede
establecer un nivel diferente de SQL que se deberá usar.

Creación de programas SQLRPGLE

Para crear un programa RPG que incorpore declaraciones SQL, se puede utilizar
un editor, como SEU o LPEX, para ingresar el código en un miembro de archivo
fuente. Sin embargo, a diferencia de los programas anteriores, que usaban un
tipo de miembro de RPGLE, si el programa incluye sentencias de SQL, el tipo de
miembro debería ser SQLRPGLE. Una vez que el miembro fuente contiene todo
el código requerido, se usa el comando de compilador CRTSQLRPGI (Crear
objeto RPG ILE SQL), para compilar el fuente y crear el programa (o módulo o
programa de servicio).

El comando CRTSQLRPGI emplea un precompilador para traducir los


segmentos SQL del código fuente antes de compilarlo. El precompilador
convierte cualquier declaración SQL en código RPG equivalente que llama a
funciones de base de datos y crea un miembro fuente temporal. Si el
precompilador traduce correctamente el código SQL, el comando compila el
miembro fuente temporal y crea el objeto resultante.

Una vez que el programa se haya creado correctamente, se puede ejecutar


como cualquier otro programa mediante el comando CALL.
Sección 5 Creación de pantallas en IBMi-AS400 (Teoría)

ARCHIVOS DE PANTALLA EN IBMi / AS/400

Una pantalla en AS/400 está compuesta de código que se conoce como DDS’s. En
versiones anteriores del AS/400 estas sentencias se usaron para construir las bases de
datos en donde las tablas se conocían como archivos físicos y las vistas se conocían
como archivos lógicos, además las DDS’s se usaron para crear archivos de pantalla y de
impresión que se conocen como archivos de dispositivo.

Hoy en día las DDS’s se continúan usando, pero con orientación a crear archivos de
dispositivos de pantalla o de impresión, siendo las pantallas las de más uso.

Una pantalla en AS/400 está compuesta de tres tipos de definiciones, una definición
corresponde a nivel de archivo y sus definiciones aplican para todos los formatos de
pantalla dentro del archivo, es como el contenedor para todo lo que se defina en relación
a formatos de pantalla, otro tipo de definición corresponde a formatos de pantalla y el
termino formato lo podemos asociar con una pantalla y la tercera definición corresponde
a las constantes y los campos que componen los formatos de pantalla.

La interacción entre un usuario y un programa interactivo se logra mediante archivos de


pantalla, éstos permiten que los valores ingresados por el usuario lleguen como datos a
los programas.

Los archivos de pantalla se definen externamente al programa y luego se compilan para


crear un tipo de objeto de archivo de pantalla, el cual se puede usar dentro de un
programa.

Como hemos dicho, los archivos de pantalla están compuestos de código DDS, el cual
se podría codificar usando algún tipo de editor, sin embargo en este curso esa no va a
ser la orientación porque las pantallas las desarrollaremos usando la herramienta
Rational Developer for i (RDI) en forma gráfica y la gran ventaja es que la herramienta
crea el código en forma automática, en algunos momentos veremos el código y lo
usaremos para la compilación.

Los formatos de pantalla establecen que campos serán leídos, que campos serán para
despliegue o que campos serán para ambos fines.

Los campos de los formatos de pantalla pueden estar conectados a campos de una tabla ,
o pueden ser campos independientes de la tabla.

En los programas de pantalla (interactivos) existen dos formas de retornar control al


programa, una es por medio de la tecla ENTER y la otra es por medio de teclas de
función u otras que luego veremos.

Para lograr la comunicación por medio de las teclas de función se hace uso de
INDICADORES, los indicadores fueron muy usados en versiones anteriores del
lenguaje RPG y los podemos comparar con el concepto de variable booleana, en donde
un indicador solo puede tener dos estados (encendido y apagado), los indicadores son
valores que están dentro del rango (01 - 99), actualmente se siguen usando dentro de los
archivos de pantalla, por ejemplo la tecla de función F3 se puede asociar con el
indicador 03, de tal forma que cuando el usuario pulse F3 el indicador 03 pasara a un
estado de ENCENDIDO y este estado se usara en un programa para tomar una decisión.

A nivel de archivo de pantalla se puede declarar una palabra clave que permite usar una
estructura de datos de 99 bytes para organizar los 99 indicadores en posiciones relativas
y permite referirse a ellos por un nombre significativo en lugar de usar un número.

CREACION DE PANTALLAS CON ARCHIVOS SUBFILE

Un Subfile es una colección de registros que se puede manipular en una operación de


entrada o salida en pantalla, esta colección de registros será el resultado de una lectura a
alguna tabla o vista de la Base de Datos.

Generalmente la implementación consiste en desplegar una lista de registros y el usuario


puede seleccionar alguno y en ese momento se hace un llamado a otro programa que se
encarga de manipular la operación deseada (Consulta, actualización o borrado), también
la implementación debe permitir la inclusión de un nuevo registro.

La creación de un Subfile debe contener dos formatos de pantalla, uno describe los
campos de la lista a desplegar y el otro controla el comportamiento de la lista.

CARACTERISTICAS DEL FORMATO DE PANTALLA PARA LA LISTA


DESPLEGABLE (SUBFILE)

Para identificar el formato de pantalla tipo Subfile se usa la palabra clave SFL (Subfile),
también se definen los campos (columnas) a desplegar, se debe definir un campo (que
generalmente no es visible) para retener el valor llave de los registros que se despliegan
y usarlo para recuperar un registro que el usuario seleccione para una operación
adicional (Consulta, actualización etc.).

CARACTERISTICAS DEL FORMATO DE PANTALLA DE CONTROL DEL


SUBFILE

El formato de control debe definirse inmediatamente después del formato de la lista del
Subfile. Este registro controla el despliegue de los registros de la lista del Subfile a
través de palabras clave a nivel de registro, además se usa para incluir encabezados e
información adicional al usuario.
PALABRAS RESERVADAS PARA CONTROLAR SUBFILES

Para poder controlar el despliegue de registros en pantalla en una lista (Subfile) se


requieren las siguientes palabras reservadas:

SFLCTL (Subfile Control): Identifica al formato de pantalla que servirá como control


para el formato de pantalla que desplegará una lista de registros (Subfile).

SFLDSP (Subfile Display): Se asocia con un indicador que permite que se despliegue la


lista de registros (Subfile) si el indicador está encendido.

SFLPAG (Subfile Page): Define la cantidad de registros que se despliegan en pantalla


en una operación de salida.

SFLSIZ (Subfile Size): Define el número de registros que se cargan en memoria para


un Subfile, este valor debe ser mayor o igual que el valor de la palabra SFLPAG
(Subfile Page), el tamaño de un Subfile no puede superar 9,999 registros.

SFLDSPCTL (Subfile Display Control): Se asocia con un indicador y si éste está


encendido se despliegan los campos y/o contantes que contenga el formato de pantalla
de control del Subfile.

SFLCLR (Subfile Clear): Se asocia con un indicador y sirve para limpiar el Subfile si


el indicador está encendido. Esta operación es opuesta a las operaciones de SFLDSP y
SFLDSPCTL (desplegar la lista del Subfile y desplegar el formato de control del
Subfile).

SFLEND (Subfile End): Se asocia con un indicador y sirve para controlar el despliegue


de un signo “+” o la palabra “More...” en la parte inferior derecha de la pantalla para
indicarle al usuario que hay más registros para desplegar por medio de las teclas de
paginación (Page Up y Page Down).

OVERLAY: Esta palabra sirve para poder desplegar otros formatos de pantalla
adicionales a la lista Subfile y el Formato de Control para dar más información al
usuario, por ejemplo el uso de las teclas de función.

PAGEDOWN: También se asocia con un indicador que sirve para detectar si el usuario
presionó la tecla de paginación (Page Down).

SFLRCDNBR (Subfile Record Number): Esta palabra reservada funciona a nivel de un


campo que se declara en el formato de pantalla de control para un Subfile, y sirve para
llevar control de la cantidad de registros que se han cargado en memoria pare el Subfile
y sobre las páginas que se han desplegado, entonces se usa para saber cual es la
siguiente página a desplegar o para localizar un registro en el Subfile por posición
relativa dentro del Subfile, el campo debe ser numérico de 4 dígitos con signo, sin
decimales y no hace falta que sea visible.
Sección 6 Programación ILE en IBMi-AS400 (Teoría)

Lenguaje RPG ILE para AS/400 - IBMi en formato Free usando sentencias SQL
para manipular las tablas de la Base de Datos

PROGRAMACION CON MODULOS EXTERNOS

En las lecciones anteriores trabajamos bajo un enfoque de programación modular, pero


en términos de llamadas a programas ejecutables en forma separada.

En este entorno, los programas de llamada y los programas llamados se puede decir que
son Programas completos en sí mismos, habiendo sido compilados individualmente.
Esta técnica se denomina llamada de programa dinámica. Con esta técnica, cuando
un programa hace un llamado a otros programas, el sistema busca el programa llamado
de forma dinámica, durante el tiempo de ejecución, utilizando un proceso interno
conocido como resolución. El programa que realiza la llamada depende de la existencia
del programa llamado, no hay dependencia en el momento de compilación, sino en el
momento de ejecución. Los programas nunca están conectados físicamente, pero pueden
ejecutarse entre sí en tiempo de ejecución.

Si una aplicación requiere muchas llamadas (hablemos de cientos o incluso miles de


veces), esta resolución dinámica lleva tiempo y puede degradar el rendimiento del
sistema; en algunos casos, gravemente.

Cuando IBM introdujo ILE en el AS/400 o IBMi, incluyó la opción de conectar


módulos RPG antes del tiempo de ejecución. Este enlace estático alivia parte de la
degradación del rendimiento de las llamadas a programas dinámicos. Con el enlace
estático, el componente que llama y el componente llamado se unen en un solo objeto
tipo (*PGM). El proceso de resolución ocurre una vez, cuando se crea el programa por
primera vez en tiempo de compilación y se comporta como un programa completo
cuando se ejecuta.

Introducción a Procedimientos

El concepto de procedimiento o método es fundamental para el modelo de enlace


estático. Un procedimiento RPG es una colección autocontenida e identificable de
declaraciones RPG, que realiza una tarea específica y regresa el control al punto de
llamada.

Los procedimientos se pueden crear independientemente del resto de los programas que
los procesarán. Se pueden codificar en miembros fuente separados y compilarlos en
módulos separados, para luego vincularlos cuando se cree un programa.
Los procedimientos introducen el concepto de variables locales en RPG. Las variables
locales se reconocen solo dentro del procedimiento en el que están definidas. Los
valores de datos se comunican de un procedimiento a otro a través de parámetros.

La codificación de procedimientos permite diseñar y crear programas de forma modular.


La programación modular permite la capacidad para reutilizar código, simplifica el
mantenimiento y las pruebas de los programas, y acelera los tiempos de compilación, al
permitir trabajar con secciones de código más pequeñas en lugar de grandes programas
de cientos o miles de líneas de código.

Los conceptos de diseño modular también fomentan la división del trabajo entre varios
programadores en un equipo, lo que permite que cada uno trabaje en partes separadas
del mismo programa para hacer el mejor uso de los recursos.

En las lecciones anteriores, todos los programas que hemos presentado y discutido
contienen, un procedimiento principal. El procedimiento principal es el primer punto de
entrada que se ejecuta cuando se inicia el programa; y es el controlador de la ejecución
del programa, este concepto es importante tenerlo presente para los cambios que vamos
a presentar en donde hacemos uso del enlace estático.

Módulos NOMAIN

La arquitectura ILE permite codificar una miembro fuente que consta de un segmento
de código sin un procedimiento principal. El módulo Nomain resultante contiene solo
procedimientos o métodos (uno o más) que se pueden combinar con otros módulos para
crear un programa. Por sí mismo, un módulo Nomain no puede crear un objeto de
programa. Uno de los módulos de un programa debe tener un procedimiento
principal, es decir con un punto de entrada.

Un módulo Nomain debe incluir una instrucción Ctl-opt con la palabra clave Nomain en
la sección de opciones de control.

Ejemplo: Ctl-opt Nomain;

Esta palabra clave le dice al compilador que el código fuente no contiene un


procedimiento principal. El código solo consta de declaraciones y procedimientos.

Otro cambio que se debe incluir en un módulo es la declaración del concepto de


procedimiento, que es lo que le dice al compilador que este segmento de código es un
procedimiento o método que puede recibir parámetros y devolver resultados en forma
de Función o vía parámetros, la declaración debe contener un inicio y un final. Existen
dos tipos de procedimientos, los internos que son métodos que solo se pueden usar
dentro de un programa y los externos que se pueden encapsular en programas sin tener
que hacer una definición interna. Para declarar un procedimiento externo se debe incluir
la palabra clave EXPORT en la declaración.

Ejemplo:

Dcl-proc MODVAL_SQL Export;

End-proc MODVAL_SQL;

Al igual que con los programas que hemos visto hasta ahora, los procedimientos
también requieren prototipos e interfaces para establecer la comunicación.

Ejemplo:

// ---------------- CONTROL OPTIONS SECTION ---------------

ctl-opt Nomain ;

// --------------- PROTOTYPES ------------------------------------

/COPY EGO1L1/QSOURCES,PROTOTYPES

// PROCEDURE MODVAL_SQL - VALIDATES PRODUCTS TABLE CODES

Dcl-proc MODVAL_SQL Export;

//---------------------------- INTERFACE ----------------------------------

Dcl-pi MODVAL_SQL;

PARM_TABLE Char(10) VALUE; // TABLE NAME

PARM_CODVA Char(5) VALUE; // CODE VALUE

PARM_RESUL CHAR(1); // RESPONSE

End-pi;
.

End-proc MODVAL_SQL;

Los módulos Nomain permiten al programador ILE RPG aprovechar al máximo los
conceptos de programación modular. El código que es común a muchos programas
puede aislarse como un procedimiento en un módulo Nomain y luego reutilizarse
muchas veces por cualquier programa que lo necesite. Además, los módulos Nomain
pueden eliminar el código redundante en una aplicación, haciendo que la aplicación sea
más fácil de mantener y más confiable en comparación de si la aplicación tuviera
múltiples copias del mismo código dispersas entre muchos programas.

Los módulos Nomain también pueden ayudar a las empresas para centralizar las
funciones de la aplicación en una biblioteca común de módulos para su uso y
reutilización en toda la organización.

Creación de programas modulares

Con la introducción de la arquitectura ILE, el sistema ahora permite, y en ocasiones


requiere, que se separen los pasos de compilación y enlace.

CRTRPGMOD (Create RPG Module) => Compilación del modulo

CRTPGM (Create Program) => Creación de un programa ejecutable que se enlaza con
módulos

Cuando se compila código fuente con el comando CRTRPGMOD, el compilador crea


un objeto tipo (*Module), en lugar de un objeto de programa ejecutable (*Pgm). Un
módulo contiene código ejecutable compilado, pero no se puede ejecutar un módulo por
sí mismo; su propósito es ser un bloque de construcción que se pueda usar dentro de un
programa. Para formar el programa ejecutable, se debe realizar un procedimiento de
vinculación por separado.

En el proceso de vinculación se copia físicamente el código compilado de los módulos


en el objeto del programa, este modelo de vinculación estática se denomina vinculación
por copia.

Los programas ILE pueden comprender código de muchos objetos de módulos.


Además, esos módulos pueden haber sido escritos usando cualquier lenguaje ILE (por
ejemplo, ILE COBOL, ILE CL, ILE C o ILE C ++), por lo que una parte de un
programa ILE podría usar ILE RPG mientras que otra parte podría usar ILE C ++. Pero
uno de los módulos en un programa debe incluir un procedimiento principal, los otros
pueden ser módulos de Nomain.

Uso de Directorios de enlace

Un directorio de enlace es un objeto (tipo *Bnddir) que contiene una lista de los
módulos que el comando CRTPGM puede necesitar para crear un programa ILE. El
comando de enlace CRTPGM puede hacer referencia a uno o más directorios de enlace
para encontrar el código necesario para completar el proceso de enlace.

El directorio de enlace no incluye código; simplemente nombra los objetos tipo


(*Module) donde el proceso de enlace puede encontrar el código. El código compilado
permanece en los propios módulos.

El comando CRTBNDDIR (Create Binding Directory) crea un objeto tipo (*Bnddir)


que corresponde a un directorio de enlace. Después de crear el directorio de enlace, se
puede utilizar el comando WRKBNDDIRE (Trabajar con entradas de directorio de
enlace) para agregar o eliminar entradas en el directorio cuyo contenido serán módulos
NOMAIN, el directorio de enlace se puede ver como un repositorio de módulos.

Para utilizar el directorio de enlace al crear un programa, debe declararlo cuando ingrese
el comando CRTPGM.

Esta seria la forma de crear un programa para que incluya métodos de un directorio de
enlace:

CRTPGM PGM(nombre-programa) +

BNDDIR(nombre-binder)

Cuando el proceso de vinculación encuentra un procedimiento exportado en uno de los


módulos enumerados en el directorio de enlace, vincula ese módulo copiándolo en el
programa como si se hubiera definido explícitamente en el programa. Ignorará cualquier
módulo en el directorio de enlace que no necesite. Una vez que se enlazan todos los
módulos enumerados se crea el programa. Si no se pueden encontrar todos los
procedimientos necesarios, fallara el proceso de creación del programa.

Pase de parámetros por valor


En las lecciones anteriores se explicó sobre el pase de parámetros por referencia, pero
ahora vamos a explicar el pase de parámetros por valor.

Cuando un procedimiento pasa un parámetro por valor, no comparte direcciones de


memoria con el procedimiento llamado. El programa llamante pasa el valor del
parámetro, pero no una dirección de memoria.

El procedimiento llamado puede realizar cambios en los parámetros si es necesario para


su propio procesamiento, pero el programa o método llamante no reconocerá esos
cambios, en este caso los valores en memoria del programa llamante estarán protegidos.

Para pasar un parámetro por valor, se codifica la palabra clave Value en la interfaz y en
el prototipo del procedimiento.

Ejemplo:

Dcl-pi MODVAL_SQL;

PARM_TABLE Char(10) VALUE; // TABLE NAME

PARM_CODVA Char(5) VALUE; // CODE VALUE

PARM_RESUL CHAR(1); // RESPONSE

End-pi;

Programas de servicio

Un programa de servicio es un contenedor de subprocedimientos (también referidos


como procedimientos), es como una especie de caja de herramientas de código que
muchos programas pueden usar sin tener que hacer una copia de los subprocedimientos
en ellos. Este modelo de creación de programas se denomina vinculación por referencia.

Un programa de servicio no es un objeto ejecutable y no contiene un procedimiento


principal. En cambio, cualquier subprocedimiento en un programa de servicio puede ser
un punto de entrada al programa de servicio. Por tanto, se dice que un programa de
servicio es un programa de múltiples puntos de entrada. Cualquier programa ILE de un
solo punto de entrada, u otro programa de servicio pueden invocar cualquier
subprocedimiento que este contenido en un programa de servicio.
En el programa de servicio, solo existe una copia del código de los subprocedimientos
que pueden ser compartidos con muchos otros programas denominados programas
clientes.

Compilación y enlace de programas de servicio

Los procedimientos que residen en un programa de servicio no tienen requisitos de


codificación diferentes a los que ya se han estudiado con respecto a procedimientos
Nomain externos, después de ingresar el código fuente para el módulo, se compila
normalmente usando el comando CRTRPGMOD. El resultado final del proceso de
compilación es un objeto *Module. Normalmente, los procedimientos en el módulo
Nomain de un programa de servicio utilizan la palabra clave Export para garantizar que
estén disponibles para los programas cliente.

Se usa el comando CRTSRVPGM para enlazar los objetos de los módulos a un


programa de servicio. En el proceso de vinculación al programa de servicio se requiere
una lista de módulos que se copiaran en el objeto del programa de servicio *Srvpgm.

Cuando se crea un programa de servicio, sus procedimientos pueden hacer referencia a


otros procedimientos que residen en otros programas de servicio. El proceso de enlace
CRTSRVPGM puede enumerar esos programas de servicio (en el parámetro
BNDSRVPGM, en Parámetros avanzados), que luego estarán vinculados por referencia
al programa de servicio que se está creando. Todos los procedimientos exportados de
esos programas de servicio también están disponibles para el nuevo programa de
servicio.

El comando CRTSRVPGM, al igual que el comando CRTPGM, admite el uso de


directorios de enlace. Además de entradas de módulos, un directorio de enlace puede
contener nombres de programas de servicio que se pueden reutilizar entre muchos
programas.

Uso de los programas de servicio

Los programas cliente pueden invocar a cualquier procedimiento en un programa de


servicio. Pero los programas no llaman al programa de servicio directamente. Debido a
que un programa de servicio carece de un procedimiento principal, no es posible
llamarlo. En lugar de esto los programas cliente invocan a procedimientos que están
contenidos en los programas de servicio.

Cuando se hace un enlace con un programa de servicio, el proceso no copia código a los
programas cliente, lo que hace el proceso es asociar una referencia entre el programa
cliente y el programa de servicio que incluye a todos los procedimientos que están
contenidos en él.

Independientemente de cuántos clientes utilicen procedimientos en un programa de


servicio, solo existe una copia del código ejecutable en el programa de servicio. En caso
de haber muchos programas que utilizan un procedimiento, el enlace por referencia
evita la redundancia de enlazar por copia y puede eliminar la necesidad de actualizar
muchos programas cuando haya que corregir o mejorar un procedimiento.

El comando DSPPGM muestra los módulos y programas de servicio que componen un


programa cliente usando los parámetros DETAIL (*MODULE *SRVPGM). De manera
similar, el comando DSPSRVPGM muestra los módulos y programas de servicio que
utiliza un programa de servicio.

Aunque un programa de servicio puede tener cientos de programas cliente, es posible


que no requieran ningún mantenimiento en caso de hacer un cambio a un programa de
servicio. La necesidad de revincular programas cliente y el programa de servicio
dependerá de la firma del programa de servicio. La firma es un atributo del programa de
servicio que identifica su interfaz externa, y esta interfaz se asocia con procedimientos
exportados y elementos de datos que le corresponden.

La firma es una propiedad que se almacena con el programa de servicio; su identidad


suele ser una cadena de caracteres generada por el sistema. Cuando se vincula un
programa de servicio con un programa cliente, el cliente registra la firma del programa
de servicio. En tiempo de ejecución, cuando el cliente se carga verifica el programa de
servicio para asegurarse que contiene la firma que el cliente espera. Cuando las firmas
en el cliente y el programa de servicio coinciden, el cliente puede utilizar el programa
de servicio. Sin embargo, si hay discrepancia el programa cliente no corre.

El programa de servicio tiene solo una firma actual, pero puede conservar muchas
firmas anteriores, que representan muchas interfaces públicas diferentes. Cuando se
vincula un programa de servicio a un programa cliente, el proceso de enlace asocia los
dos objetos utilizando la firma actual. Pero si existen programas cliente que identifican
el programa de servicio por una firma previa, esos programas pueden continuar usando
el programa de servicio sin volver a compilar o volver a enlazar. El comando
DSPSRVPGM muestra todas sus firmas válidas.

Uso del lenguaje para enlace (Binder)

El lenguaje Binder es una sintaxis especial que consta de comandos para describir la
interfaz externa de un programa de servicio. Usando un editor, se ingresan comandos en
un miembro fuente (tipo BND) y luego se refiere a ese miembro fuente cuando se crea
el programa de servicio. Aunque no se compila el miembro fuente, se usa en un
algoritmo que se utiliza para crear la firma del programa de servicio. Hay tres comandos
del lenguaje Binder:

STRPGMEXP (Inicio de la lista de módulos que podrán ser usados por programas
cliente)

EXPORT (Refiere a un módulo que podrá usar un programa cliente)


ENDPGMEXP (Final de la lista de módulos que podrán ser usados por programas
cliente)

STRPGMEXP PGMLVL(*CURRENT)

EXPORT SYMBOL("MODVAL_SQL")

ENDPGMEXP

La declaración STRPGMEXP inicia el comienzo del lenguaje Binder para definir una
firma para el programa de servicio. La especificación PGMLVL (*CURRENT)
corresponde al valor de la firma actual. La declaración ENDPGMEXP indica el final de
la firma. Entre las declaraciones inicial y final, las declaraciones EXPORT enumeran
los procedimientos que estarán incluidos en la firma.

No se puede compilar el miembro fuente. En su lugar, se hace referencia a él cuando se


crea el programa de servicio:

CRTSRVPGM SRVPGM(APPSRVPGM) +

MODULE(MODVAL_SQL) +

EXPORT(*SRCFILE) +

SRCFILE(QSOURCES) +

SRCMBR(APPSRVPGM)

El parámetro EXPORT (*SRCFILE) le dice al proceso de vinculación que generare la


firma del programa de servicio utilizando el lenguaje Binder. Los parámetros SRCFILE
y SRCMBR identifican la ubicación y el miembro fuente que usara el lenguaje Binder,
en el ejemplo anterior QSOURCES y APPSRVPGM.

Una vez creado el programa de servicio, se puede ver la firma resultante mediante el
comando DSPSRVPGM (Mostrar programa de servicio). La firma es un string de 16
caracteres generada por el sistema. Cuando se vincula el programa de servicio a un
programa cliente, el cliente también almacena la firma actual del programa de servicio.

A medida que se agregan nuevos procedimientos a un programa de servicio, el lenguaje


Binder administra no solo la nueva firma actual del programa de servicio, sino que
también administra las firmas anteriores. Entonces, si algún programa cliente identifica
el programa de servicio por una firma previa, esos programas cliente podrán continuar
usando el programa de servicio sin volver a compilar.
Si se agregan nuevos módulos se debe seguir este procedimiento:

STRPGMEXP PGMLVL(*CURRENT)

EXPORT SYMBOL("MODVAL_SQL")

EXPORT SYMBOL("MODINC_SQL")

ENDPGMEXP

STRPGMEXP PGMLVL(*PRV)

EXPORT SYMBOL("MODVAL_SQL")

ENDPGMEXP

La segunda firma es la firma original (que antes era *CURRENT) , pero ahora se ha


cambiado para que sea la firma anterior PGMLVL (*PRV). La nueva firma actual
(*CURRENT) es la misma que la firma original, pero con los nuevos procedimientos
agregados al final. El bloque de exportación de la firma actual debe contener los
mismos procedimientos en el mismo orden que los bloques de exportación anteriores y
se agregan los nuevos procedimientos solo al final del bloque.

Grupos de activación

Un grupo de activación es una subdivisión de un trabajo que la arquitectura ILE alista


para realizar un seguimiento de los recursos que utiliza un programa. Los recursos
incluyen la memoria que utiliza un programa, ciertas rutinas de manejo de errores y
recursos de administración de datos temporales. El sistema utiliza grupos de activación
para permitirle a un trabajo crear límites lógicos alrededor de sus aplicaciones.

Los programas ILE RPG (RPG IV), admiten todas las características ILE modernas.
Aunque los programas ILE RPG pueden ejecutarse en el grupo de activación
predeterminado DFTACTGRP(*YES), no deberían. Si se ejecutan en el grupo de
activación predeterminado DFTACTGRP(*YES), no se admitirá módulos,
procedimientos o programas de servicio, entre otras restricciones.

El grupo de activación por defecto es el que usaban los programas RPG antes de que se
creara la arquitectura ILE.
Hay varias opciones para seleccionar un grupo de activación:

El grupo de activación predeterminado (DFTACTGRP).

Un grupo de activación ILE con un nombre definido por el usuario.

Un grupo de activación con nombre definido por el sistema (*NEW).

El grupo de activación correspondiente al programa llamante (*CALLER).

El grupo de activación predeterminado (DFTACTGRP), como se explicó anteriormente


es el grupo de activación en que corren los programas que no son ILE. No se debe usar
con programación ILE.

Un grupo de activación ILE con un nombre definido por el usuario es un grupo de


activación que si funciona para los programas ILE, el nombre no es relevante, muchos
mandatos de IBM utilizan el nombre QILE para un grupo de activación ILE y uno como
desarrollador también lo puede usar. Un trabajo crea un grupo de activación con nombre
de usuario cuando se inicia el primer programa que debe utilizar ese grupo de
activación. Si los programas posteriores especifican un grupo de activación con el
mismo nombre, se conectarán a ese grupo de activación sin crear uno nuevo.

Un grupo de activación con nombre de sistema, también conocido como grupo de


activación *NEW, también es un grupo de activación ILE genuino. Sin embargo, a
diferencia de un grupo de activación con nombre de usuario, un grupo de activación
*NEW solo permanece activo mientras el programa que lo usa todavía esté en la pila de
llamadas. El grupo de activación se crea de nuevo cada vez que se llama al programa y
se destruye cuando finaliza el programa.

La opción de grupo de activación más útil puede ser la opción *CALLER. Esta opción
no especifica ningún grupo de activación en particular. Con esta opción, el programa
simplemente se ejecuta en el mismo grupo de activación que el programa que lo llamó.
No se crea ningún grupo de activación nuevo; en cambio, el programa usa los mismos
recursos que el programa llamante.

También podría gustarte