Manual Del SQL
Manual Del SQL
Índice
Pág. 3
MultiBase. Manual del SQL
© Copyright BASE 100, S.A. Todos los derechos reservados. Ninguna parte de este documento puede ser reproducida ni transmitida por
medio alguno sin permiso previo por escrito del titular del copyright. Todos los productos citados en este documento son marcas registra-
das o marcas comerciales registradas de sus respectivos propietarios.
[MB_Manual_Sql_v3.0_es.docx]
Pág. 5
MultiBase
MANUAL DEL SQL
Pág. 7
MultiBase. Manual del SQL
Introducción
MultiBase consta de dos herramientas básicas para el desarrollo de una aplicación: Un servidor de base de da-
tos relacional, denominado CTSQL, y un lenguaje propio de propósito general, denominado CTL.
El presente volumen recoge todos los conceptos y particularidades relativos al gestor de base de datos de Mul-
tiBase, el CTSQL. De forma esquemática, la información contenida en el presente manual es la siguiente:
• Elementos básicos del CTSQL: Conceptos y elementos manejados por el gestor de base de datos de
MultiBase. Estos conceptos son fundamentales para comprender la forma de trabajar del CTSQL.
• Instrucciones del SQL: Dónde se incluyen, sintaxis, corrección de errores, etc.
• Sublenguajes del CTSQL: Lenguaje de Definición de Datos (DDL), Lenguaje de Control de Acceso a Da-
tos (DCL), Lenguaje de Consulta (QL) y Lenguaje de Manipulación de Datos (DML).
• Transacciones.
• Catálogo de tablas de la base de datos.
• Optimización.
• Comunicación con el lenguaje de cuarta generación de MultiBase (CTL).
• Recomendaciones.
1.— DDL (Data Definition Language): Lenguaje de definición de datos. Este sublenguaje comprende todas
aquellas instrucciones relativas a la definición de los elementos que componen una base de datos. Es decir,
creación de bases de datos, tablas, índices, etc. Las características de este sublenguaje se comentan en el capí-
tulo 5 de este manual.
2.— DCL (Data Control Language): Lenguaje de control de acceso a datos. Este sublenguaje contempla la con-
cesión y retirada de permisos a usuarios sobre la base de datos, tablas y columnas. Asimismo, se encarga de los
bloqueos o control de concurrencia entre usuarios. Las características de este sublenguaje se comentan en el
capítulo 6.
3.— QL (Query Language): Lenguaje de consulta. Parte del SQL dedicada a la extracción de información de las
tablas de la base de datos. Este sublenguaje lo constituye la instrucción SELECT. Sus características se comentan
en el capítulo 7.
4.— DML (Data Management Language): Lenguaje de manipulación de datos. Este sublenguaje se encarga del
mantenimiento de los datos en la base de datos. Esto significa que permitirá dar de alta nuevos valores, modifi-
carlos o borrarlos en las tablas de la base de datos. Las características de este sublenguaje se comentan en el
capítulo 8.
Pág. 9
MultiBase
MANUAL DEL SQL
Pág. 11
MultiBase. Manual del SQL
• De valores agregados.
• De fecha.
• De hora.
• De sistema.
• De manejo de caracteres.
• De conversión.
• Generales.
• Numéricas.
En los siguientes apartados se comenta en detalle cada una de ellas.
Valores agregados
count(*) Devuelve el número de filas que cumplen la cláusula «WHERE».
count(distinct x) Devuelve el número de valores únicos en la columna «x» en las filas que cum-
plen la cláusula «WHERE».
sum([distinct] x) Devuelve la suma de todos los valores de la columna «x» para las filas que cum-
plen la cláusula «WHERE». Esta función sólo podrá utilizarse con valores numé-
ricos. En caso de utilizarse la palabra clave «distinct», el resultado será la suma
de los valores distintos de la columna «x».
avg([distinct] x) Devuelve el promedio de todos los valores de la columna «x» para las filas que
cumplen la cláusula «WHERE». Esta función sólo podrá utilizarse con valores
numéricos. En caso de utilizarse la palabra clave «distinct», el resultado será el
promedio de los valores distintos de la columna «x».
max(x) Devuelve el valor máximo de la columna «x» de aquellas filas que cumplen la
cláusula «WHERE».
min(x) Devuelve el valor mínimo de la columna «x» de aquellas filas que cumplen la
cláusula «WHERE».
NOTAS:
1.— La cláusula «WHERE» sólo puede especificarse en las instrucciones de CTSQL: SELECT, UPDATE o DELETE.
2.— En las funciones «sum(x)», «avg(x)», «max(x)» y «min(x)», «x» puede ser una expresión en lugar de una
columna. En este caso, la función se evalúa sobre los valores de la expresión como cómputo de cada fila que
satisface la cláusula «WHERE».
3.— La palabra clave «distinct» sólo puede utilizarse con columnas, nunca con expresiones.
4.— La palabra clave «distinct» sólo puede utilizarse una sola vez en una «select_list».
5.— Los valores nulos afectan a los valores agregados de la siguiente forma:
• «count(*)» cuenta todas las filas aunque el valor de cada columna sea nulo.
• «count(distinct x)», «avg(x)», «sum(x)», «max(x)» y «min(x)» ignoran las filas con valores nulos para
«x». Sin embargo, si «x» contiene solamente valores nulos, devuelven nulo («NULL») para esa co-
lumna.
Pág. 13
MultiBase. Manual del SQL
Funciones de fecha
date(expresión) Devuelve el valor de «expresión» convertido a tipo DATE.
day(expresión) Devuelve el día de la fecha que resulte de la conversión de «expresión».
month(expresión) Devuelve el mes de la fecha que resulte de la conversión de «expresión».
year(expresión) Devuelve el año de la fecha que resulte de la conversión de «expresión».
weekday(expresión) Devuelve el día de la semana de la fecha que resulte de la conversión de «ex-
presión».
mdy(mes, día, año) Devuelve un valor de tipo DATE. «mes», «día» y «año» son expresiones que re-
presentan los componentes de la fecha.
Funciones de hora
time(expresión) Devuelve el valor de «expresión» convertido a tipo TIME.
hour(expresión) Devuelve las horas de la hora que resulten de la conversión de «expresión».
minute(expresión) Devuelve los minutos de la hora que resulten de la conversión de «expresión».
second(expresión) Devuelve los segundos de la hora que resulten de la conversión de «expresión».
hms(hora, minutos, segundos) Devuelve un valor de tipo TIME. «hora», «minutos» y «segundos»
son expresiones numéricas que representan los componentes de la hora.
mt(expresión) Devuelve «AM» o «PM», dependiendo del valor de la hora que devuelva «ex-
presión» (Meridian Time).
tomt(expresión) Devuelve el valor de tipo TIME correspondiente a la hora del meridiano.
Función de sistema
typelength(expr1, expr2) Devuelve el número de bytes ocupados por un tipo de dato SQL. «expr1»
debe dar el número correspondiente al tipo de dato (0 al 3 y del 5 al 8) y «ex-
pr2» debe dar la longitud, siendo este último significativo sólo en el tipo de da-
to CHAR.
rpad(exp1,n[,exp2]) Rellena la cadena por la derecha hasta la longitud ‘n’ con el carácter definido,
por defecto es blanco. Si el número de caracteres que le indicamos es menor
que la longitud de la expresión, trunca por la derecha.
substr(exp,m[, n])) Retorna un substring de la expresión que recibe como parámetro. Se le indica el
carácter de inicio y el número de caracteres del substring. Si el número de
carácter de inicio es negativo, indicará que empezará a ‘n’ caracteres del final
de la cadena.
Funciones de conversión
TO_DATE(cadena[, formato [, nlsparams ]])
Convierte una cadena de caracteres representando una fecha en un valor de fe-
cha según el formato especificado.
TO_CHAR(numero, [formato [, nlsparams] ])
TO_CHAR(fecha, [formato [, nlsparams] ])
Convierte un número o una fecha en una cadena de caracteres con el modelo
de formato indicado.
Funciones generales
DECODE(expresion, busqueda, resultado [, busqueda, resultado]...[, default ])
Esta función compara expr con cada uno de valores de búsqueda uno a uno. Si
expr es igual a un valor de búsqueda la base de datos devuelve el resultado co-
rrespondiente.
NVL(expr1,expr2) Compara expr1 con NULL. Si expr2 es NULL retornará expr2
Funciones numéricas
ABS(expr) Retorna el valor absoluto de una expresión numérica en una query.
SIGN(num) Esta función retorna -1 si el número que recibe como parámetro es negativo, 1
si es positivo y 0 si es 0.
TRUNC(numero, decimales) Trunca en la enésima posición decimal.
Pág. 15
MultiBase
MANUAL DEL SQL
Pág. 17
MultiBase. Manual del SQL
Convenciones utilizadas
Las convenciones empleadas en los diferentes manuales de MultiBase para presentar la sintaxis de cualquier
elemento son las siguientes:
Las convenciones empleadas en los diferentes manuales de MultiBase para presentar la sintaxis de cualquier
elemento son las siguientes:
[] Aquellos elementos que se encuentren entre corchetes son opcionales.
{} Indica la obligatoriedad de incluir uno de los elementos encerrados entre llaves. Dichos
elementos estarán separados por el carácter de «barra vertical» (carácter ASCII 124).
| Este carácter se utiliza para separar los diferentes elementos opcionales u obligatorios
de la sintaxis, dependiendo de si éstos van entre corchetes o entre llaves, respectiva-
mente.
… El elemento anterior a los puntos puede aparecer más veces en la sintaxis.
,… Como el caso anterior, pero cada nueva aparición del dicho elemento debe ir precedi-
da por una coma.
palabra Las palabras en minúsculas corresponden con identificadores CTL o CTSQL, expresio-
nes, etc.
PALABRA Las palabras en mayúsculas y en negrita son reservadas del lenguaje CTL o CTSQL, y por
tanto invariables.
subrayado Si no se especifica otra opción, la palabra o palabras subrayadas se toman por defecto.
negrita Cualquier signo de las convenciones que se encuentre en negrita es obligatorio en la
sintaxis.
El resto de signos se utilizan con su valor. Así, en aquellos lugares en los que se pueden especificar comillas,
éstas podrán ser simples o dobles (", '), con la obligatoriedad de cerrar el texto con el mismo tipo con el que se
abrió. Asimismo, se pueden incluir comillas como parte de un texto, en cuyo caso, dichas comillas deberán ser
necesariamente del tipo contrario a las que sirven de delimitadores del texto. Es decir, en el caso de definir un
literal entre comillas que incluya una parte también entrecomillada, habrá que combinar ambos tipos. Por
ejemplo:
tsql "load from 'fichero.unl' insert into tabla"
El símbolo del ratón indica que el texto que figura a continuación se refiere exclusivamente a la versión de Mul-
tiBase para Windows.
• Alias.
Un identificador puede estar formado por letras, números y signos de subrayado («_»). El primer carácter de-
berá ser siempre una letra, pudiendo ser la longitud del identificador de 1 a 18 caracteres. En el caso de intro-
ducir letras en mayúsculas en un identificador del CTSQL, éste las convertirá a minúsculas.
La estructura de un identificador CTSQL es la siguiente:
letra [ subrayado | letra | número ]
Donde:
letra Puede ser mayúscula o minúscula. Rango: a-z.
subrayado Símbolo de subrayado («_»).
número Carácter numérico. Rango: 0-9.
En una misma base de datos, los identificadores de cada tipo deben ser unívocos, aplicándose adicionalmente
esto a los siguientes grupos de elementos en su conjunto:
• Tablas y «views».
• Índices, claves primarias y claves referenciales.
• Columnas (en la misma tabla) y alias (en la misma instrucción SELECT).
Número Tipo
0 CHAR
1 SMALLINT
2 INTEGER
3 TIME
5 DECIMAL
6 SERIAL
7 DATE
8 MONEY
0 — CHAR(n): Cadena de caracteres alfanuméricos de longitud «n». «n» tiene que ser mayor o igual que uno y
menor o igual que 32.767 (1<= n <= 32767). El número de bytes que reserva en disco este tipo de dato es el
indicado en «n». El parámetro «n» es necesario indicarlo para la definición del elemento en concreto.
1 — SMALLINT: Este tipo de dato permite almacenar números enteros. El rango de valores numéricos admiti-
dos está entre -32.767 y +32.767, ambos inclusive. Este tipo de dato ocupa siempre 2 bytes, independiente-
mente del número de caracteres numéricos a grabar. Es decir, si se introduce el valor «1», éste ocupará lo
mismo que el valor «32.767». Por lo tanto, al estar ya predefinida la longitud de este tipo de datos no será ne-
cesario indicarla.
2 — INTEGER: Este tipo de dato permite almacenar números enteros. El rango de valores numéricos que admi-
te está entre -2.147.483.647 y +2.147.483.647, ambos inclusive. Este tipo de dato ocupa siempre 4 bytes, in-
Pág. 19
MultiBase. Manual del SQL
dependientemente del número de caracteres numéricos que se incluyan como dato. Al igual que en el tipo de
dato anterior, su longitud está ya predefinida.
3 — TIME: Este tipo de dato admite horas con el formato por defecto «HH:MM:SS» y almacenada como el
número de segundos desde las «00:00:01». Este tipo de dato es equivalente a un INTEGER, siendo en este caso
el rango de valores admitidos entre 1 y 86.400, que se corresponden con los valores «00:00:01» y «24:00:00»,
respectivamente. Al igual que el tipo de dato INTEGER, ocupa 4 bytes.
Los tipos TIME se introducen como una secuencia de hora, minutos y segundos, sin separador entre ellos (tam-
poco espacios en blanco). Su representación depende de la variable de entorno DBTIME (ver el capítulo corres-
pondiente a Variables de Entorno en el Manual del Administrador de MultiBase). Asimismo, estos tipos de da-
tos permiten ordenaciones y comparaciones horarias entre dos columnas de tipo TIME.
5 — DECIMAL [(m [, n])]: Tipo de dato que almacena números decimales de coma flotante con un total de «m»
dígitos significativos (precisión) y «n» dígitos a la derecha de la coma decimal (escala). «m» puede ser como
máximo menor o igual a 32 («m <= 32») y «n» menor o igual que «m» («n <= m»).
Cuando se asignan valores a «m» y «n», la variable decimal tendrá punto aritmético fijo. El segundo parámetro
«n» es opcional, y si se omite se tratará como un decimal de coma flotante. Un elemento decimal(m) tiene una
-130 125
precisión «m» y un rango de valor absoluto entre «10 » y «10 ». En caso de no especificar parámetros a un
elemento decimal, éste será tratado como «decimal(16)».
6 — SERIAL[(n)]: Este tipo de dato sólo puede utilizarse en CTSQL, no así en el lenguaje de cuarta generación
CTL. Los valores que admite son números enteros secuenciales y únicos asignados automáticamente por CTSQL
(contador automático). El valor inicial de este tipo de dato, por defecto, es uno (1). No obstante, este valor ini-
cial puede cambiarse al asignar un valor a «n» en la definición del elemento (columna). El CTSQL no permitirá la
introducción ni la modificación de ningún valor ya utilizado sobre una columna SERIAL. La ocupación en disco es
de 4 bytes.
Comportamiento del tipo de dato SERIAL:
• Al grabar el primer valor sobre la columna definida como SERIAL, el valor asignado automáticamente
por defecto es «1». Esto es así, excepto si se asigna un valor inicial en la definición de la columna.
Asimismo, dicha columna SERIAL también puede inicializarse mediante la asignación de un valor con-
creto en una inserción o actualización. Si dicha columna SERIAL dispone de datos, el siguiente valor a
insertar será el máximo más uno, siempre y cuando no haya existido alguna baja.
• En caso de que la columna SERIAL haya tenido ciertos valores y se hayan borrado, el siguiente valor a
insertar será el máximo valor que haya existido o exista más uno.
• Por defecto, en las inserciones de valores nuevos en la columna SERIAL siempre se incrementará al
valor máximo existente, o que haya existido, más uno.
7 — DATE: Tipo de dato que admite fechas con el formato por defecto «dd/mm/yyyy» y las almacena como el
número de días transcurridos partiendo desde el «01/01/1900». Dicho número será positivo o negativo depen-
diendo de si la fecha es posterior o anterior, respectivamente, al valor inicial indicado. Este tipo de dato es
equivalente a un INTEGER, ocupando por tanto 4 bytes en disco.
Los tipos DATE son introducidos como una secuencia de día, mes y año con caracteres numéricos:
• El día puede representarse como el día del mes (1 ó 01, 2 ó 02, etc.).
• El mes se representa como un número (1 equivale a enero, 2 a febrero, etc.).
• El año se representa como un número de cuatro dígitos (0001 a 9999). En caso de visualizar dos dígi-
tos para el año, CTSQL asume que el año es 19yy.
Se puede ordenar por una columna de tipo DATE y hacer una comparación cronológica entre dos columnas de
tipo DATE.
El orden en que se introduzcan el año, mes y día dependerá del valor de la variable de entorno DBDATE (con-
sulte el capítulo sobre Variables de Entorno en el Manual del Administrador de MultiBase).
8 — MONEY [(m [, n])]: Tipo de dato que almacena números decimales de coma flotante con un total de «m»
dígitos significativos (precisión) y «n» dígitos a la derecha de la coma decimal (escala). Ésta es la misma defini-
ción que se asignó al tipo de dato «DECIMAL». Este tipo de dato se utiliza para representar cantidades referen-
tes a unidades monetarias, empleando la variable de entorno DBMONEY para identificar la unidad monetaria a
la que se refiere el importe definido como valor (consulte el capítulo sobre «Variables de Entorno» en el Ma-
nual del Administrador).
El tipo de dato MONEY(m) se define como el tipo de dato DECIMAL(m,2), y si no se especifican parámetros,
MONEY se trata como una variable de tipo DECIMAL(16,2).
El siguiente cuadro recoge de forma resumida los valores de los diferentes tipos del datos:
En caso de existir valores introducidos en las tablas, la conversión de tipos de datos entre sí es válida en los
siguientes casos:
Desde\Hasta CHAR SMALLINT INTEGER TIME DECIMAL SERIAL DATE MONEY
CHAR — SÍ SÍ SÍ SÍ SÍ SÍ SÍ
SMALLINT NO — SÍ SÍ SÍ SÍ SÍ SÍ
INTEGER NO SÍ — SÍ SÍ SÍ SÍ SÍ
TIME NO SÍ SÍ — SÍ SÍ SÍ SÍ
DECIMAL NO SÍ SÍ SÍ — SÍ SÍ SÍ
SERIAL NO SÍ SÍ SÍ SÍ — SÍ SÍ
DATE NO SÍ SÍ SÍ SÍ SÍ — SÍ
MONEY NO SÍ SÍ SÍ SÍ SÍ SÍ —
La conversión «SÍ» será válida cuando los datos a convertir cumplan las reglas de rango especificadas para cada
tipo en particular.
NOTA: Los tipos de datos expuestos en este apartado, excepto SERIAL, son válidos igualmente para la definición
de los distintos tipos de variables en el lenguaje de cuarta generación de MultiBase (CTL).
Pág. 21
MultiBase. Manual del SQL
Los atributos que se comentan en este epígrafe corresponden al CTSQL. Si bien la mayoría de ellos afectan
igualmente al CTL, este último dispone no obstante de una serie de atributos específicos que se comentan en el
Manual del Programador.
Los atributos del CTSQL sólo podrán utilizarse en las instrucciones CREATE TABLE y ALTER TABLE dentro del
Lenguaje de Definición de Datos (DDL).
Los atributos que afectan al CTSQL son los siguientes:
CHECK NOT NULL
DEFAULT NOUPDATE
DOWNSHIFT PICTURE
FORMAT RIGHT
LABEL UPSHIFT
LEFT ZEROFILL
NOENTRY
CHECK
Este atributo se utiliza para definir una expresión de tipo booleana (condición). Todos los valores de la variable
que hagan que dicha expresión evalúe a FALSE serán rechazados. Su sintaxis es la siguiente:
CHECK (condición)
Donde:
condición Lista de una o más subcondiciones separadas por operadores AND, OR y NOT.
Estas condiciones pueden incluir el signo dólar ( $ ) para sustituir el nombre de
la columna a la que se aplica este atributo. Las condiciones válidas en CTSQL son
las siguientes:
Donde:
lista_valores Lista de valores separados por comas.
Pág. 23
MultiBase. Manual del SQL
2.— Una expresión que devuelva el valor «NULL» sólo hará cierta la condición «IS NULL».
DEFAULT
Este atributo se utiliza para asignar un valor por defecto a la columna de la tabla, en el momento de iniciar la
edición en modo agregar para provocar «insert» de filas. Asimismo, en caso de utilizar la instrucción INSERT del
«DML» y no asignar un valor a la columna con este atributo, por defecto siempre se grabará el valor especifica-
do en el mismo. Su sintaxis es la siguiente:
DEFAULT valor
Donde:
valor Constante numérica o alfanumérica que se tomará por defecto. Si la columna es
de tipo DATE, TIME o CHAR, este valor tiene que ir entre comillas.
En caso de no utilizar este atributo, todas las columnas toman por defecto el valor nulo.
En las columnas de tipo DATE y TIME pueden utilizarse las variables internas de CTSQL para asignar valores por
defecto. Estas variables son TODAY y NOW, respectivamente.
Ejemplo:
create table albaranes (
albaran integer not null label "Num. Albaran",
cliente integer not null label "Cod. Cliente",
fecha_albaran date default today label "Fecha Albaran",
fecha_envio date default today label "Fecha Envio",
fecha_pago date label "Fecha Pago",
formpago char( 2) label "Cod. F.Pago",
estado char( 1) upshift check ($ in ("S", "N")) default "N" label "Facturado");
En este ejemplo, tanto la columna «fecha_albaran» como «fecha_envio» tienen asignadas el atributo DEFAULT
con el valor de la fecha del sistema («TODAY»).
Asimismo, la columna «estado» incluye el atributo DEFAULT indicando que por defecto siempre se grabará el
valor «N» en mayúsculas (UPSHIFT). Este valor es uno de los dos posibles indicados por el atributo CHECK: «S» y
«N».
DOWNSHIFT
Este atributo convierte las letras mayúsculas de los valores de una columna de tipo CHAR a minúsculas en el
momento de su inserción o actualización (INSERT o UPDATE), respectivamente. En caso de emplear el lenguaje
de cuarta generación CTL como interface para el mantenimiento de la tabla en concreto, en la edición de los
valores de esta columna también convertirá las mayúsculas a minúsculas.
NOTAS:
1.— Si la tabla ya está creada y se han grabado en ella valores con letras en mayúsculas, al incorporar este atri-
buto a dicha columna mediante una alteración de la tabla (ALTER TABLE) no se convertirán dichos valores.
2.— Si se especifican los atributos DOWNSHIFT y UPSHIFT, CTSQL considerará únicamente el último que en-
cuentre en la definición de la columna.
Ejemplo:
create table proveedores(
proveedor integer not null label "Codigo Proveedor",
empresa char(25) downshift label "Empresa",
apellidos char(25) label "Apellidos",
nombre char(15) label "Nombre",
direccion1 char(25) label "Direccion1",
direccion2 char( 25) label "Direccion2",
poblacion char( 15) label "Poblacion",
provincia smallint label "Provincia",
distrito integer label "Distrito",
telefono char( 13) label "Telefono")
primary key (proveedor) ;
En este ejemplo, la columna «empresa» se define con el atributo DOWNSHIFT, lo que significa que no podrá
contener ningún carácter en mayúsculas. Si intentásemos insertar o actualizar un valor con caracteres en
mayúsculas, éstos se convertirán automáticamente a minúsculas.
FORMAT
Este atributo determina el formato en el que se presentarán los valores de aquellas columnas en las que se
asigne. El atributo FORMAT podrá asignarse a todos los tipos de datos, excepto a CHAR (para éste habrá que
utilizar el atributo PICTURE). Su formato es el siguiente:
FORMAT "formato"
Donde:
formato Cadena de caracteres entre comillas que indica el formato Dependiendo del ti-
po de dato, los posibles formatos son:
1.— Numéricos (SMALLINT, INTEGER, DECIMAL, SERIAL y MONEY). Los caracte-
res que se podrán emplear son:
# Representa cada dígito. No cambia los espacios en blanco por
ningún otro carácter.
. (punto) El punto indica la posición de la coma decimal. Aparecerá una coma
o un punto, dependiendo de la definición de la variable de entorno
DBMONEY (consulte el capítulo sobre «Variables de Entorno» en el
Manual del Administrador).
, (coma) Permite indicar la separación de miles, millones, etc. Se imprimirá
una coma si la variable de entorno DBMONEY tiene como valor un
punto, y un punto si aquél es una coma.
* Rellena los espacios en blanco con asteriscos.
& Rellena los espacios en blanco con ceros.
Pág. 25
MultiBase. Manual del SQL
2.— Fechas (DATE). En este caso los caracteres que se evalúan con un significa-
do para la plantilla de formato son «d», «m» e «y», en las combinaciones que se
indican a continuación, representándose cualquier otro carácter como tal.
dd Dos dígitos para el día del mes (01 a 31).
ddd Tres letras significativas del nombre del día de la semana (Lun
a Dom).
mm Dos dígitos para el mes (01 a 12).
mmm Tres letras significativas del nombre del mes (Ene a Dic).
3.— Horas (TIME). En este caso los caracteres que se evalúan con un significado
para la plantilla de formato son «h», «m» y «s», en las combinaciones que se
indican a continuación:
hh:mm:ss Dos dígitos para la hora, dos para los minutos y dos para los
segundos.
hh:mm Dos dígitos para la hora y dos dígitos para los minutos.
hh Dos dígitos para la hora.
Casos prácticos:
Hora Formato Resultado
15:10:20 hh:mm:ss 15:10:20
15:10:20 hh:mm 15:10
15:10:20 hh 15
Ejemplo:
create table lineas(
albaran integer not null format "###" label "Num. Albaran",
linea smallint not null format "##" label "Num. Linea",
articulo smallint label "Cod. Articulo",
proveedor integer label "Cod. Proveedor",
cantidad smallint format "###" label "Cantidad",
descuento smallint label "Descuento",
precio money(9,2) label "Precio")
primary key (albaran,linea)
LABEL
Este atributo en CTSQL define por defecto una etiqueta (título) para la columna que se está definiendo. Esta
etiqueta será mostrada con las instrucciones de entrada/salida del lenguaje de cuarta generación CTL. Por
ejemplo: PROMPT FOR, WINDOW FROM SELECT, TSQL SELECT, etc.
La sintaxis de este atributo es la siguiente:
LABEL "literal"
Donde:
literal Cadena de caracteres alfanumérica entre comillas.
Ejemplo:
create table provincias (
provincia smallint not null label "Cod. Provincia",
descripcion char(20) label "Provincia",
prefijo smallint label "Prefijo");
En este ejemplo, todas las columnas tienen asignadas una etiqueta («label»). Estas etiquetas aparecerán a la
hora de hacer una lectura (SELECT) de los datos de la tabla, o bien en cualquier operación de entrada/salida
desde el lenguaje de cuarta generación CTL.
Pág. 27
MultiBase. Manual del SQL
LEFT
Alínea el campo a la izquierda. Su sintaxis es la siguiente:
LEFT
Si no se especifica ningún atributo de alineación, por defecto los datos numéricos se ajustan a la derecha y los
tipos de datos CHAR a la izquierda.
NOTA: En caso de especificar los atributos LEFT y RIGHT (ver más adelante), CTSQL considerará únicamente el
último que encuentre en la definición de la columna.
NOENTRY
Este atributo impide insertar un valor en aquella columna donde se define mediante la instrucción INSERT del
CTSQL. Sin embargo, sí admitirá dicho valor cuando se actualice una fila mediante la instrucción UPDATE del
CTSQL. Su sintaxis es la siguiente:
NOENTRY
NOTA: En caso de asignar a una columna los atributos NOT NULL y NOENTRY habrá que definir también el atri-
buto DEFAULT.
Ejemplo:
create table albaranes (
albaran integer not null label "Num. Albaran",
cliente integer not null label "Cod. Cliente",
fecha_albaran date label "Fecha Albaran",
fecha_envio date label "Fecha Envio",
fecha_pago date label "Fecha Pago",
formpago char( 2) label "Cod. F.Pago",
estado char( 1) upshift check ($ in ("S", "N")) noentry
default "N" label "Facturado");
En este ejemplo, cada vez que se dé de alta una fila en la tabla «albaranes», la columna «estado» no admitirá la
asignación de valor alguno. No obstante, el valor que se grabará en todas las filas será «N», ya que es su valor
por defecto. Dicha columna en actualización (UPDATE) podrá admitir los valores «S» y «N».
NOT NULL
Este atributo obliga a que la columna tenga un valor distinto de nulo («NULL») al insertar o modificar un fila de
una tabla. Su sintaxis es la siguiente:
NOT NULL
CTSQL utiliza «NULL» como valor por defecto en la inserción de nuevas filas sobre una tabla. A la hora de agre-
gar una columna con este atributo a una tabla ya existente, es aconsejable asignar un valor por defecto. Esta
operación la realiza el atributo DEFAULT (comentado anteriormente). Esta recomendación se debe a que si la
tabla ya contiene filas, la columna nueva se agregará con el valor por defecto en cada una de las filas existen-
tes. Sin embargo, en caso de no especificar valor por defecto se producirá un error en dicha operación.
IMPORTANTE
Todas las columnas que integran la clave primaria de una tabla han de tener asignado el atributo
NOT NULL.
Ejemplo:
create table provincias (provincia smallint not null label "Cod. Provincia",
descripcion char(20) label "Provincia",
prefijo smallint label "Prefijo")
primary key (provincia);
En este ejemplo, se asigna el atributo NOT NULL a la columna que integrará la clave primaria de la tabla «pro-
vincias».
NOUPDATE
Este atributo impide actualizar un valor en aquella columna donde se define mediante la instrucción UPDATE
del CTSQL. No obstante, sí admitirá dicho valor cuando se inserte una fila mediante la instrucción INSERT del
CTSQL. Su sintaxis es la siguiente:
NOUPDATE
Este atributo se asignará a aquellas columnas cuyo valor insertado (INSERT) no se pueda modificar. En caso de
asignar este atributo a la(s) columna(s) que integran la clave primaria («primary key»), su acción será similar a
los controles originados por la programación de la integridad referencial entre dos tablas.
Ejemplo:
create table formpagos (
formpago char(2) not null noupdate label "Cód. F. Pago",
descripcion char(20) label "Forma de pago")
primary key (formpago);
En este ejemplo, una vez asignado un valor en la inserción de la fila, la columna «formpago» de la tabla «form-
pagos» nunca podrá actualizarse por dos razones:
PICTURE
Este atributo especifica la máscara de edición de una columna de tipo CHAR. Su sintaxis es la siguiente:
PICTURE "máscara"
Donde:
Pág. 29
MultiBase. Manual del SQL
máscara Cadena de caracteres que especifica el formato deseado. Esta cadena tiene que
ir necesariamente entre comillas, pudiendo estar compuesta por la combina-
ción de los siguientes símbolos:
Símbolo Representación
A Cualquier carácter alfabético.
# Cualquier carácter numérico.
X Cualquier carácter alfanumérico.
Cualquier otro carácter se tratará como un literal y aparecerá en el campo en la
misma posición en que se especifique en la máscara.
Ejemplo:
alter table clientes add
(cif char(15) picture "A##/###########" upshift);
En este ejemplo, la columna «cif» tiene una máscara que indica lo siguiente:
• El primer carácter debe ser una letra («A-Z») en mayúsculas, ya que también incluye el atributo UPS-
HIFT.
• El segundo y tercer caracteres tienen que ser un número («0-9»).
• El cuarto carácter siempre será una barra («/»).
• A partir del cuarto carácter hasta el final tienen que ser números («0-9»).
RIGHT
Ajusta el campo a la derecha. Su sintaxis es la siguiente:
RIGHT
Si no se especifica ningún atributo de alineación, por defecto los datos numéricos se ajustan a la derecha y los
tipos de datos CHAR a la izquierda.
NOTA: En caso de especificar los atributos RIGHT y LEFT, CTSQL considerará únicamente el último que encuen-
tre en la definición de la columna.
UPSHIFT
Este atributo convierte las letras minúsculas de los valores de una columna de tipo CHAR en mayúsculas en el
momento de su inserción o actualización (INSERT o UPDATE), respectivamente. Su sintaxis es la siguiente:
UPSHIFT
NOTAS:
1.— Si la tabla ya está creada y en ella se han grabado valores con letras en minúsculas, al incorporar este atri-
buto a dicha columna mediante una alteración de la tabla (ALTER TABLE) no se convertirán dichos valores.
2.— En caso de coincidir el atributo UPSHIFT con DOWNSHIFT, CTSQL obedecerá al último que se encuentre en
la definición de la columna.
Ejemplo:
create table proveedores(
proveedor integer not null label "Codigo Proveedor",
empresa char(25) upshift label "Empresa",
apellidos char(25) label "Apellidos",
nombre char(15) label "Nombre",
direccion1 char(25) label "Direccion1",
En este ejemplo, la columna «empresa» se define con el atributo UPSHIFT. Esto significa que no podrá contener
ningún carácter en minúsculas. En el caso de que intentásemos insertar o actualizar un valor con caracteres en
minúsculas, éstos se convertirán automáticamente a mayúsculas.
ZEROFILL
Este atributo alinea a la derecha el valor, sin tener en cuenta el tipo de dato (numérico o alfanumérico), y relle-
na con ceros por la izquierda. Su sintaxis es la siguiente:
ZEROFILL
En este ejemplo, la columna «distrito» tiene asignado el atributo ZEROFILL. En caso de no incorporar este atri-
buto, los códigos postales que comiencen por cero (aunque se tecleen los ceros de la izquierda), se perderían al
tratarse de un tipo de dato numérico (INTEGER). Por ejemplo: El distrito «08031» de Barcelona aparecería co-
mo «8031». Sin embargo, si se incluye el atributo ZEROFILL como en nuestro ejemplo, no será necesario teclear
los ceros de la izquierda, ya que dicho atributo los incluye automáticamente.
Columna
Una columna es un conjunto de valores del mismo tipo de datos, no necesariamente distintos y variables en el
tiempo. Una columna se define con un nombre (identificador), el tipo de datos al que pertenecen sus valores y
los atributos de éstos. Dichos atributos son los que se han comentado en el epígrafe anterior para el CTSQL.
Un valor de una columna es la unidad más pequeña que puede seleccionarse o actualizarse. Una columna re-
presenta un atributo en una entidad «tabla» de la base de datos.
Una columna se puede nombrar simplemente por su identificador o por medio de su identificador precedido
por el nombre de la tabla y un punto decimal («tabla.columna»). Esta forma de identificar las columnas es de
uso obligado en caso de existir ambigüedades en una instrucción de CTSQL, por ejemplo, si se utilizan dos co-
lumnas del mismo nombre pero diferentes tablas en una misma instrucción de CTSQL.
Pág. 31
MultiBase. Manual del SQL
IMPORTANTE
Una columna no puede denominarse igual que la tabla a la que pertenece. El CTSQL no tiene
problema, ya que distingue perfectamente entre una columna y una tabla con el mismo nombre,
pero con el lenguaje de cuarta generación CTL se producirían errores de compilación.
Ejemplo:
create table provincias (
provincia smallint not null label "Cod. Provincia",
descripcion char(20) label "Provincia",
prefijo smallint label "Prefijo");
La instrucción CREATE TABLE será la que provoque la creación de una tabla con sus respectivas columnas. En
este ejemplo, la tabla «provincias» contiene tres columnas, denominadas «provincia», «descripcion» y «prefi-
jo», con distintos tipos de datos.
Tabla base
Una tabla base es un conjunto de columnas no necesariamente distintas. Una tabla se define con un nombre
(identificador) y la definición de sus correspondientes columnas. Una tabla representa una entidad de la base
de datos.
El concepto de tabla es similar al de fichero en otros lenguajes de programación. Las diferencias entre una tabla
de la base de datos y un fichero son las siguientes:
• Las filas de una tabla no guardan ningún orden particular en el almacenamiento, sin embargo, pue-
den recuperarse en cualquier orden con sólo especificarlo en la operación que la define.
• Las columnas de una tabla pueden recuperarse en cualquier orden con sólo especificarlo al indicar
sus nombres.
• No es necesario acceder a todas las filas ni a todas las columnas, existen operaciones que permiten
manejar un subconjunto de las filas, o un subconjunto de las columnas, o ambas posibilidades a la
vez (tablas derivadas o «views»).
Al crear la tabla, los ficheros «.dat» e «.idx» ocupan 0 y 2.048 bytes en disco, respectivamente, incrementándo-
se ambos en múltiplos de 1 Kbyte. En este espacio, el fichero de índices («.idx») incluye la siguiente informa-
ción:
• Número de índices.
• Descripción de cada índice (si es único o duplicado y de qué columnas está compuesto).
• Número de registros, incluyendo «huecos» provocados por borrado de filas, en el fichero de datos.
• Longitud (en bytes) de una fila.
• Número de registros del fichero de índices.
• Lista de «huecos» de los ficheros de datos e índices.
• Una estructura en árbol («B+ tree») por índice.
El espacio que ocupará el fichero de datos «.dat» para una tabla se calcula de la siguiente forma:
(número_filas + número_huecos) * (bytes_fila + 1)
Donde:
número_filas Número de filas existentes en la tabla. Este número se encuentra en la columna
«nrows» de la tabla del catálogo de la base de datos «systables». Para que la in-
formación de esta columna sea coherente con el número de filas reales de la
tabla hay que ejecutar previamente la instrucción UPDATE STATISTICS del
CTSQL.
número_huecos Número de «huecos» existentes en el fichero provocados por el borrado de fi-
las.
bytes_fila Número de bytes que ocupa una fila en la tabla de la que se desea calcular su
ocupación en disco. Esta información se encuentra en la columna «rowsize» de
la tabla del catálogo de la base de datos «systables».
El espacio (aproximado) que ocupa el fichero de índices se calcula mediante la ejecución del siguiente módulo
CTL:
define
variable i integer
frame keydesc
screen
{
Número Registros.....: [f1 ]
Longitud Clave ........: [f2 ]
Duplicados (S/N) .....: [d]
Factor .....................: [f]
%
O C U P A C I O N D E L I N D I C E ....: [f3 ]Kbytes
% *
‘T O T A L A C U M U L A D O ...............: ‘[f4 ]Kbytes
*
}
end screen
variables
f1 = nr integer
f2 = lk integer
d = dup char(1) include ("S","N") upshift
f = fac integer
f3 = totindice integer reverse noupdate
f4 = tot integer reverse noupdate
end variables
layout
Pág. 33
MultiBase. Manual del SQL
box
line 5
column 3
label "T a m a ñ o I n d i c e s"
end layout
end frame
end define
main begin
let tot = 2
let dup = "N"
let fac = 3
forever begin
input frame keydesc for update
end input
if cancelled = true then break
let totindice = keysize(nr, lk, dup, fac)
let tot = tot + totindice
view
end
clear frame keydesc
end main
fac Factor de aproximación. Por lo general, el factor empleado debe ser el número 3.
totindice Número de Kbytes que ocupará dicho índice en disco.
tot Número total de Kbytes que ocupará el fichero «.idx» con la composición de todos los
índices.
IMPORTANTE
Los ficheros «.dat» e «.idx» no son ficheros ASCII, por tanto, NO pueden editarse con ningún edi-
tor de textos. La forma de actualizar su contenido será mediante el manejo de instrucciones de
tipo CTSQL.
Almacenamiento de la información
CTSQL aumenta de forma automática los ficheros físicos «.dat» e «.idx» correspondientes a la tabla en la que
se inserta información. En el fichero de datos («.dat»), este aumento se realiza cuando no exista ninguna fila
marcada. Una fila se marca cuando se produce su borrado mediante la instrucción DELETE del DML. Esto signi-
fica que cuando el CTSQL produce un borrado de filas, éstas no se borran, sino que se marcan. Estas filas mar-
cadas se sustituirán por nuevas filas agregadas a la tabla mediante la instrucción INSERT del DML.
La única forma de compactar estas filas marcadas («borradas») para la recuperación de espacio, sin inserción
de filas nuevas, es mediante la ejecución del comando trepidx sobre dicha tabla (para más información acerca
de los comandos, consulte el capítulo correspondiente en el Manual del Administrador de MultiBase).
Por ejemplo, supongamos que disponemos de una tabla con la siguiente información y cuyo orden físico de
almacenamiento de las filas es el siguiente:
Provincia Descripción
1 ALAVA
2 ALBACETE
3 ALICANTE
4 ALMERIA
«M» es una «marca» interna que ocupa 1 byte y que indica, en el caso de contener información («*»), que la
fila se encuentra borrada.
Cuando se inserta una nueva fila, por ejemplo: «5», «ÁVILA»:
insert into provincias (provincia, descripcion)
values (5,"AVILA")
Pág. 35
MultiBase. Manual del SQL
En caso de realizar una lectura secuencial de las filas de esta tabla, la información que devolverá es la expuesta
en el ejemplo anterior.
IMPORTANTE
Debido a esta forma de actuar del CTSQL, NUNCA nos podremos fiar del orden de introducción
de las filas en la tabla. Posiblemente, si dicha tabla ha tenido bajas de filas, el orden de inserción
de las mismas no coincida con la lectura secuencial de las mismas.
Tabla temporal
Una tabla temporal es igual que una tabla base, pero con la diferencia de que su borrado será automático en el
momento que termine el programa que la creó. Asimismo, las tablas temporales no pertenecen a ninguna base
de datos. Debido a esta característica, estas tablas son un buen soporte para compartir información entre dis-
tintas bases de datos.
Una tabla temporal se crea mediante la instrucción CREATE TEMP TABLE del sublenguaje DDL del CTSQL o bien
mediante la cláusula «INTO TEMP» de la instrucción SELECT.
NOTA: Los ficheros físicos con extensión «.dat» e «.idx» se crean en el directorio de trabajo definido en la va-
riable de entorno DBTEMP.
Tabla derivada
Una tabla derivada es una tabla resultado que contiene la información obtenida por la ejecución de la instruc-
ción de lectura SELECT del CTSQL. Una tabla derivada no es un objeto persistente, no tiene nombre y no está
definida por separado de la operación que la genera (como ya se ha indicado, esta operación la realiza la ins-
trucción SELECT). Por ejemplo, ejecutar la siguiente instrucción en la aplicación de demostración de MultiBase:
select provincia, descripcion from provincias
where provincia = 28
En este caso, la tabla derivada está compuesta de una sola fila, que es la siguiente:
Provincia Descripción
28 MADRID
Ahora bien, una tabla derivada también puede estar compuesta de varias filas. Por ejemplo:
select provincia, descripcion from provincias
where provincia < 10
«View»
Una «view» es una tabla derivada a la que se le asigna un nombre. Asimismo, podría tratarse como una tabla
base de la base de datos, pero sin ficheros «.dat» e «.idx» físicos. Pese a no existir físicamente en el almacena-
miento, se trata de un objeto persistente en la base de datos. El contenido de una «view» es evaluado cada vez
que se la invoca en una instrucción CTSQL.
La creación de una «view» la provoca la instrucción CREATE VIEW del sublenguaje DDL del CTSQL, mientras que
su borrado se efectúa con la instrucción DROP VIEW. La estructura física de una «view» está definida por la
instrucción SELECT asociada en su creación. Dicha estructura física no puede alterarse.
Las «views» tienen fundamentalmente dos utilidades:
• Presentar los datos de las tablas en una estructura más próxima al concepto que posee el usuario de
una aplicación sobre cómo se estructuran dichos datos. Esto es debido a que, por motivos de norma-
lización de tablas, es posible que se llegue a una fragmentación que suponga constantes operaciones
de «join» a la hora de recuperar dichas tablas. También puede ser un medio de nominar instruccio-
nes SELECT cuya estructura sea de uso muy frecuente.
• Delimitar el acceso a los datos en conjuntos preestablecidos, de forma que se pueda establecer una
privacidad lógica sobre ellos. Dado que la tabla derivada resultante de la «view» consta de columnas
y filas, esta delimitación de acceso se puede aplicar en uno o en ambos sentidos. Sobre columnas
significa no acceder a determinados datos de cada entidad (tabla). Por ejemplo: Ver el precio de ven-
ta de un artículo, pero no el de coste. Aplicado este criterio a las filas, significa acceder a un subcon-
junto de las entidades posibles en una tabla. Por ejemplo: Un departamento sólo puede ver sus filas.
IMPORTANTE
Una «view» no tiene «rowid».
Asimismo, una «view» puede utilizarse para modificar la(s) relación(es) base, ya sea insertando nuevas «tu-
plas», ya actualizando las existentes o borrándolas. Esto supone que cualquiera de estas operaciones se des-
compondrá en las correspondientes sobre las tablas base que la componen (tablas relacionadas en la instruc-
ción SELECT de creación de la «view») y no sobre la «view», que es el resultado de la instrucción SELECT aso-
ciada a ella.
Antes de la creación de una «view» destinada a la actualización de tablas, es imprescindible una definición cla-
ra de la integridad referencial de las tablas base implicadas (claves primarias —«primary keys»— y claves refe-
renciales —«foreign keys»—).
• La «lista_select» de la instrucción SELECT que define la «view» no puede incluir expresiones. Un va-
lor agregado se considera una expresión.
• Asimismo, la instrucción SELECT que define la «view» no puede incluir las siguientes cláusulas: «DIS-
TINCT», «GROUP BY» y «HAVING».
• Ninguna columna de la tabla base y no incluida en la «view» debe estar definida como «NOT NULL».
• Debe incluir la clave primaria («primary key») de la tabla base o aquellas columnas que identifiquen
unívocamente la tupla «fila» en la tabla.
Pág. 37
MultiBase. Manual del SQL
Si, por el contrario, la «view» que se desea actualizar y/o la «view» base incluye(n) algún «join» (enlace con
otra tabla), se aplicarán las restricciones de la actualización de «views» derivadas de más de una tabla. Estas
restricciones se comentan en el siguiente epígrafe.
INSERT:
Si existe clave en A y no en B o a la inversa:
• Los datos del que existen deben coincidir. Si fuera así, no se insertaría.
• INSERT sobre la tabla que no existe.
DELETE:
• Si existen en ambas tablas, se borran.
UPDATE:
• Si existen en ambas tablas, se actualizan.
• En caso de no existir se produce un INSERT.
b) En actualización de un «join» con relación entre clave primaria («primary key») en «A» y clave referencial
(«foreign key») en «B»:
INSERT:
Si no existe una clave para ninguno de A y B:
DELETE:
No se puede borrar A.
Si la(s) columna(s) del «join» en B está(n) definida(s) como «NOT NULL»:
• DELETE de B.
Si ésta(s) admite(n) nulos:
UPDATE:
No se puede actualizar A.
Si existe B:
• UPDATE sobre B.
Fila o «tupla»
Una fila es un conjunto vacío de valores tal que:
Esta instrucción se encarga de agregar una fila en la tabla «provincias» de la base de datos de demostración. En
el ejemplo anterior, la fila sería la siguiente:
Base de datos
Una base de datos es un conjunto de tablas variables con el tiempo que representa un conjunto de entidades
de un sistema de información.
Pág. 39
MultiBase. Manual del SQL
El CTSQL sólo puede tener abierta una base de datos, por lo que el CTSQL sólo podrá acceder a la información
grabada en las tablas que la componen. Como ya se verá en la instrucción DATABASE del CTSQL, al abrir una
base de datos se cerrará automáticamente la que estaba abierta hasta ese momento.
ROWID
El «rowid» es el número de fila en una tabla, y la forma más rápida de acceso a ésta, por lo que su utilización es
prioritaria. Por ejemplo, la tabla de «provincias» en la base de datos de demostración incluye la siguiente in-
formación:
Provincia Descripción Prefijo ROWID
1 ALAVA 945 1
2 ALBACETE 967 2
3 ALICANTE 965 3
4 ALMERIA 951 4
33 ASTURIAS 985 5
5 AVILA 918 6
6 BADAJOZ 924 7
7 BALEARES 971 8
8 BARCELONA 93 9
Índices
El esquema relacional no incluye definición alguna sobre el método de acceso a los datos, por lo que las ins-
trucciones relativas a índices incluidas en CTSQL son una extensión de éste. Los motivos de definición de un
índice son fundamentalmente los siguientes:
• Los índices juegan un papel fundamental en el enlace de tablas, por lo que es preferible que exista al
menos un índice en la tabla que pueda tener más filas.
Integridad
De un modo genérico, y referido al esquema lógico de una base de datos, se puede definir la integridad como la
consistencia de las definiciones que forman dicho esquema y de la utilización de las mismas.
Los niveles de integridad que pueden considerarse son los siguientes:
• Integridad de Diccionario.
• Integridad de Tablas.
• Integridad Referencial.
En los siguientes epígrafes se explica en detalle cada uno de estos niveles.
Integridad de diccionario
Este tipo de integridad se refiere a la definición de características de una columna de forma centralizada en el
diccionario de la base de datos (tablas «sys*»). Se corresponde con los atributos de columnas que suponen
restricciones de los valores posibles para esa columna (INCLUDE y CHECK), formato de la misma (FORMAT y
PICTURE), operaciones posibles con ella (NOENTRY y NOUPDATE), etc.
Mediante la centralización de dichos atributos de columnas, que son mantenidos automáticamente por CTSQL,
se evita incluir estas definiciones en los distintos programas CTL que manejen dichas columnas, asegurando así
la integridad de éstas en todo el desarrollo de la aplicación.
Integridad de tablas
Considerando una relación «tabla» como un conjunto de objetos del mismo tipo, y un «tupla» («fila») como
una instancia de esa relación, podemos definir la integridad de tablas como la ausencia de redundancias en esa
relación, esto es, cada «tupla» es única en esa relación.
Pág. 41
MultiBase. Manual del SQL
Así, en el diseño de una base de datos se utiliza el procedimiento de normalización de tablas, cuyo objetivo es
agrupar los distintos fragmentos de información redundantes en nuevas tablas. En este procedimiento surge el
concepto fundamental de clave primaria («primary key»), que no es otra cosa sino la columna o combinación
de columnas de una tabla que identifican unívocamente a cada una de sus filas. Dado el carácter unívoco de
esta clave, ésta no podrá contener valores nulos en ninguna de sus columnas.
Integridad referencial
La integridad referencial es la consistencia de datos entre las «tuplas» de dos o más tablas. La relación entre
dichas tablas se establece mediante una o varias columnas de enlace (máximo ocho), estableciendo así una
relación de interdependencia entre ellas. Dichas columnas de enlace («join») serán los componentes de la defi-
nición de la clave primaria («primary key») en una tabla y de las claves referenciales («foreign keys») en el resto
de tablas.
Por tanto, la clave primaria («primary key») de una tabla es un índice único, simple o compuesto, una o varias
columnas, respectivamente, cuyos componentes identifican una fila. La clave primaria («primary key») de una
tabla se crea mediante las instrucciones CREATE TABLE o ALTER TABLE del CTSQL.
Las claves referenciales son los identificadores que establecen la relación con las claves primarias («primary
key») de otra tabla. Esta relación consiste en controlar la existencia de ciertos datos en la tabla que contiene la
clave primaria («primary key») para poder utilizarlos en la tabla con claves referenciales («foreign keys»). Dicha
relación se controla a nivel de modificaciones (UPDATE) y borrado (DELETE) de filas en la tabla con la clave pri-
maria («primary key»). Las acciones que se pueden tomar son:
• RESTRICT: Si el operador intenta borrar o modificar una fila en la tabla que contiene la clave prima-
ria, la cual establece la integridad referencial, y dichos valores existen en otras tablas que contienen
claves referenciales (RESTRICT) significa que dicha operación no será permitida.
• SET NULL: Esta opción indica que la actualización o el borrado sobre la tabla que contiene la clave
primaria, la cual establece la integridad referencial, implicará el poner a nulos todas aquellas colum-
nas componentes de la clave referencial («foreign key») en las filas afectadas.
Por ejemplo, las tablas de «clientes» y «proveedores» referencian a la tabla de «provincias» para controlar la
integridad de datos. La estructura de estas tablas es la siguiente:
create table provincias(
provincia smallint not null label "Cod. Provincia",
descripcion char(20) label "Provincia",
prefijo smallint label "Prefijo")
primary key (provincia) ;
create table clientes(
cliente integer not null label "Codigo Cliente",
empresa char(25) upshift label "Empresa",
apellidos char(25) label "Apellidos",
nombre char(15) label "Nombre",
direccion1 char(25) label "Direccion1",
direccion2 char(25) label "Direccion2",
poblacion char(15) label "Poblacion",
provincia smallint label "Provincia",
distrito integer label "Distrito",
telefono char(13) label "Telefono",
formpago char(2) label "Forma de Pago",
total_factura money(11,2) label "Total Facturado")
primary key (cliente) ;
Como se puede comprobar en estas tablas, existe una columna común a todas ellas. Esta columna es el código
de la provincia, denominada «provincia» en las tres tablas: «provincias», «clientes» y «proveedores». En este
caso, el nombre de la columna que establece la relación coincide en las tres tablas, si bien esta denominación
común no es obligatoria. Por contra, lo que sí es necesario es que dicha columna coincida en tipo y longitud de
dato SQL en las tres tablas. En nuestro ejemplo, las tres columnas «provincia» son SMALLINT.
La columna «provincia» en la tabla de «provincias» es el componente de la clave primaria («primary key»). Esto
significa que no pueden existir dos provincias con el mismo código, es decir, establece la «unicidad» de datos.
primary key (provincia);
Sin embargo, la columna «provincia» en las tablas de «clientes» y «proveedores» forma dos claves referencia-
les: «for1_cli» y «for1_pro», respectivamente, estableciendo relación con la tabla de «provincias». Esta rela-
ción siempre será por medio de la clave primaria:
alter table clientes foreign key for1_cli (provincia) references provincias
on update restrict
on delete restrict ;
alter table proveedores foreign key for1_pro (provincia) references provincias
on update restrict
on delete restrict ;
Como se puede comprobar en estos ejemplos, ambas claves referenciales se crean con la cláusula RESTRICT
tanto en actualización como en borrado (UPDATE y DELETE, respectivamente). Esto significa que si el operador
intenta borrar o modificar el código de la provincia en la tabla de «provincias» a la que pertenece algún «clien-
te» o «proveedor», dicha operación se cancelará de forma automática (RESTRICT).
Información en la tabla de «provincias»:
select provincia, descripcion from provincias
where provincia between 18 and 22;
Pág. 43
MultiBase. Manual del SQL
Empresa Provincia
ATC 19
LEM 18
PEREIRA 18
RTP 19
JULIO SOTO 18
MACARRON S.A. 19
ROBLES 19
REXPACS 18
TABLAS 20
ALFOMBRAS MAS 19
Empresa Provincia
VENTAUTO 18
BARNICES DEVA 21
ALFOMBRAS MAS 19
AGRUGASA 19
TYLER 18
GUYON 19
CARO 19
TABLAS 19
HORARIO 19
En este caso, no se podrán modificar o borrar filas en las provincias que se indican a continuación, ya que exis-
ten «clientes» o «proveedores» con el mismo código de provincia:
18 GRANADA
19 GUADALAJARA
20 GUIPUZCOA
21 HUELVA
Sin embargo, el código «22», que corresponde a «HUELVA», podrá modificarse siempre y cuando el nuevo
código asignado sea un dato único en la tabla. Asimismo, dicha fila también podrá borrarse si lo desea el ope-
rador. Una vez identificadas las claves primarias y referenciales surge la necesidad de definir qué acciones to-
mar cuando se presente algún caso de los mencionados. Dichas acciones afectan a ambas tablas, puesto que
son interdependientes. En este caso consideraremos las siguientes situaciones:
• Se actualiza cualquiera de las columnas que componen la clave primaria («primary key») en la tabla
referenciada. En el ejemplo, «provincias».
• Se borra cualquiera de las columnas de la clave primaria («primary key») de la tabla referenciada.
• Se insertan filas en la tabla referenciante, «clientes» o «proveedores», sin enlace en la tabla referen-
ciada («provincias»).
Las acciones a tomar serían:
IMPORTANTE
La cláusula «primary key» implica la creación automática de un índice único, no siendo así en el
caso de la «foreign key», por lo que en ciertas ocasiones es recomendable crear uno (único o du-
plicado). Esto se debe a que dichas columnas serán las que enlazarán ambas tablas, es decir, las
que realizarán el «join». El índice puede ser único o duplicado, siendo este último el más lógico
en este planteamiento.
Transacciones
La seguridad física y lógica de los datos es un aspecto primordial en una aplicación de base de datos. Son mu-
chas las causas que pueden dañar una base de datos, dejándola en un estado inconsistente e incluso inaccesi-
ble, por ejemplo fallos del sistema, tales como «caídas del sistema», sobrecarga de procesos, memoria de pagi-
nación insuficiente, terminación anormal de CTSQL, etc.
Para sistematizar la seguridad de los datos de una base de datos ante este tipo de problemas, se pueden acti-
var, opcionalmente, las transacciones.
Una transacción es el conjunto de operaciones relativas a la recuperación y actualización sobre la base de datos
que se realizan de forma unitaria e indivisible, esto es, o se realizan totalmente o no se realizan. En otras pala-
bras, es el modo de agrupar varias instrucciones DML (INSERT, DELETE y UPDATE) y asegurar que dicho grupo
se ejecute completamente. Para ello hay que marcar explícitamente el principio y fin de dicho grupo de ins-
trucciones mediante las siguientes:
Esta instrucción convierte en transaccional una base de datos creada previamente sin transacciones.
Asimismo, en una base de datos definida con transacciones sirve para cambiar el fichero transaccio-
nal («log file»). Es decir, asigna un nuevo fichero transaccional a una base de datos que no lo tuviera
o crea uno nuevo.
La consecuencia práctica de estas instrucciones es que en el fichero transaccional («log file») se registrarán
tanto el comienzo de la transacción como su fin. Esta operación permite:
Pág. 45
MultiBase. Manual del SQL
una actualización (UPDATE), aunque ésta sea absurda (por ejemplo, actualizar una fila dejando el mismo valor).
En este momento dicha fila se encuentra bloqueada para el resto de usuarios que tengan acceso a la base de
datos.
NOTAS:
1.— Hay que tener en cuenta que cada sistema operativo UNIX/LINUX tiene un número máximo de bloqueos
(«tunable parameter»), por lo que habrá que ajustar éste o utilizar, en su caso, el bloqueo de tabla (LOCK TA-
BLE) que inhibe el bloqueo de fila.
2.— En Windows sólo existe la posibilidad de bloqueo en las versiones para red local. En estos sistemas, el co-
mando share es el que permite el bloqueo de filas y tablas. Dicho comando tiene un número máximo de blo-
queos (parámetro «/L»), por lo que habrá que ajustarlo o utilizar, en su caso, el bloqueo de tabla (LOCK TABLE)
que inhibe el bloqueo de fila.
Valores nulos
En CTSQL se considera valor nulo («NULL») de una columna a la ausencia de valor dentro del rango correspon-
diente (esto es, los valores posibles para su tipo de dato), o también valor desconocido. Para detectar un valor
nulo en una columna, existe en CTSQL un operador explícito. Su sintaxis es la siguiente:
IS [NOT] NULL
Cualquier expresión que contenga operadores aritméticos devolverá «NULL» si al menos uno de los operandos
lo es. Por ejemplo:
select pr_vent - pr_cost from articulos
En este ejemplo, en el momento en que cualquiera de los dos operandos («pr_vent» o «pr_cost») sea nulo
(«NULL»), el resultado de dicha operación también será nulo.
En la base de datos, CTSQL utiliza el valor nulo («NULL») como valor por defecto en la inserción de nuevos re-
gistros, en el supuesto de que no se hubieran especificado los siguientes atributos en la creación de la tabla
para las columnas a las que no se asigne ningún valor:
• NOT NULL: La columna que tenga asignado dicho atributo no puede contener un valor nulo en nin-
guna de las filas de la tabla.
• DEFAULT: Este atributo permite asignar un valor por defecto a una columna en caso de no asignarle
ningún valor en la inserción de la fila.
Estos atributos se han explicado anteriormente en este mismo capítulo.
El tratamiento de los valores nulos en las distintas instrucciones CTSQL es el siguiente:
1.— Cláusula «WHERE»: En este caso es posible utilizar el operador antes mencionado («IS [NOT] NULL»). En
cualquier otro no se recuperarán filas cuyos valores en la columna a la que se aplica la condición sean nulos.
2.— Cláusula «ORDER BY»: Si la columna por la que se ordena contiene valores nulos, éstos se considerarán
menores que cualquier otro valor de la misma columna; así, con ordenación ascendente (ASC) aparecerán al
principio, mientras que con descendente (DESC) lo harán al final.
3.— Cláusula «GROUP BY»: Cada fila que contenga valores nulos en la columna por la que se agrupa será consi-
derada un grupo distinto, ya que su valor es desconocido.
4.— Valores agregados: El valor agregado «count(*)» devuelve siempre el número de filas de la tabla, incluyen-
do los nulos. Si para una columna «x» existen valores nulos y no nulos, «count(distinct x)», «avg(x)», «sum(x)»,
«min(x)» y «max(x)» devuelven los valores correspondientes a su función, ignorando las filas con valores nulos
para «x». Si «x» sólo contiene valores nulos, «count(distinct x)» devolverá cero, y el resto de valores agregados
nulo («NULL»).
El enlace de la tablas «clientes» y «albaranes» se realiza siempre en la cláusula «WHERE». Como se ha indicado
anteriormente, en este enlace interviene la columna «cliente» de la tabla «clientes», la cual compone la clave
primaria («primary key») de dicha tabla, mientras que la columna «cliente» de la tabla «albaranes» compone
una clave referencial («foreign key») de ésta.
En el ejemplo anterior, se obtienen todos los clientes que tengan albaranes y sus correspondientes albaranes.
No obstante, puede haber clientes a los que aún no se les haya suministrado nada. Éstos no aparecerán en el
resultado de esta instrucción SELECT (es como si se «perdieran» datos —suponiendo que lo que se quiere re-
almente es listar los clientes, tengan o no albaranes—).
Para evitar esta aparente «pérdida de información», CTSQL dispone de los «outer joins», cuya función es recu-
perar en una operación de enlace de tablas todas las filas de una de tabla, tengan o no enlace en la otra. En
nuestro ejemplo, si hubiéramos utilizado un «outer join», éste nos habría devuelto todos los clientes, tanto los
Pág. 47
MultiBase. Manual del SQL
que tuvieran albaranes como los que no (éstos aparecerían una sola vez con la columna de la tabla de enlace
«albaranes» a nulo). El aspecto de esta instrucción de lectura SELECT es la siguiente:
select clientes.empresa, albaranes.fecha_albaran
from clientes, outer albaranes
where clientes.cliente = albaranes.cliente
Así pues, como características de un «outer join» podemos definir las siguientes:
• Un «outer join» implica, al menos, dos tablas, una de las cuales es «dominante» (en la que se preser-
va la clave de enlace) y otra «subordinada».
• Todas las filas de la tabla dominante estarán presentes en la proyección resultante, tengan o no en-
lace con la tabla subordinada. Aquellas que no tengan enlace en dicha tabla se completarán con va-
lores nulos en las columnas correspondientes en la tabla subordinada.
• Una operación de «outer join» que implique más de dos tablas puede descomponerse en operacio-
nes análogas entre dos tablas, es decir, se resuelve el enlace entre dos tablas y a continuación el en-
lace entre la tabla resultante y una tercera, y aplicando a esta nueva tabla resultante otro enlace con
otra tabla, y así sucesivamente. Para ello hemos de considerar los distintos enlaces agrupados en ni-
veles por las siguientes reglas de precedencia:
El orden de las tablas en la cláusula «FROM» es significativo, evaluándose de izquierda a dere-
cha (precedencia implícita).
Los enlaces entre paréntesis tienen precedencia sobre otros enlaces (precedencia explícita).
Los «inner join» se consideran en el mismo nivel y tienen precedencia sobre los «outer join»
(interno en oposición a «outer» o externo; en el «inner join» no se preservan las claves que no
tengan enlace).
Por su parte, los «outer join» implican un nuevo nivel en dependencia de las tablas relaciona-
das (ver representaciones gráficas más adelante).
Veamos a continuación los casos posibles de enlaces entre tres tablas, sus restricciones y la resolución de dicho
enlace. En éstos la representación del operador de enlace es «op», significando cualquier operador de compa-
ración.
Consideraremos tres tablas «A», «B» y «C», cuyas columnas de enlace son respectivamente «a», «b» y «c».
Asimismo, se representan gráficamente las tablas en distintos niveles en dependencia del tipo de enlace. El
signo «Ø» representa el «outer join».
Supongamos como ejemplo los siguientes contenidos para las tablas «A», «B» y «C»:
A.a B.b C.c
1 2 3
2 3 4
3 4 5
IMPORTANTE
Las formas de resolución que se exponen en los ejemplos que siguen no son necesariamente las
que utiliza CTSQL; se han indicado éstas por claridad en el planteamiento del problema.
Supuesto 1º:
FROM A, B, C
WHERE …
Éste es el «inner join», donde no se precisa ninguna restricción en la cláusula «WHERE», es decir, se realizará el
producto cartesiano de las tres tablas. Si se especifica un enlace, entonces será la intersección de las tablas
implicadas y el producto cartesiano de la resultante con la tercera tabla, para la que no se especificó enlace. Si
se especifican los enlaces entre «A» y «B» y entre «B» y «C», entonces será la intersección de las tres.
Ejemplo: «Inner join» de «A» y «B». Su resultado producto cartesiano con «C»:
select *
from A, B, C
where a = b
Resultado:
a b c
2 2 3
2 2 4
2 2 5
3 3 3
3 3 4
3 3 5
Supuesto 2º:
FROM A, B, OUTER C
WHERE (a OR b) op c
En el ejemplo anterior, «A» y «B» están «relacionadas» al mismo nivel. Debe existir un enlace entre «A» y «C»
o entre «B» y «C». Antes de aplicarse este enlace (el «outer»), se resolverá el enlace entre «A» y «B» para efec-
tuar el «outer join» sobre la tabla resultante y «C».
Ejemplo: Producto cartesiano de «A» y «B» y «outer join» de su resultado con «C» enlazando con la clave de
«A»:
select *
from A, B, outer C
where a = c
Resultado:
a b c
1 2 null
1 3 null
1 4 null
2 2 null
2 3 null
2 4 null
3 2 3
3 3 3
3 4 3
Supuesto 3º:
3- FROM A, OUTER B, OUTER C
WHERE a op b and a op c
Aunque «B» y «C» están en el mismo nivel, no están conectadas por ningún operador. La cláusula «WHERE»
debe incluir un enlace entre «A» y «B» y otro entre «A» y «C». Primero se resolverá el «outer join» entre «A» y
«B» y sobre esta tabla resultante aplicar el «outer join» con «C».
Pág. 49
MultiBase. Manual del SQL
Ejemplo: «Outer join» de «A» y «B», «outer join» de su resultante con «C»:
select *
from A, outer B, outer C
where a = b and a = c
Resultado:
a b c
1 null null
2 2 null
3 3 3
Supuesto 4º:
FROM A, OUTER (B, C)
WHERE a op (b OR c)
A ---- Ø nivel 1
|
B ---- C nivel 2
Hay que considerar en el gráfico de este caso que las posiciones de «B» y «C» son intercambiables, lo que su-
pone la exigencia de incluir un enlace entre la tabla de nivel 1 («A») y alguna de las de nivel 2 («B» o «C») o con
ambas. Además, «B» y «C» pueden tener algún enlace entre ellas (lo que supondría una mayor restricción). La
resolución de este enlace es: el «inner join» de «B» y «C» y el «outer join» de su resultado con la tabla «A» a
través de la clave de «B» o «C», según se indique en la cláusula «WHERE».
Ejemplo: Producto cartesiano de «B» y «C», «outer join» de su resultado con «A»:
select *
from a, outer (b, c)
where a = c
Resultado:
a b c
1 null null
2 null null
3 2 3
3 3 3
3 4 3
Supuesto 5º:
FROM A, OUTER (B, OUTER C)
WHERE a op b AND b op c
A ---- Ø nivel 1
|
B ---- Ø nivel 2
|
C nivel 3
La cláusula WHERE debe incluir dos enlaces: uno entre «A» y «B» y otro entre «B» y «C». La resolución de este
enlace es: resolver el «outer join» entre «B» y «C», siendo la tabla resultante la tabla subordinada en el «outer
join» con «A».
Ejemplo: «Outer join» de «B» y «C», y «outer join» de su resultado con «A»:
select *
from a, outer (b, outer c)
where a = b and b = c
Resultado:
a b c
1 null null
2 2 null
3 3 3
Pág. 51
MultiBase. Manual del SQL
Alias
Un «alias» es un nombre alternativo asignado en una instrucción de lectura SELECT a una tabla o columna. La
construcción de un «alias» es como un identificador más de la base de datos. A partir de la versión 2.0 de Mul-
tiBase, estos identificadores pueden emplear caracteres en mayúscuas. Este nombre alternativo es válido úni-
camente para dicha instrucción SELECT. Equivale a un renombrado de tabla o columna en ejecución, y es tem-
poral mientras se está ejecutando la instrucción de lectura.
Un «alias» de columna se define en la «lista_select» de la instrucción SELECT, y sólo puede emplearse en la
cláusula «ORDER BY» de aquélla. Un «alias» de columna se emplea para mostrar una etiqueta distinta de la
columna a la que se le asigna cuando se muestra en una ventana la tabla derivada devuelta por su lectura.
Por su parte, un «alias» de tabla es especialmente útil cuando se desea enlazar una tabla consigo misma. Un
«alias» de tabla se define en la cláusula «FROM» de una instrucción SELECT.
Un «alias» se define mediante un espacio en blanco entre su nombre y el de la columna o tabla en cada caso.
Por ejemplo:
a) Alias de columna:
select provincia Codigo, descripcion from provincias
En este ejemplo, se ha definido un «alias» («Codigo») a la columna «provincia». A la hora de presentar la tabla
derivada devuelta por esta instrucción SELECT, la etiqueta de dicha columna será «Codigo» en lugar de «Cod.
provincia» («label» de dicha columna).
b) Alias de tabla:
select provincias.descripcion, prov2.descripcion
from provincias, provincias prov2
where provincias.provincia = 28
and prov2.provincia = 19
En este ejemplo, se ha definido un «alias» a la tabla «provincias» que se lee dos veces en esta instrucción. El
«alias» es el que distingue entre ambas tablas que físicamente es una sola. Esta instrucción genera una tabla
derivada compuesta por dos columnas, cada una de las cuales contiene el nombre de una provincia distinta:
«MADRID» y «GUADALAJARA».
Sinónimos
Un sinónimo es un nombre alternativo asignado a una tabla de la base de datos. Cuando se asigna un sinónimo
a una tabla, ésta podrá ser llamada indistintamente por su propio nombre o por el sinónimo.
Este concepto es similar al explicado anteriormente para el «alias» de una tabla. La diferencia principal entre
ambos conceptos es que el sinónimo persiste hasta la ejecución de la instrucción DROP SYNONYM, mientras
que el «alias» sólo es válido para la instrucción de lectura SELECT que lo define.
Un sinónimo se crea mediante la instrucción CREATE SYNONYM y se borra de la base de datos mediante DROP
SYNONYM.
Pág. 53
MultiBase. Manual del SQL
Introducción
En este capítulo se explica la forma de utilizar las instrucciones del CTSQL en ficheros SQL. Dichas instrucciones
son las que se detallan más adelante en este mismo volumen en los capítulos correspondientes a los distintos
sublenguajes del CTSQL: DDL, DCL, QL y DML.
• Una instrucción CTSQL puede introducirse en tantas líneas como se precisen. Es decir, cada palabra
que constituye una instrucción CTSQL podría introducirse en una línea, pulsando [RETURN] después
de cada una de ellas.
• Asimismo, puede utilizarse la barra espaciadora o el tabulador para la indentación de las instruccio-
nes. Por ejemplo:
select provincia, descripcion, prefijo
from provincias
where provincia between 1 and 10;
• Las constantes de tipo alfanumérico (CHAR), hora (TIME) o fecha (DATE) tienen que encerrarse entre
comillas, simples o dobles ( ' ' o " " ). Por ejemplo:
"27/06/1965" Fecha
"16:10:01" Hora
"Manuel Ruiz López" Alfanumérico
• Una instrucción CTSQL puede introducirse en mayúsculas o minúsculas. Sin embargo, en aquellos va-
lores constantes entrecomillados sí existe diferencia entre minúsculas y mayúsculas:
sElEcT CLIENTE, empresa, DIRECCION1 from clientes
WHERE empresa = "CIFERSA";
El resultado de estas dos instrucciones de CTSQL es idéntico, ya que el valor constante a buscar está
escrito en mayúsculas. El resto de la instrucción es indiferente cómo se escriba.
• En caso de introducir más de una instrucción CTSQL en el mismo fichero, éstas tienen que separarse
por un punto y coma («;»). Este signo es opcional en la última instrucción especificada.
• Cuando se ejecutan varias instrucciones de CTSQL seguidas y alguna de ellas tiene interacción con el
operador, para continuar con la ejecución de las siguientes hay que pulsar, por defecto, la tecla [F2]
en UNIX/LINUX o [ESC] en Windows, o bien la tecla correspondiente a la acción <fquit> en el fichero
de acciones «Tactions» (que se encuentra en el subdirectorio «etc» de MultiBase).
Ficheros «.sql»
Un fichero con extensión «.sql» contendrá instrucciones pertenecientes al CTSQL. En estos ficheros no se pue-
den incluir datos variables. Si un fichero incluye más de una instrucción CTSQL, cada una de ellas, y opcional-
mente la última, tiene que finalizar en un punto y coma («;»). Por ejemplo:
create table provincias(
provincia smallint not null label "Cod. Provincia",
descripcion char( 20) label "Provincia",
prefijo smallint label "Prefijo")
primary key (provincia);
La forma de ejecutar un fichero con extensión «.sql» es mediante la instrucción TSQL en programas CTL.
Módulos CTL
Todas las instrucciones propias del CTSQL pueden incluirse en los módulos de programación del lenguaje de
cuarta generación CTL. A la hora de desarrollar una aplicación, al manejar los datos de la base de datos hay que
incluir las instrucciones de CTSQL correspondientes en el módulo de programación.
Los objetos y herramientas propios del CTL para comunicación con el CTSQL se explican más adelante en este
mismo volumen dentro del capítulo «Comunicación con CTL».
NO EJECUTAR NUNCA:
# kill -9 132
En caso de abortar el proceso CTSQL podría peligrar la integridad de datos entre los ficheros «.dat» e «.idx»
correspondientes a la tabla que se maneja en ese momento, e incluso la de la base de datos.
Pág. 55
MultiBase
MANUAL DEL SQL
Pág. 57
MultiBase. Manual del SQL
Introducción
El lenguaje de definición de datos (DDL) es el sublenguaje del CTSQL encargado de la definición y mantenimien-
to de aquellos elementos SQL que se manejarán en el desarrollo de la aplicación. Dichos elementos son los si-
guientes:
• Bases de datos.
• Tablas.
• Filas o «tuplas».
• Columnas.
• Índices.
• Sinónimos.
• «Views».
En este capítulo se indican las características y operaciones que se pueden realizar con cada uno de ellos.
IMPORTANTE
Todas las características expuestas en este capítulo sobre instrucciones de SQL corresponden al
gestor de base de datos de MultiBase (CTSQL).
Bases de datos
Las operaciones que se pueden llevar a cabo sobre una base de datos son:
Esta instrucción crea un subdirectorio cuyo nombre será igual al de la base de datos indicada en «base_datos»
más la extensión «.dbs» («base_datos.dbs»). Este subdirectorio se genera por defecto dentro del directorio en
curso. No obstante, en caso de tener activa la variable de entorno DBPATH (ver capítulo «Variables de Entor-
no» en el Manual del Administrador), este subdirectorio se generará por defecto en el primer directorio indica-
do en dicha variable.
Dentro del directorio «base_datos.dbs» se crean asimismo unos ficheros, con extensiones «.dat» e «.idx», que
se corresponden con las tablas del catálogo de la base de datos (tablas «sys*»).
Ejemplo:
create database almacen
Esta instrucción crea un directorio, denominado «almacen.dbs», dentro del cual se generarán las tablas del
catálogo de la base de datos («sys*»).
La base de datos que se genera por defecto no es transaccional. En caso de que deseásemos crear una base de
datos transaccional habría que ejecutar necesariamente la instrucción CREATE DATABASE del CTSQL con la
cláusula «WITH LOG IN». Por ejemplo:
create database almacen with log in "/usr/ctldemo/lunes"
Esta instrucción generaría el fichero «/usr/ctldemo/lunes» para recoger las transacciones realizadas sobre la
base de datos.
Asimismo, todas las bases de datos que se generen utilizarán la tabla de caracteres «GCS#2» de IBM para las
fuciones de ordenación. Si se desea modificar esta tabla habrá que utilizar la cláusula «COLLATING» junto al
nombre del fichero compilado con la secuencia de ordenación deseada. En el subdirectorio «msg» existe un
ejemplo de este tipo de ficheros, cuya estructura se comenta en el capítulo «Otros Ficheros de Administración»
en el Manual del Administrador. La compilación de este fichero de ordenación se tiene que realizar desde el
sistema operativo mediante el comando tcollcomp, cuya sintaxis se explica en el capítulo dedicado a «Coman-
dos» dentro del Manual del Administrador).
Tablas
Las operaciones que se pueden realizar sobre las tablas son:
• Creación.
• Definición de la integridad referencial.
• Modificación de la estructura.
• Renombrado.
• Borrado.
En los epígrafes que siguen se comentan en detalle cada una de estas operaciones.
Creación de tablas
Una vez creada la base de datos según lo explicado anteriormente, el siguiente paso a realizar para el desarro-
llo de una aplicación consiste en la creación de las tablas que compondrán dicha base de datos. La creación de
estas tablas se realiza mediante la instrucción CREATE TABLE del CTSQL, cuya sintaxis es la siguiente:
CREATE [TEMP] TABLE nombre_tabla (
nombre_columna tipo_sql atributos,
nombre_columna tipo_sql atributos, …)
[IN "directorio"]
[PRIMARY KEY (lista_columnas)]
[FOREIGN KEY identificador (lista_columnas)
REFERENCES nombre_tabla
ON UPDATE {RESTRICT | SET NULL}
ON DELETE {RESTRICT | SET NULL}]
[FOREIGN KEY …]
Pág. 59
MultiBase. Manual del SQL
Para poder crear tablas en una base de datos es necesario haber seleccionado ésta previamente. Ejemplo:
create table provincias(
provincia smallint not null label "Cod. Provincia",
descripcion char(20) label "Provincia",
prefijo smallint label "Prefijo");
create table clientes(
cliente integer not null label "Codigo Cliente",
empresa char(25) upshift label "Empresa",
apellidos char(25) label "Apellidos",
nombre char(15) label "Nombre",
direccion1 char(25) label "Direccion1",
direccion2 char(25) label "Direccion2",
poblacion char(15) label "Poblacion",
provincia smallint label "Provincia",
distrito integer label "Distrito",
telefono char(13) label "Telefono",
formpago char(2) label "Forma de Pago",
total_factura money(11,2) label "Total Facturado");
Estas dos instrucciones crean respectivamente las tablas «provincias» y «clientes», compuestas ambas por sus
correspondientes columnas con sus respectivos atributos. En el directorio que representa la base de datos se
habrán creado asimismo los cuatro ficheros correspondientes a ambas tablas (dos «.dat» y dos «.idx»). Para
comprobar la existencia de dichos ficheros ejecutar el siguiente comando:
a) En UNIX/LINUX:
$ cd almacen.dbs
$ ls provi* clien*
clien164.dat
clien164.idx
provi150.dat
provi150.idx
Directorio de C:\MBDEMO\ALMACEN.DBS
Directorio de C:\MBDEMO\ALMACEN.DBS
En estos momentos estas dos tablas no contienen información. El sublenguaje DML de CTSQL será el que se
encargue de agregar y borrar información sobre estas tablas.
Con esta relación entre claves primarias y referenciales se intenta evitar la inconsistencia de datos entre las
tablas de la base de datos. Dentro de este epígrafe consideraremos los siguientes apartados:
Este ejemplo crea la tabla de «clientes» con sus respectivas columnas y atributos. Asimismo, al final de ésta se
crea la clave primaria que identificará una fila de dicha tabla. La clave primaria estará compuesta por la colum-
na «cliente».
Ejemplo:
alter table clientes drop primary key;
Esta instrucción se encarga de borrar la clave primaria creada previamente con la instrucción CREATE TABLE en
el ejemplo anterior.
Pág. 61
MultiBase. Manual del SQL
Ejemplo:
create table clientes(
cliente integer not null label "Codigo Cliente",
empresa char(25) upshift label "Empresa",
apellidos char(25) label "Apellidos",
nombre char(15) label "Nombre",
direccion1 char(25) label "Direccion1",
direccion2 char(25) label "Direccion2",
poblacion char(15) label "Poblacion",
provincia smallint label "Provincia",
distrito integer label "Distrito",
telefono char(13) label "Telefono",
formpago char(2) label "Forma de Pago",
total_factura money(11,2) label "Total Facturado")
primary key (cliente)
foreign key for2_cli(formpago)
references formpagos
on update restrict
on delete restrict
foreign key for1_cli(provincia)
references provincias
on update restrict
on delete restrict ;
En este ejemplo, la instrucción CREATE TABLE genera la tabla «clientes», y a su vez su respectiva clave primaria
por la columna «cliente». Asimismo, esta instrucción crea dos claves referenciales restrictivas en actualización y
borrado que referencian a las tablas de «provincias» y «formpagos».
Ejemplo:
alter table clientes drop foreign key for2_cli;
Esta instrucción borra la clave referencial definida por la instrucción CREATE TABLE del ejemplo anterior.
Ejemplo:
alter table clientes foreign key for2_cli(formpago)
references formpagos
on update set null
on delete set null
En este ejemplo, se vuelve a crear la clave referencial «for2_cli», pero sin ser restrictiva con respecto a la tabla
«formpagos», sino que al actualizar o al borrar el código de la tabla referenciada dichos códigos en la tabla re-
ferenciante se actualizarán con valores nulos.
NOTA: Un tanto por ciento bastante elevado de las claves referenciales deberían crearse también como índices.
Por lo general, dichos índices serán duplicados.
La forma de modificar la estructura de una tabla es mediante la instrucción ALTER TABLE del CTSQL, cuya sin-
taxis es la siguiente:
ALTER TABLE nombre_tabla
[ADD (nombre_columna tipo_sql atributos
[BEFORE columna_existente],…)][,]
[DROP (columna_existente)][,]
[MODIFY (columna_existente atributos
[REMOVE lista_atributos],…)][,]
[PRIMARY KEY (lista_columnas)][,]
[FOREIGN KEY identificador (lista_columnas)
REFERENCES nombre_tabla
ON UPDATE {RESTRICT | SET NULL}
ON DELETE {RESTRICT | SET NULL}][,]
[DROP PRIMARY KEY][,]
[DROP FOREIGN KEY identificador][,]
En caso de borrar una columna que componga un índice o parte de él, dicho índice se borrará igualmente. Esta
regla se aplica también a las claves primarias y referenciales.
Cuando se efectúa una modificación en la estructura de una tabla, el nombre de sus ficheros físicos también se
modifica. Este cambio de nombre se produce al cambiar el número de identificador interno de dicha tabla, ya
que éste es parte del nombre de los ficheros. Este número se corresponde con la columna «tabid» de la tabla
«systables».
Asimismo, en el momento en que se produce una modificación de la estructura de una tabla se compactan sus
ficheros, eliminando todas aquellas filas marcadas que indican que se encuentran borradas por la instrucción
DELETE.
Ejemplo 1:
alter table clientes add (nif integer label "N.I.F.")
Esta instrucción agrega una columna nueva («nif») a la tabla «clientes» de la base de datos de demostración
«almacen». Esta columna será la última de las que componen la citada tabla. Si la tabla de «clientes» tuviese ya
filas grabadas, la columna «nif» se agregará con valores nulos («null») en todas ellas, ya que no se les asigna un
valor por defecto («default»).
Ejemplo 2:
alter table clientes modify (distrito zerofill)
Esta instrucción asigna el atributo ZEROFILL a la columna «distrito» de la tabla «clientes». Este atributo se en-
carga de rellenar con ceros los datos agregados en la columna «distrito».
Ejemplo 3:
alter table clientes drop (nif)
Pág. 63
MultiBase. Manual del SQL
La operación de renombrado de una tabla se realiza mediante la instrucción RENAME TABLE del CTSQL, cuya
sintaxis es la siguiente:
RENAME TABLE nombre_tabla TO nuevo_nombre_tabla
Ejemplo:
rename table formpagos to formapago
A partir de estos momentos, la tabla «formpagos» de la base de datos de demostración «almacen» se denomi-
nará «formapago».
Ejemplo:
drop table formpagos;
Esta instrucción eliminaría la tabla «formpagos» de la base de datos, tanto a nivel de estructura como de datos.
Filas o «Tuplas»
A nivel de filas, la única operación que puede realizarse desde el sublenguaje DDL del CTSQL es la actualización
de estadísticas para la optimización de los accesos a dicha tabla.
La actualización de estadísticas consiste en modificar la columna «nrows», perteneciente a la tabla del catálogo
de la base de datos «systables». El dato que se graba en dicha columna corresponde al número real de filas que
contiene la tabla o tablas en cuestión en ese momento. Esta operación la realiza la instrucción UPDATE STATIS-
TICS del CTSQL, cuya sintaxis es la siguiente:
UPDATE STATISTICS [FOR TABLE nombre_tabla]
Si no se especifica el nombre de la tabla de la cual se desean actualizar sus estadísticas, esta operación se reali-
zará sobre todas las tablas de la base de datos seleccionada.
Ejemplo:
select nrows from systables
where tabname = "clientes";
update statistics for table "clientes";
select nrows from systables
where tabname = "clientes";
Este ejemplo contiene tres instrucciones CTSQL para mostrar la ejecución de la instrucción UPDATE STATISTICS.
La primera y la última SELECT muestran el número de filas que el catálogo interpreta que tiene la tabla de
«clientes» antes y después de actualizar las estadísticas.
Columnas
Además de lo indicado a nivel de columnas en la creación o modificación de la estructura de las tablas, la única
operación que se puede realizar sobre una columna con el sublenguaje DDL es cambiarle el nombre.
El cambio de nombre de una columna puede implicar muchos problemas si la aplicación se encuentra acabada,
ya que habría que modificar el nombre de dicha columna en todos aquellos módulos CTL donde se hubiese
hecho referencia a ella.
La operación de renombrado de una columna se realiza con la instrucción RENAME COLUMN del CTSQL, cuya
sintaxis es la siguiente:
RENAME COLUMN nombre_tabla.nombre_columna TO nuevo_nombre_columna
Ejemplo:
rename column formpagos.descripcion to descrip
A partir de estos momentos, la columna «descripcion» de la tabla «formpagos» de la base de datos de demos-
tración «almacen» se denominará «descrip».
Índices
El sublenguaje DDL del CTSQL contempla únicamente dos operaciones respecto a los índices: creación y borra-
do.
Los índices pueden crearse y borrarse en cualquier momento durante el desarrollo de una aplicación. La ejecu-
ción de cualquiera de estas dos operaciones lleva implícita la modificación del fichero de índices («.idx»), cons-
truyendo o eliminando respectivamente su árbol correspondiente.
En los dos epígrafes que siguen se comentan en detalle cada una de estas operaciones.
Creación de índices
La creación de índices para las tablas de la base de datos se lleva a cabo con la instrucción CREATE INDEX del
CTSQL, cuya sintaxis es la siguiente:
CREATE [UNIQUE | DISTINCT] INDEX nombre_índice
ON tabla (columna [ASC | DESC] [, columna [ASC | DESC] [, …])
• El nombre del índice que se asigna en esta instrucción («nombre_índice») sirve para identificar dicho
índice a la hora de su posible borrado.
• Un índice puede construirse sobre una o más columnas (máximo 8) de una tabla, siendo simple o
compuesto, respectivamente.
• Si las columnas componentes de un índice pueden tener varios valores iguales en distintas filas de la
tabla, entonces a éste se le denomina «índice duplicado». Por el contrario, si una determinada com-
binación de valores sólo puede aparecer una vez en la tabla, entonces se llamará «índice único».
• La longitud máxima de un índice es 120 bytes, ya sea único o duplicado. Es decir, la suma de las lon-
gitudes en bytes de las columnas que lo componen no debe superar los 120 bytes.
• Al crear un índice se puede establecer el orden de recuperación de los datos asociados, ascendente
(por defecto) o descendente.
• No se puede crear un índice con una estructura (lista de columnas componentes) idéntica a la de
otro índice existente sobre la misma tabla.
• Un índice no se puede crear con columnas de distintas tablas de la base de datos.
• Un índice no se puede crear con parte de una columna.
• No se puede crear un índice duplicado, simple o compuesto, para una tabla en la que existan más de
32.767 filas con valores duplicados para el conjunto de columnas que componen dicho índice.
• Los tipos de datos de las columnas que componen un índice pueden ser distintos.
Pág. 65
MultiBase. Manual del SQL
IMPORTANTE
Una clave primaria es un índice único. Esto significa que no se puede crear un índice compuesto
con la(s) misma(s) columna(s) que la clave primaria. Sin embargo, una clave referencial NO es
índice en dicha tabla. No obstante, un tanto por ciento bastante alto de dichas claves referencia-
les deberían ser índices para la tabla.
Ejemplo:
create index i2_articulo on articulos(descripcion );
create index i3_articulo on articulos(articulo );
create index i4_articulo on articulos(proveedor );
create index i_artlineas on lineas(articulo,proveedor);
create index alblin_ind on lineas(albaran );
En este ejemplo se crean los índices sobre las tablas «articulos» y «lineas» de la base de datos de demostración
«almacen». Todos estos índices son duplicados y la mayoría simples, ya que están compuestos de una sola co-
lumna, excepto el índice «i_artlineas», que está compuesto por dos columnas («articulo» y «proveedor»).
Borrado de índices
El borrado de índices se efectúa mediante la instrucción DROP INDEX del CTSQL, cuya sintaxis es la siguiente:
DROP INDEX nombre_índice
El nombre del índice («nombre_índice») es el identificador que se le asigna en el momento de crearlo. Como ya
se ha indicado anteriormente, este nombre sólo sirve como referencia para la operación de borrado. Por ejem-
plo:
drop index i3_articulo
Este ejemplo borra uno de los índices creados en el ejemplo expuesto en el epígrafe anterior.
Sinónimos
Las dos únicas operaciones que se pueden llevar a cabo con los sinónimos desde el sublenguaje DDL del CTSQL
son las que hacen referencia a su creación y borrado.
En los dos epígrafes siguientes se explican en detalle cada una de estas operaciones.
Creación de sinónimos
La operación de creación de sinónimos de tablas se lleva a cabo a través de la instrucción CREATE SYNONYM
del CTSQL, cuya sintaxis es la siguiente:
CREATE SYNONYM sinónimo FOR tabla
Esta instrucción crea un sinónimo para la tabla especificada en «tabla». Este sinónimo persistirá en la base de
datos hasta que se borre.
Por ejemplo:
create synonym proclien from provincias;
create synonym proprove from provincias;
select provincia, descripcion, prefijo from proclien;
select provincia, descripcion, prefijo from proprove;
select provincia, descripcion, prefijo from provincias;
En este ejemplo se crean dos sinónimos para la tabla «provincias»: «proclien» y «proprove». Desde este mo-
mento, dicha tabla podrá ser invocada por tres nombres diferentes: «provincias», su nombre original, «pro-
clien» y «proprove». Esto se comprueba con la ejecución de las tres últimas instrucciones del ejemplo: Las ta-
blas derivadas generadas por cada una de las instrucciones de lectura SELECT son idénticas.
Borrado de sinónimos
El borrado de sinónimos se realiza mediante la instrucción DROP SYNONYM del CTSQL, cuya sintaxis es la si-
guiente:
DROP SYNONYM sinónimo
Por ejemplo:
drop synonym proclien;
Este ejemplo elimina uno de los dos sinónimos generados en el ejemplo del epígrafe anterior.
«Views»
Al igual que sucedía con los sinónimos y los índices, en el caso de las «views» el sublenguaje DDL del CTSQL
contempla únicamente la realización de las operaciones de creación y borrado.
En los dos epígrafes que siguen se comentan en detalle cada una de estas operaciones.
Creación de «views»
La creación de «views» se realiza mediante la instrucción CREATE VIEW, cuya sintaxis es la siguiente:
CREATE VIEW nombre_view [(columnas)]
AS instrucción_select [WITH CHECK OPTION]
La única forma de modificar la estructura de una «view» es borrándola y volviéndola a crear. La cláusula «WITH
CHECK OPTION» se emplea cuando la «view» es utilizada para actualizar las tablas originales que componen
dicha «view».
Una «view» persistirá en la base de datos hasta que se borre.
Ejemplo:
create view art_facturados(texto, totlinea)
as select descripcion,
sum(precio * (1 - descuento/100) * cantidad)
from lineas, articulos, albaranes
where articulos.articulo = lineas.articulo
and lineas.proveedor = articulos.proveedor
and albaranes.albaran = lineas.albaran
and albaranes.estado = "S"
group by 1 ;
select texto, totlinea from art_facturados;
Esta instrucción crea una «view» («art_facturados») en la aplicación de demostración («almacen»), compuesta
por dos columnas, denominadas «texto» y «totlinea». La tabla derivada se genera del enlace de tres tablas de
la base de datos: «lineas», «articulos» y «albaranes». Una vez creada la «view», todas las operaciones que se
pueden realizar sobre tablas también se podrán aplicar sobre ella.
La instrucción de lectura SELECT que acompaña a la creación de la «view» devuelve la tabla derivada que la
compone. Realmente, dicha lectura se provoca sobre las tablas «lineas, «articulos» y «albaranes».
La «view» generada en el ejemplo anterior no puede ser actualizada, ya que en la selección existen valores
agregados y, además, la instrucción SELECT que la genera incluye la cláusula «GROUP BY».
Pág. 67
MultiBase. Manual del SQL
NOTA: Para más información sobre restricciones y manejo de «views» consulte el capítulo 3 de este mismo
volumen.
Borrrado de «Views»
Para proceder al borrado de «view» habrá que ejecutar la instrucción DROP VIEW del CTSQL, cuya sintaxis es la
siguiente:
DROP VIEW nombre_view
Esta instrucción se encarga de eliminar del catálogo de la base de datos (tablas «sys») toda la información ge-
nerada por la instrucción CREATE VIEW. No obstante, hay que indicar que una «view» no tiene representación
física en el disco. La instrucción DROP VIEW es a las «views» lo que la DROP TABLE es a las tablas.
Ejemplo:
drop view art_facturados;
Pág. 69
MultiBase. Manual del SQL
Introducción
Dentro de la definición de una base de datos, el sublenguaje de control de acceso a datos del CTSQL contempla
los siguientes procesos:
• Permisos.
• Bloqueos.
En los epígrafes que siguen se comenta en detalle el comportamiento del CTSQL y las operaciones a realizar en
cada uno de estos procesos.
Permisos
Uno de los aspectos a tener en cuenta en el diseño de una base de datos es quiénes pueden acceder y a qué
datos, así como el tipo de acceso que se autoriza.
En función de los datos y del tipo de usuario se podrán establecer distintos niveles de privacidad en el acceso.
En este sentido se pueden considerar dos niveles en el control de accesos:
c) CONNECT: Este nivel es el más bajo y sólo permite acceso a instrucciones de consulta o actualización de ta-
blas.
Las instrucciones del CTSQL que permiten establecer estas jerarquías de acceso son GRANT y REVOKE.
La palabra reservada «PUBLIC» indica cualquier usuario. En caso de intentar seleccionar una base de datos y no
tener permiso de acceso el CTSQL presentará el siguiente mensaje de error:
En instalaciones de MultiBase en redes de área local bajo Windows, así como en arquitecturas cliente-servidor,
el nombre del usuario corresponde al valor que contenga la variable de entorno DBUSER en cada uno de las
máquinas «cliente». Por ejemplo:
database almacen;
grant connect to public
En este ejemplo, la instrucción GRANT concede el permiso de conexión a la base de datos «almacen» a cual-
quier usuario existente en la máquina. A partir de estos momentos, cualquier usuario podrá manejar la infor-
mación grabada en cualquiera de las tablas pertenecientes a dicha base de datos, siempre y cuando no se
hayan retirado los permisos por defecto. Esto se debe a que al crear una tabla, ésta toma por defecto que «PU-
BLIC» (cualquier usuario) puede mantener sus datos: altas (INSERT), bajas (DELETE), modificaciones (UPDATE) y
consultas (QUERY).
Ejemplo:
database almacen;
revoke connect from ctldemo;
En este caso, «ctldemo» es el usuario que previamente tenía concedido el permiso de acceso a la base de datos
«almacen», el cual queda «revocado» mediante el empleo de la instrucción REVOKE.
Pág. 71
MultiBase. Manual del SQL
La cláusula «WITH GRANT OPTION» indica que se van a conceder permisos a los usuarios indicados en la «lis-
ta_usuarios» para conceder o retirar permisos a los restantes usuarios del sistema.
Existen dos grupos de permisos, por una parte, los que alteran el esquema de la tabla («alter» e «index»), y por
otra, los que afectan a los datos como filas completas («delete», «insert», «select» y «update»). Si se especifica
una lista de columnas entre paréntesis después de «select» o «update», el permiso no se aplicará a nivel de
tabla completa, sino sólo a nivel de las columnas especificadas. El hecho de que esto no se aplique a «insert» ni
a «delete» es que estas operaciones involucran a una fila completa.
Por ejemplo:
database almacen;
grant alter on unidades to ctldemo
En este ejemplo se concede permiso para alterar la estructura de la tabla «unidades» de la base de datos «al-
macen» al usuario «ctldemo».
revoke alter to unidades from ctldemo
En este caso se retira el permiso (REVOKE) asignado en el ejemplo anterior con la instrucción GRANT.
Bloqueos
En aplicaciones de bases de datos y entornos multiusuario surge como problema efectivo el de la concurrencia.
Ésta se puede definir como la simultaneidad de accesos de distintos usuarios a los mismos datos para opera-
ciones completas de consulta y/o actualización. Aunque CTSQL procese una petición sobre una base de datos
cada vez, virtualmente, para cada peticionario, su operación es «única» (primero consulta, después actualiza).
Esto podría llevar a inconsistencias tales como lectura de valores irreales, actualización de valores aleatoria e,
incluso, pérdida de información.
Para solventar estas posibles inconsistencias, el CTSQL dispone de un mecanismo, denominado «bloqueo», que
consiste básicamente en la limitación del acceso a determinados datos por parte del primer programa que los
solicita, de forma que los demás no puedan leerlos ni modificarlos.
En función de los diferentes objetos manejados por el CTSQL, existen distintos niveles de bloqueos, según
afecten a la base de datos, a las tablas o a las filas.
En los epígrafes que siguen se explica el mecanismo y la consecuencia de cada uno de estos bloqueos en los
diferentes objetos del CTSQL.
En este caso, la cláusula «EXCLUSIVE» debe especificarse para producir el bloqueo de la base de datos indicada
en «base_datos». Esta operación sólo podrá ejecutarla el administrador de la base de datos, es decir, aquellos
usuarios que tengan permiso «DBA» sobre la misma.
En el caso de ejecutar esta instrucción sobre una base de datos que ya estuviese seleccionada por otro usuario
se produciría un error indicando la imposibilidad del bloqueo. Este error también se produciría si un usuario
con permiso sobre la base de datos intentase seleccionarla y ésta ya hubiese sido seleccionada previamente
por un usuario «DBA» en modo exclusivo («EXCLUSIVE»).
NOTA: La instrucción «DATABASE base_datos EXCLUSIVE» puede simularse también con el bloqueo de la tabla
«systables» perteneciente al catálogo de la base de datos:
lock table systables in exclusive mode
Hay que tener en cuenta que esta instrucción se encuentra implícita en la instrucción DATABASE del sublengua-
je de manipulación de datos (DML) del CTSQL. Al ejecutar DATABASE se cierra la base de datos en curso, selec-
cionándose la que se hubiese indicado en su sintaxis (para más información sobre estas instrucciones consulte
el capítulo correspondiente al «Lenguaje de manipulación de datos» en este mismo volumen).
NOTA: En caso de haber bloqueado la base de datos mediante la ejecución de la instrucción LOCK TABLE de la
tabla «systables», su desbloqueo deberá llevarse a cabo mediante la instrucción UNLOCK TABLE. Por ejemplo:
unlock table systables
Bloqueo de tablas
La forma de bloquear una tabla de una base de datos es mediante la ejecución de la instrucción LOCK TABLE,
cuya sintaxis es la siguiente:
LOCK TABLE tabla IN {SHARE | EXCLUSIVE} MODE
Como puede verse en esta sintaxis, existen dos niveles de bloqueo sobre las tablas:
IN SHARE MODE Esta cláusula indica que se permite la lectura de la tabla sobre la que se aplica el
bloqueo a otros usuarios, pero no su actualización.
IN EXCLUSIVE MODE Esta cláusula indica que no se permite a otros usuarios ni la lectura ni la actuali-
zación de la tabla sobre la que se aplica el bloqueo. Este tipo de bloqueo es el
más adecuado cuando la actualización de la tabla implique un número elevado
de filas o cualquier proceso masivo sobre la misma.
Por ejemplo:
lock table articulos in exclusive mode;
update articulos set pr_vent = pr_vent * 1,05;
unlock table articulos;
Pág. 73
MultiBase. Manual del SQL
Este ejemplo bloquea la tabla «articulos» para una actualización masiva de todas sus filas, incrementando el
precio de venta («pr_vent») un cinco por ciento. Después de realizar la operación de actualización la tabla que-
da desbloquedada mediante la ejecución de la instrucción UNLOCK TABLE.
Desbloqueo de tablas
La forma de desbloquear una tabla previamente bloqueada por la instrucción LOCK TABLE es ejecutando UN-
LOCK TABLE, con independencia del modo de bloqueo aplicado. Su sintaxis es la siguiente:
UNLOCK TABLE tabla
Bloqueo de filas
El CTSQL no dispone de una instrucción específica para bloquear una fila de la tabla. Este tipo de bloqueo es
automático en el momento en que se intenta modificar dicha fila mediante la instrucción UPDATE del sublen-
guaje de manipulación de datos (DML) del CTSQL. Si esta instrucción detecta que la fila a actualizar se encuen-
tra bloqueada por otro usuario, se queda en espera hasta que se desbloquee.
Hay ocasiones en las que interesa bloquear un número indeterminado de filas de una tabla. Esta operación sólo
podrá realizarse si la base de datos sobre la que actúa es transaccional. Este tipo de bloqueo se tiene que reali-
zar comenzando una transacción (BEGIN WORK) y actualizando todas las filas que se desean bloquear, aunque
ésta sea una actualización absurda. Dichas filas quedarán bloqueadas hasta que finalice la transacción (COM-
MIT WORK o ROLLBACK WORK).
NOTAS:
1.— Por el procedimiento de bloqueo que maneja el CTSQL hay que tener cuidado con los bloqueos de las ta-
blas. Un usuario podría quedarse infinitamente bloqueado al intentar actualizar una fila de una tabla bloqueada
mediante la instrucción LOCK TABLE, ya que ésta no se desbloquea nunca (el único que podría desbloquearla
sería el usuario que la hubiese bloqueado).
2.— El lenguaje de cuarta generación CTL dispone de mecanismos para el bloqueo de filas en procesos de ac-
tualización. Los objetos CTL que controlan este tipo de bloqueos son CURSOR y FORM (para más información
consulte el capítulo sobre «Comunicación con el CTL» en este mismo volumen).
Pág. 75
MultiBase. Manual del SQL
Introducción
La única forma de leer las filas contenidas en las tablas de una base de datos es mediante la instrucción SELECT
del sublenguaje de consultas (Query Language) del CTSQL.
La particularidad de cualquier SQL existente en el mercado a la hora de manipular los datos grabados de las
tablas es que éstos son siempre tratados en bloque. Es decir, la instrucción SELECT se encarga de leer de una o
varias tablas y el resultado de esta lectura es una tabla derivada compuesta por un número determinado de
columnas.
Una instrucción SELECT es una operación entre tablas cuyo resultado puede ser interpretado como otra tabla
(«tabla derivada»). Esta tabla sería el producto cartesiano de las tablas contenidas en la cláusula «FROM». En el
caso de existir una cláusula «WHERE» se eliminarían de la tabla resultado todas aquellas filas que no cumplie-
sen las condiciones especificadas en dicha cláusula. De todas las filas resultantes se eliminarán, además, todas
aquellas columnas que no hayan sido indicadas en la cláusula «lista_select».
IMPORTANTE
— No editar NUNCA los ficheros «.dat» e «.idx» correspondientes a una tabla. En caso de hacerlo
se podrían perder todas sus filas.
— El orden de definición de las columnas dentro de una tabla es indiferente para la selección, in-
serción, borrado y modificación de información sobre ellas.
B):
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT …
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Según esta sintaxis, la instrucción SELECT mínima estará compuesta por las cláusulas «lista_select» y «FROM».
Todas las cláusulas de esta instrucción SELECT tienen que especificarse según el orden indicado en esta sintaxis.
En los siguientes epígrafes se comenta por separado cada una de estas cláusulas.
Cláusula SELECT
Esta cláusula indica las columnas que contendrá la tabla derivada obtenida como resultado de la ejecución de
la instrucción SELECT. Esta cláusula es una de las obligatorias dentro de la estructura de una instrucción SELECT,
y su sintaxis es la siguiente:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select FROM lista_tablas
Donde:
ALL Palabra reservada que indica que se seleccionarán «todas» las filas que satisfa-
gan la cláusula «WHERE» (si existe), sin eliminar las filas duplicadas que pudie-
ran surgir como resultado en la tabla derivada. Esta cláusula es la que se utili-
zará por defecto en cualquier selección.
Ejemplo 1. Seleccionar las provincias en las que se encuentran clientes:
select all clientes.provincia, provincias.descripcion
from clientes, provincias
where clientes.provincia = provincias.provincia
Como puede comprobarse, la tabla derivada indica varias veces la misma pro-
vincia («MADRID» y «BARCELONA»). Esto significa que existen tantos clientes
en dichas provincias como veces salgan repetidas éstas en esta tabla derivada.
Ejemplo 2. Seleccionar los códigos y nombres de empresa que hayan realizado
alguna compra:
select all albaranes.cliente, clientes.empresa
from albaranes, clientes
where albaranes.cliente = clientes.cliente
order by 1
Pág. 77
MultiBase. Manual del SQL
30 PEREIRA
30 PEREIRA
…
Pág. 79
MultiBase. Manual del SQL
La provincia 33 es ASTURIAS 0
La provincia 5 es AVILA 0
La provincia 6 es BADAJOZ 0
La provincia 7 es BALEARES 0
La provincia 8 es BARCELONA 0
La provincia 9 es BURGOS 0
La provincia 10 es CACERES 0
La provincia 11 es CADIZ 0
…
Esta tabla derivada está compuesta por una sola columna, re-
sultante de concatenar el nombre y apellidos de los clientes
de la tabla «clientes».
Ejemplo 4:
select count(*), max(pr_vent), min(pr_vent)
from articulos
Cláusula FROM
Esta cláusula —obligatoria dentro de una instrucción SELECT— determina la tabla o lista de tablas de las que se
desea leer datos. Si se especifica más de una tabla en esta cláusula, la tabla derivada resultante será el produc-
to cartesiano de éstas si no se enlazan en la cláusula «WHERE». Su sintaxis es la siguiente:
FROM lista_tablas
Donde:
lista_tablas Lista de tablas separadas por comas. Cada una de estas tablas puede especifi-
carse con la siguiente sintaxis:
[OUTER] tabla [alias]
Donde:
OUTER Palabra reservada que se emplea para componer «outer
joins» (ver epígrafe «Enlace de Tablas» en el capítulo 3 de este
mismo volumen).
Ejemplo 1:
select clientes.empresa, albaranes.albaran,
albaranes.fecha_albaran
from clientes, outer albaranes
where clientes.cliente = albaranes.cliente
Pág. 81
MultiBase. Manual del SQL
El resultado sería:
Provincia Empresa Num. Albaran Fecha Albaran
ALAVA
ALBACETE
ALICANTE
ALMERIA
ASTURIAS
AVILA
BADAJOZ
BALEARES
BARCELONA MOTOROLA
BARCELONA RTC S.A.
BARCELONA GUYON
BARCELONA MATE
BARCELONA ALCOLEA
BARCELONA BARNICES DEVA 3 23/04/1988
BARCELONA INTEL 25 07/01/1988
BARCELONA SPOT IBERICA
BARCELONA ATR 40 15/05/1988
…
El resultado sería:
Prov_Cliente Empresa Descripcion Empresa Prov_Proveedor
MADRID P.P.B. Alter Ego SLIPPER BARCELONA
MADRID P.P.B. Silent Service LLORENS CASTELLON
MADRID P.P.B. PC Forth GISA MADRID
MADRID P.P.B. Hallow VENTAUTO BARCELONA
MADRID P.P.B. Nice Print CARLIPS BARCELONA
MADRID P.P.B. Perfect Link R. BRANAS MADRID
MADRID P.P.B. Smart Key TRITON MADRID
MADRID P.P.B. Better Basic AMPER BARCELONA
MADRID CIFERSA PC Arcade FEMBASA BARCELONA
MADRID CIFERSA Baseball SLIPPER BARCELONA
…
Pág. 83
MultiBase. Manual del SQL
Esta tabla derivada está compuesta en cada fila por dos datos
de la misma tabla «provincias». Las columnas «Prov_Cliente»
y «Prov_Proveedor» corresponden a las provincias del cliente
que realiza la compra y del proveedor que suministra el pro-
ducto. Ambas columnas se extraen de la tabla de «provincias»,
la cual se lee dos veces gracias a la definición de los alias de
tabla «p1» y «p2».
Año Clientes
1998 34
2015 1
2016 2
NOTAS:
1.— No están implementadas las tablas derivadas como parte de los campos de la instrucción SELECT.
2.— Las tablas derivadas se deben emplear cuando queramos anidar funciones agregadas, hallar el promedio
de las cantidades compradas o filtrar las filas de la tabla principal antes de un JOIN.
Implementada en la versión 3.6 0.41 del gestor de base de datos.
Cláusula WHERE
Mediante esta cláusula se podrán especificar diferentes criterios de selección. Aquellas filas que cumplan las
condiciones indicadas serán las que formarán la tabla derivada. Su sintaxis es la siguiente:
WHERE condición
Donde:
condición Lista de una o más subcondiciones separadas por los operadores lógicos
«AND», «OR» o «NOT». Existen dos tipos de condiciones de búsqueda:
• Condición de Comparación.
• Condición de Subselección.
A continuación se explica en detalle cada uno de ellos.
Condición de comparación
Una condición de comparación puede definirse mediante cualquiera de las siguientes sintaxis:
El ejemplo anterior es un caso muy común en el enlace de tablas. Las tres tablas se encuentran enlazadas en la
cláusula «WHERE» mediante dos condiciones de enlace.
NOTA: Si la relación entre las tablas declaradas en la cláusula «FROM» es mediante una columna, el número de
condiciones de enlace a especificar en la cláusula «WHERE» es el número de tablas menos una. Es decir, si en la
cláusula «FROM» hay tres tablas por lo menos tiene que existir dos condiciones de enlace entre ellas. En caso
de faltar alguna de las condiciones se producirá el producto cartesiano con la tabla que falta el enlace.
Pág. 85
MultiBase. Manual del SQL
Donde:
expresión Cualquier expresión válida. Este concepto ya se ha comentado en el capítulo 2
de este mismo volumen.
NOT Palabra reservada que invierte el resultado.
BETWEEN Palabra reservada que indica el rango.
expresión_desde Cualquier expresión válida cuyo resultado es el valor inicial del rango. Este valor
inicial es incluido en el rango.
expresión_hasta Cualquier expresión válida cuyo resultado es el valor final del rango. Este valor
final es incluido en el rango. Esta expresión deberá ser mayor que el de «expre-
sión_desde».
Ejemplo:
select provincia, descripcion from provincias
where provincia between 10 and 15
Las provincias que aparecen son aquellas cuyo código está comprendido en el rango entre el 10 y el 15, ambos
inclusive. Si hubiésemos indicado la partícula «NOT», la tabla derivada que se habría generado sería la formada
por el resto de provincias.
Resultado:
Cod. F.Pago Forma de Pago
C Contado
L3 30/60/90
La tabla derivada que se genera en este ejemplo está compuesta por aquellas filas cuyo código de forma de
pago es «C», «L3» o «Z». En este caso, al no existir «Z», éste no aparece en la tabla derivada. Esta misma tabla
derivada podría construirse también con la siguiente instrucción SELECT:
select formpago, descripcion
from formpagos
where formpago = "C"
or formpago = "L3"
or formpago = "Z"
La tabla derivada resultante de esta instrucción SELECT sería idéntica a la expuesta en el primer caso.
Resultado:
Cod. Provincia Provincia
50 ZARAGOZA
La máscara «Z__A%» indica la selección de todas las provincias que comiencen por «Z» mayúscula y cuyo cuar-
to carácter sea una «A» mayúscula. El resto de caracteres son incondicionales.
Ejemplo 2:
select provincia, descripcion
from provincias
where descripcion like "%D"
Pág. 87
MultiBase. Manual del SQL
Resultado:
Cod. Provincia Provincia
28 MADRID
47 VALLADOLID
La máscara «%D» seleccionará aquellas provincias cuyo nombre termine por una «D» mayúscula.
Resultado:
Cod. Provincia Provincia
50 ZARAGOZA
Este ejemplo es equivalente al expuesto en primer lugar en el caso de la condición con «LIKE».
Ejemplo 2:
select provincia, descripcion
from provincias
where descripcion matches "[BMV]A*"
Resultado:
Cod. Provincia Provincia
6 BADAJOZ
7 BALEARES
8 BARCELONA
28 MADRID
29 MALAGA
46 VALENCIA
47 VALLADOLID
La máscara «[BMV]A*» extraerá las provincias cuyo nombre comience por «B», «M» o «V» mayúsculas y cuyo
segundo carácter sea una «A» mayúscula.
Ejemplo 3:
select provincia, descripcion
from provincias
where descripcion matches "[^A-T]???A*"
Resultado:
Cod. Provincia Provincia
47 VALLADOLID
48 VIZCAYA
En este ejemplo se seleccionarán todas las provincias cuyo nombre no comience por ninguna de las letras com-
prendidas en el rango desde «A» hasta «T», y cuyo quinto carácter sea una «A» mayúscula.
Este ejemplo mostrará aquellos clientes a los que no se les haya facturado.
Condición de subselección
Una condición de «subselect» consiste en comparar una expresión, que por lo general será una columna, con
una tabla derivada devuelta por otra instrucción SELECT. En CTSQL existen tres formas de realizar condiciones
de «subselect»:
Pág. 89
MultiBase. Manual del SQL
ANY Palabra reservada que indica que el resultado de la comparación debe ser ver-
dadero al menos para uno de los valores devueltos por la instrucción «subse-
lect».
SOME Sinónimo de ANY.
Ejemplo:
select cliente, albaran, fecha_albaran
from albaranes
where cliente = any (select cliente from clientes
where provincia = any (select provincia from provincias
where descripcion = "MADRID"))
Resultado:
Cod. Cliente Num. Albaran Fecha Albaran
14 1 11/11/1988
84 6 16/10/1988
67 7 22/07/1988
32 9 28/07/1988
37 10 14/11/1988
89 14 21/09/1988
4 16 19/10/1988
54 17 27/07/1988
…
Este ejemplo muestra los códigos de cliente que pertenecen a la provincia de «MADRID» y que hayan realizado
alguna compra.
Las partículas «ANY» y «ALL» pueden omitirse si se sabe de antemano que «subselect» devolverá una sola fila.
En este caso, «condición» evalúa a verdadero o falso dependiendo de que se cumpla o no la condición de com-
paración entre «expresión» y el resultado devuelto por la instrucción «subselect»
Ejemplo: Comparar una columna de la tabla de la que se extrae información («clientes») con el resultado de la
lectura de otra («provincias»). Este caso es válido siempre y cuando la instrucción SELECT que se encuentra en
la cláusula «WHERE» sólo devuelva una fila.
select cliente, empresa, provincia
from clientes
where clientes.provincia = (select provincia
from provincias
where descripcion = "GUADALAJARA")
Resultado:
Codigo Cliente Empresa Provincia
12 ATC 19
39 RTP 19
75 MACARRON S.A. 19
80 ROBLES 19
99 ALFOMBRAS MAS 19
Resultado:
Codigo Cliente Empresa
4 P.P.B.
11 CIFERSA
12 ATC
13 BARNICES DEVA
14 ZENITH
15 INTEL
23 DETECSA
26 POLIGAS
27 DSE
28 GYM
30 PEREIRA
32 DSE
35 ATC
…
Este ejemplo presenta la lista de los clientes que han realizado alguna compra.
Pág. 91
MultiBase. Manual del SQL
NOTAS:
1.— La partícula «IN» puede simularse mediante la subselect «= ANY».
2.— La partícula «NOT IN» puede reemplazarse por la «subselect» «!= ALL».
Este ejemplo no devuelve ninguna fila si las tablas de «clientes» y «provincias» tienen la integridad referencial
correcta. En caso de devolver alguna fila significará que existe algún cliente dado de alta en una provincia que
no existe en la tabla de «provincias». Esto sería una falta muy grave de integridad de datos.
Cláusula GROUP BY
Esta cláusula devuelve una fila a partir de un conjunto de filas que tienen un denominador común para las co-
lumnas indicadas en ella.
GROUP BY lista_grupos
Donde:
GROUP BY Palabras reservadas.
lista_grupos Nombre de una columna, o lista de nombres de columnas separados por comas,
que determinan el grupo. En lugar de los nombres de columnas se pueden in-
troducir uno o más números enteros. Estos números enteros se refieren a la
posición de las columnas en la «lista_select». Esto último es obligatorio si los
elementos de la «lista_select» por los que se desea agrupar no son nombres de
columnas sino expresiones.
El máximo de columnas que se pueden incluir en una cláusula «GROUP BY» es 8. Asimismo, la suma de las lon-
gitudes de las columnas indicadas en esta cláusula no puede exceder de 120 bytes. Estas dos condiciones vie-
nen impuestas por el XOPEN-ISAM.
NOTAS:
1.— En la «lista_select» de la instrucción SELECT no se pueden indicar columnas que no estén incluidas en la
cláusula «GROUP BY».
2.— Cuando en la «lista_select» se encuentra al menos una función de valores agregados junto a más colum-
nas, esto constituye una instrucción SELECT que obligatoriamente requiere establecer grupos por dichas co-
lumnas.
Ejemplo 1:
select provincia, count(*)
from clientes
group by 1;
Resultado:
Provincia (count(*))
8 46
12 1
18 4
19 5
20 1
28 37
29 1
41 3
42 2
Esta tabla derivada devuelve el número de clientes que existe en los códigos de provincias indicados en la pri-
mera columna.
En caso de querer mostrar el nombre de la provincia al que corresponde cada uno de estos grupos habría que
ejecutar la siguiente instrucción SELECT:
select clientes.provincia, count(*), provincias.descripcion
from clientes, provincias
where clientes.provincia = provincias.provincia
group by 1, 3;
Cuando se incrementan las columnas en la «selección» de la instrucción SELECT, todas ellas tienen que ser gru-
pos y especificarse en la cláusula «GROUP BY».
Ejemplo 2:
select albaran, sum( precio * cantidad - (precio * (descuento /100)))
from lineas
group by albaran;
Resultado:
Num. Albaran (sum)
1 954546,97
2 516025,40
3 206855,58
4 710863,32
5 66299,04
6 221273,95
Pág. 93
MultiBase. Manual del SQL
7 1188598,27
8 373555,11
9 55910,88
10 219976,78
…
Este ejemplo muestra el total facturado de cada uno de los albaranes emitidos.
NOTAS:
1.— Cada fila que contenga un valor nulo («NULL») en la columna por la que se agrupa será considerada un
grupo distinto, ya que su valor es desconocido.
2.— En una instrucción SELECT con cláusula «GROUP BY» no tiene por qué incluirse en la «lista_select» una
función de valor agregado. Por ejemplo:
select provincia
from clientes
group by provincia;
Resultado:
Provincia
8
12
18
19
20
28
29
41
42
Esta instrucción muestra los códigos de provincia en los que existen clientes. Esta tabla derivada también pue-
de conseguirse con la siguiente instrucción SELECT:
select unique provincia
from clientes;
Cláusula HAVING
Esta cláusula establece condiciones de selección sobre los grupos determinados por la cláusula «GROUP BY».
Una de las expresiones de la condición obligatoriamente tiene que ser una función de valor agregado. Por lo
general, esta cláusula siempre acompañará a la «GROUP BY». No obstante, en caso de que no se incluya esta
última en la sintaxis se considerará que todas las filas componen un único grupo. Su sintaxis es la siguiente:
HAVING condición_grupos
Donde:
HAVING Palabra reservada que especifica condiciones de grupos.
condición_grupos Cualquiera de las condiciones explicadas en la cláusula «WHERE» de una ins-
trucción SELECT (ver epígrafe 7.1.3 de este mismo capítulo). La condición puede
descomponerse en una lista de comparaciones separadas por «AND» u «OR».
Estas comparaciones relacionan un valor agregado sobre el grupo con una cons-
tante o con otro valor agregado sobre el mismo grupo.
Ejemplo 1:
select clientes.provincia, count(*), provincias.descripcion
from clientes, provincias
where clientes.provincia = provincias.provincia
group by 1, 3
having count(*) > 10 ;
Resultado:
Provincia (count(*)) Provincia
8 46 BARCELONA
28 37 MADRID
Esta tabla derivada muestra las provincias en las que existen más de diez clientes. Como puede comprobarse
en la tabla derivada, «MADRID» y «BARCELONA» tienen 37 y 46 clientes respectivamente.
Ejemplo 2:
select lineas.articulo, articulos.descripcion, count(*)
from lineas, articulos
where lineas.articulo = articulos.articulo and lineas.proveedor = articulos.proveedor
group by 1, 2
having count(*) > 5;
Resultado:
Cod. Articulo Descripcion (count(*))
6 Alter Ego 7
12 Basic Compiler 7
42 Cross 80386 7
84 M/S Quick Basic 6
86 Map Edit 6
114 PC Tutor 6
148 Side View 8
167 T3 7
Esta tabla derivada muestra los códigos y nombres de aquellos artículos que han sido comprados más de cinco
veces.
Cláusula ORDER BY
Esta cláusula ordena el resultado de la selección por el valor de una o más columnas de forma ascendente o
descendente. Esta tabla derivada sólo puede ordenarse por las columnas que intervienen en la «selección» de
la instrucción SELECT. Su sintaxis es la siguiente:
ORDER BY columna [ASC | DESC][,
columna [ASC | DESC][, …]
Donde:
ORDER BY Palabra reservada que indica ordenación de la tabla derivada.
columna Nombre de una columna (o alias) por el que se desea ordenar el resultado de la
selección. En lugar de nombres de columnas también se pueden introducir uno
o más números enteros que se refieren a la posición de las columnas dentro de
la cláusula «selección» de la instrucción SELECT. Esto último es obligatorio si los
elementos de la «selección» por los que se quiere ordenar son expresiones, por
ejemplo: Valores agregados, operaciones aritméticas, «substrings», etc.
Pág. 95
MultiBase. Manual del SQL
ASC Palabra reservada que especifica que el resultado debe ordenarse de forma as-
cendente. Esta opción es la que CTSQL utiliza por defecto, por lo que no será
necesario especificarla en la sintaxis.
DESC Palabra reservada que especifica que el resultado debe ordenarse de forma
descendente.
Las columnas de la cláusula «ORDER BY» tienen que aparecer de forma implícita, es decir, por medio del símbo-
lo «*» (asterisco, que indica que se listan todas las columnas de la tabla) o explícita en la cláusula «lista_select»
de la instrucción SELECT.
El máximo de columnas que se pueden incluir en una cláusula «ORDER BY» es 8. Asimismo, la suma de las lon-
gitudes de las columnas indicadas en esta cláusula no puede exceder de 120 bytes. Estas dos condiciones vie-
nen impuestas por el XOPEN-ISAM.
Si la columna por la que se ordena contiene valores nulos («NULL»), éstos se considerarán menores que cual-
quier otro valor de la misma columna; así, con ordenación ascendente («ASC») aparecerán al principio, con
descendente («DESC») al final.
Ejemplo:
select albaranes.albaran, lineas.linea, articulos.descripcion
from albaranes, lineas, articulos
where albaranes.albaran = lineas.albaran
and (lineas.articulo = articulos.articulo
and lineas.proveedor = articulos.proveedor)
order by 1, 2 desc ;
Resultado:
Num. Albaran Num. Linea Descripcion
1 19 Smart Key
1 18 PC Plot
1 17 Cross NSC-800
1 16 Graph Talk
1 15 M/S Quick Basic
1 14 C Helper
1 13 Clipper
1 12 Super Draw
1 11 Spell Binder
1 10 CopyII-PC
1 9 Basic Primer
1 8 PC Palette
1 7 Easy DOS
1 6 PC MPX
1 5 8087 Test
1 4 Basic Compiler
1 3 Faster C
1 2 C Tools Plus
1 1 Personnal Basic
2 7 Side View
2 6 Smart Works
2 5 Turbo Prolog
2 4 PC Write
2 3 Cobol
2 2 PFS Graph
2 1 P-Mate
3 3 VP Planner
…
Esta tabla derivada presenta todos los albaranes ordenados de forma ascendente junto con todas sus líneas,
pero éstas ordenadas en forma descendente.
Cláusula LIMIT
Esta cláusula indicará al motor que retorne un rango de N registros, desde la posición X a la posición Y, o los Y
primeros. La sintaxis de la sentencia LIMIT dentro de la SELECT es la siguiente:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
LIMIT {[offset,] row_count | row_count OFFSET offset}]
[INTO TEMP tabla_temporal]
La cláusula LIMIT debe ir después de la cláusula ORDER BY y antes de la cláusula INTO TEMP.
Por compatibilidad con la sintaxis de diversos motores, esta sentencia se puede escribir de dos maneras:
• LIMIT.
• LIMIT y OFFSET.
Las dos indican lo mismo, pero con los parámetros cambiados.
Parámetros:
row_count Indica el número de registros que se van mostrar.
offset Indica el número de registro que se van a ignorar.
Ejemplos:
Si se realiza la siguiente intrucción "SELECT":
select provincia, descripcion from provincias order by provincia limit 10
Pág. 97
MultiBase. Manual del SQL
Donde:
INTO TEMP Palabras reservadas que implican la creación de una tabla temporal.
tabla_temporal Nombre de identificador que se desea asignar a la tabla temporal. Los nombres
de las columnas de la tabla temporal son los que aparecen en la «lista_select»
de la instrucción SELECT que la crea. No obstante, en caso de utilizar una expre-
sión (operaciones aritméticas, funciones de valores agregados, «substrings»,
etc.) hay que asignarles obligatoriamente un nombre de «alias». En este caso,
los nombres de los «alias» serán los de las columnas de la tabla temporal.
Los ficheros físicos «.dat» e «.idx» que representan a la tabla temporal se crearán en el directorio indicado por
la variable de entorno DBTEMP.
Ejemplo 1:
select articulo, descripcion, pr_vent - pr_cost beneficio
from articulos
into temp beneficios;
select * from beneficios;
Resultado:
articulo descripcion beneficio
1 3D Graphics 40269,48
2 8087 Test 26136,79
3 ANSI Draw 33347,82
4 ADA 22473,38
5 Alley Cat 44837,44
6 Alter Ego 43613,46
7 Artic Fox 33137,74
8 Assembler Tutor 22256,89
9 Astro Talk 17138,00
10 Auto Word 20846,41
11 Auto Code 27918,09
12 Basic Compiler 19243,01
13 Basic Primer 29650,11
…
Esta tabla derivada es el resultado de leer todas las filas grabadas en la tabla temporal «beneficios».
Ejemplo 2:
select articulo, descripcion, pr_vent from articulos
into temp actualiza;
update actualiza set pr_vent = pr_vent * 1,05
Este ejemplo extraerá todos los artículos de la tabla «articulos» y los grabará en una tabla temporal denomina-
da «actualiza». Tras esta operación, se actualizarán todos los artículos, incrementando su precio de venta en un
5 por ciento sobre dicha tabla temporal, no así en la maestra.
Cláusula UNION
Esta cláusula devuelve una tabla derivada compuesta por el resultado de varias instrucciones SELECT concate-
nadas (unidas). La sintaxis de una instrucción SELECT con la cláusula «UNION» es la siguiente:
SELECT [UNION [ALL]] SELECT [UNION [ALL]] …
[ORDER BY columna [ASC | DESC][, columna [ASC | DESC][, …]]
[INTO TEMP tabla_temporal]
Donde:
UNION Palabra reservada que indica la unión de los resultados de cada una de las ins-
trucciones SELECT que se incluyan.
ALL La tabla derivada final («UNION») no contendrá filas duplicadas a menos que se
especifique esta palabra reservada.
SELECT Instrucción SELECT con las siguientes cláusulas:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
La sintaxis completa de una instrucción SELECT con varias cláusulas «UNION» tiene el siguiente aspecto:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT …
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Pág. 99
MultiBase. Manual del SQL
Por ejemplo: Construir una tabla derivada compuesta por los nombres de empresa, de clientes y proveedores,
así como sus respectivas direcciones y códigos postales. Esta tabla derivada podría utilizarse para la realización
de un «mailing» sin tener en cuenta si son clientes o proveedores.
select clientes.empresa, clientes.nombre, clientes.apellidos,
clientes.direccion1, clientes.distrito, clientes.poblacion,
provincias.descripcion, "C"
from clientes, provincias
where clientes.provincia = provincias.provincia;
Pág. 101
MultiBase. Manual del SQL
Introducción
El lenguaje de manipulación de datos (DML) contempla las siguientes operaciones:
IMPORTANTE
El orden de definición de las columnas dentro de una tabla es indiferente para la selección, inser-
ción, borrado y modificación de información sobre ellas.
En los epígrafes siguientes se explica el comportamiento del CTSQL en las diferentes operaciones antes men-
cionadas.
Donde:
base_datos Identificador CTSQL que se corresponde con el nombre de la base de datos.
EXCLUSIVE Palabra reservada que provoca el bloqueo de la base de datos para el resto de
usuarios (esta cláusula se ha explicado en el epígrafe 6.3, relativo a los bloque-
os, en este mismo volumen).
b) Para cerrarla:
CLOSE DATABASE
En este ejemplo se selecciona la base de datos «almacen» y a continuación se crea una tabla denominada
«provincias». Esta tabla se creará para la base de datos seleccionada previamente. Por último, se cierra la base
de datos.
Inserción de filas
CTSQL permite la inserción de valores sobre todas las columnas de una tabla o bien de parte de ellas. Esta faci-
lidad de manejo de datos es estándar de cualquier SQL del mercado. Cuando una tabla está vacía, todas las filas
que se graben a continuación se insertarán físicamente de forma secuencial en el fichero de datos («.dat»),
mientras que el fichero de índices («.idx») se irá actualizando automáticamente por cada una de ellas.
Sin embargo, en el caso de que dicha tabla tuviese alguna fila borrada, las inserciones rellenarían los huecos
marcados por dichas filas borradas. Como veremos en el siguiente epígrafe («Borrado de Filas»), las filas no se
borran físicamente del fichero de datos («.dat»), sino que se marcan, de ahí que la longitud de cada fila en el
fichero de datos ocupe el número de bytes de todas las columnas más uno. Este uno corresponde con el byte
reservado para la marca de borrado.
Como característica fundamental del CTSQL, el manejo de datos puede realizarse de fila en fila o bien en un
bloque indeterminado de filas (tabla derivada). La instrucción que permite la inserción de filas en las tablas es
INSERT. Esta instrucción tiene dos posibles sintaxis:
a) Inserción de una sola fila:
INSERT INTO tabla [(columnas)] VALUES (valores)
Donde:
tabla Nombre de una tabla de la base de datos en curso.
columnas Nombre de la columna o columnas pertenecientes a la tabla indicada anterior-
mente. La especificación de estos nombres de columnas es opcional. No obstan-
te, es recomendable indicarlos siempre. Hay que tener en cuenta que una tabla
puede cambiar de estructura (añadir columnas nuevas, borrar columnas exis-
tentes, etc.) en cualquier momento.
En caso de no especificar los nombres de las columnas, CTSQL irá rellenando las
columnas según se encuentran creadas físicamente en la tabla.
valores Valores a insertar en cada una de las columnas especificadas anteriormente. En
caso de no especificar los nombres de las columnas, cada valor se asignará a ca-
da una de las columnas según el orden de creación de las mismas para la tabla.
Los tipos de datos de los valores a insertar en la tabla tienen que coincidir con
los de las columnas respectivas en la tabla. Los tipos de dato «CHAR», «TIME» y
«DATE» tienen que ir entre comillas.
instrucción_select Instrucción SELECT que generará la tabla derivada a insertar en bloque sobre la
tabla elegida. Esta SELECT puede estar compuesta por cualquiera de las combi-
naciones indicadas en el capítulo anterior («Lenguaje de Consultas»). Sin em-
bargo, las cláusulas «ORDER BY» e «INTO TEMP» no pueden incluirse nunca de-
ntro de esta instrucción SELECT cuando forme parte de una instrucción INSERT.
En caso de insertar alguna fila que provoque conflicto con algún índice único, CTSQL provocará un error y dicha
fila no se grabará. Asimismo, si deseamos insertar una fila en una tabla que no cumple las normas de integridad
Pág. 103
MultiBase. Manual del SQL
referencial con otra(s) tabla(s) de la base de datos, CTSQL no permitirá su inserción. Además, si alguna de las
columnas tiene asignado un atributo incongruente con los valores a insertar pueden ocurrir dos cosas:
a) Que CTSQL convierta el dato automáticamente en función de las características de dicho atributo. Por ejem-
plo, si el atributo es UPSHIFT y se intenta grabar un dato alfanumérico en minúsculas, CTSQL se encargará de
convertirlo automáticamente a mayúsculas.
b) Que se produzca un error. Este error puede ser producido por el atributo «NOT NULL» en caso de intentar
insertar una fila sin asignar un valor a una columna con dicho atributo y sin tener valor por defecto (atributo
DEFAULT). Asimismo, puede ser producido por el atributo NOENTRY al intentar insertar un valor sobre dicha
columna.
Ejemplo 1:
database almacen;
insert into provincias (provincia, descripcion, prefijo)
values (1, "ALAVA", 945);
En caso de intentar insertar esta fila en la tabla de «provincias» de la base de datos de demostración «alma-
cen» se producirá un error. Este error es debido a que dicha tabla posee un índice («primary key») por la co-
lumna «provincia», y si el código a insertar («1») ya existe, CTSQL producirá el siguiente error:
Imposible agregar una fila. Valor duplicado para una columna con un índice único OK
Ejemplo 2:
database almacen;
insert into provincias values (51, "CEUTA", 951);
Esta instrucción insertará una fila sobre la tabla «provincias» de la base de datos de demostración «almacen».
La estructura de la tabla a nivel de columnas es la siguiente:
• La primera columna es el código de la provincia «provincia». Por lo tanto, el primer valor de la fila a
insertar («51») será el que se asigne a dicha columna.
• La segunda columna es el nombre de la provincia («descripcion»). Por tanto, el segundo valor de la
fila («CEUTA») será el que se asigne a esta columna.
• La tercera y última columna es el prefijo de la provincia. Por lo tanto, el valor «951» es el que le co-
rresponde.
Ejemplo 3:
database almacen;
insert into clien_mail (cliente, empresa, apellidos, nombre, direccion1, poblacion, distrito, provincia)
select clientes.cliente, clientes.empresa, clientes.apellidos, clientes.nombre,
clientes.direccion1, clientes.poblacion, clientes.distrito, provincias.descripcion
from clientes, provincias
where clientes.provincia = provincias.provincia
and provincias.descripcion matches "?A*";
3.— Inserción en bloque de todos aquellos clientes de la tabla de «clientes» que pertenezcan a la provincia
cuyo nombre contenga como segunda letra una «A» mayúscula.
La instrucción de inserción también podría presentar el siguiente aspecto:
insert into clien_mail
select clientes.cliente, clientes.empresa, clientes.apellidos, clientes.nombre,
clientes.direccion1, clientes.poblacion, clientes.distrito, provincias.descripcion
from clientes, provincias
where clientes.provincia = provincias.provincia
and provincias.descripcion matches "?A*";
Borrado de filas
El borrado de filas en CTSQL consiste en marcar físicamente aquellas filas que cumplan las condiciones impues-
tas por la instrucción DELETE, cuya sintaxis es la siguiente:
DELETE FROM tabla
[WHERE {condicion_comparación | condición_subselect} ]
Donde:
tabla Nombre de la tabla sobre la que se va a realizar el borrado de aquellas filas que
cumplan la «condición» especificada a continuación.
WHERE Palabra reservada a la que siguen las condiciones que deben cumplir las filas a
borrar.
condición Cualquiera de las condiciones especificadas en la cláusula WHERE de la instruc-
ción SELECT (ver el capítulo anterior de este mismo volumen).
Como ya se ha indicado anteriormente, el borrado de las filas no es físico, sino lógico. Por lo tanto, cuando se
borra un número indeterminado de filas de una tabla, los ficheros físicos que representan a la tabla siguen
ocupando el mismo espacio en el disco. Para recuperar el espacio ocupado por las filas borradas se pueden
realizar dos operaciones:
1.— Ejecutar el comando de reparación de índices trepidx para dicha tabla. Este comando, además de la repa-
ración de índices, se encarga también de compactar la tabla, eliminando las filas borradas («marcadas»).
2.— Provocar una alteración («ALTER TABLE») sobre la estructura de dicha tabla, aunque ésta sea absurda. Esta
instrucción del sublenguaje «DDL» del CTSQL generará unos ficheros físicos nuevos para dicha tabla.
INSERT rellena los huecos dejados por DELETE.
Pág. 105
MultiBase. Manual del SQL
Si se intenta borrar una fila que está bloqueada por otro usuario, la instrucción DELETE quedará bloqueada a su
vez hasta que dicha fila se libere.
Asimismo, en caso de intentar borrar alguna fila que tenga referencia en cualquier otra tabla de la base de da-
tos, existiendo entre ambas integridad referencial mediante una clave referencial («foreign key») con cláusula
«RESTRICT» en borrado hacia la clave primaria («primary key»), el CTSQL devolverá un error indicando la impo-
sibilidad de borrarla.
Ejemplo 1:
delete from clientes;
Este ejemplo borraría todos los clientes cuyo nombre de empresa incluyese como segundo carácter una «A»
mayúscula.
Ejemplo 3:
delete from clientes
where provincia = any (select provincia from provincias
where descripcion matches "M*");
Este ejemplo borraría todos los clientes pertenecientes a cualquier provincia que comenzase por «M» mayús-
cula.
Ejemplo 4:
delete from clientes
where provincia in (select provincia from provincias
where descripcion matches "M*");
Ejemplo 5:
delete from clientes
where exists (select "cualquier expresión"
from provincias
where descripcion matches "M*"
and clientes.provincia = provincias.provincia);
En los ejemplos 4 y 5, la instrucción DELETE es idéntica a la expuesta en el ejemplo 3, si bien en estos dos últi-
mos casos se han aplicado otras comparaciones de «subselect».
NOTA: En todos estos ejemplos, CTSQL puede que devuelva un error por incurrir en un borrado que provocaría
una falta grave de integridad de datos. Este error es provocado por la integridad referencial establecida en la
base de datos de demostración «almacen». No obstante, las filas borradas previamente al error producido por
la integridad referencial no se recuperarán, excepto si el borrado es tratado en una transacción cuando la base
de datos sea transaccional. Por ejemplo:
database …
begin work
delete from …
where …
if errno != 0 then rollback work else commit work
Modificación de filas
CTSQL permite la modificación de todos los valores de todas las columnas de una tabla o bien de parte de ellas.
La modificación de los valores de las columnas que componen una tabla se realiza mediante la ejecución de la
instrucción UPDATE. En caso de no incluir condiciones, una instrucción UPDATE actualizará todas las filas que
contenga la tabla. La sintaxis de esta instrucción es la siguiente:
UPDATE tabla SET columna = expresión
[, columna = expresión] [,…]
[WHERE {condicion_comparación | condición_subselect} ]
Donde:
tabla Nombre de la tabla sobre la que se va a realizar la modificación de los valores
de las columnas indicadas en «columna» de aquellas filas que cumplan la «con-
dición» especificada a continuación.
columna Nombre de la columna que pertenece a «tabla». El valor actual de esta columna
será cambiado por el contenido de «expresión».
expresión Cualquiera de las expresiones válidas indicadas en el capítulo 2 de este mismo
volumen.
WHERE Palabra reservada a la que siguen las condiciones que deben cumplir las filas a
modificar.
condición Cualquiera de las condiciones especificadas en la cláusula WHERE de la instruc-
ción SELECT (ver el capítulo anterior de este mismo volumen).
En caso de intentar actualizar una fila bloqueada por otro usuario, la instrucción UPDATE quedará a su vez blo-
queada hasta que dicha fila se libere.
Asimismo, en caso de intentar actualizar alguna fila que tenga referencia en cualquier otra tabla de la base de
datos, existiendo entre ambas integridad referencial con cláusula «RESTRICT» en actualización hacia la clave
primaria («primary key»), el CTSQL devolverá un error indicando la imposibilidad de dicha actualización.
Además, en caso de intentar actualizar una columna que tenga asignado el atributo «NOUPDATE», CTSQL de-
volverá un error indicando que dicha columna no puede actualizarse. Este caso es idéntico al del atributo «NO-
ENTRY» en la instrucción INSERT. El mensaje que aparece es el siguiente:
Ejemplo 1:
update articulos set pr_vent = pr_vent * 1.05;
Esta instrucción actualiza todos los artículos incluidos en la tabla «articulos», incrementando el precio de venta
(«pr_vent») en un cinco por ciento.
Ejemplo 2:
update articulos set pr_vent = pr_vent * 1.05
where pr_cost >= 10000;
Esta instrucción incrementará los precios de venta de aquellos artículos de la tabla «articulos» cuyo precio de
coste («pr_cost») sea superior o igual a 10.000.
Pág. 107
MultiBase. Manual del SQL
Ejemplo 3:
update clientes set total_factura = (select sum((cantidad * precio) * (1 - (descuento / 100)))
from albaranes, lineas
where albaranes.cliente = 4
and albaranes.albaran = lineas.albaran)
where clientes.cliente = 4
Esta instrucción actualizará el total facturado al cliente cuyo código es el «4» en su ficha de la tabla «clientes».
En este caso, como «expresión» de la instrucción UPDATE se utiliza la tabla derivada de una instrucción SELECT.
Esta instrucción SELECT tiene que devolver obligatoriamente una sola fila, como es el caso de nuestro ejemplo.
CAPÍTULO 9. Transacciones
Pág. 109
MultiBase. Manual del SQL
Introducción
Una transacción es el conjunto de operaciones relativas a recuperación y actualización sobre la base de datos
que se realizan de forma unitaria e indivisible, esto es, o se realizan totalmente o no se realizan. En otras pala-
bras, es el modo de agrupar varias instrucciones DML (INSERT, DELETE y UPDATE) y asegurar que dicho grupo
se ejecute completamente.
Para poder contemplar transacciones en una base de datos, ésta tiene que ser una base de datos transaccional.
En los siguientes epígrafes se indican las operaciones necesarias para crear una base de datos transaccional y,
además, la forma de convertir una base de datos que no es transaccional en transaccional y viceversa.
Las características de esta instrucción se han explicado en el capítulo 5 de este mismo volumen al tratar las
instrucciones referentes al sublenguaje DML del CTSQL.
Además del directorio que representa la base de datos («.dbs»), en el directorio en curso o en cualquiera de los
indicados en la variable de entorno DBPATH se generará el fichero especificado en «fichero_log». El nombre de
«fichero_log» tiene que incluir el «path» completo. Este «fichero_log» no tiene formato ASCII, por lo que
NUNCA deberá ser editado. La única forma de manejar su información es mediante las instrucciones del CTSQL
que se comentan en este capítulo.
Ejemplo:
create database almacen with log in "/usr/ctldemo/lunes"
Esta instrucción genera el fichero «/usr/ctldemo/lunes» para recoger todas las operaciones de manejo y man-
tenimiento sobre las tablas de la base de datos. Asimismo, en la tabla «systables» del catálogo de la base de
datos existe una fila correspondiente a la identificación del fichero transaccional. La información que se guarda
para este fichero transaccional en esta tabla es la siguiente:
select * from systables
where tabname = "syslog"
tabname syslog
owner ctldemo
dirpath /usr/ctldemo/lunes
tabid 150
rowsize 0
ncols 1
nindexes 0
nrows 0
created 25/05/1994
version 0
tabtype L
nfkeys 0
part1 0
part2 0
part3 0
part4 0
part5 0
part6 0
part7 0
part8 0
Este fichero transaccional («fichero_log») tiene que existir mientras la base de datos se encuentre activa, o
bien hasta que se sustituya por otro fichero mediante la instrucción START DATABASE. En caso de perder el
fichero de transacciones, la base de datos no podrá abrirse jamás. En este supuesto, debemos conocer el
«path» completo donde residía dicho fichero transaccional y crearlo, aunque sea vacío.
Para más información sobre la tabla «systables» consulte el siguiente capítulo de este mismo volumen.
NOTAS:
1.— La única forma de crear una base de datos transaccional es mediante la ejecución de la instrucción indica-
da anteriormente.
2.— El fichero transaccional crecerá constantemente, pudiendo ocupar un espacio considerable en el disco
duro.
Al igual que sucedía en la instrucción CREATE DATABASE, el nombre del fichero transaccional («fichero_log»)
tendrá que incluir el «path» completo donde se ubicará.
Esta instrucción sólo puede realizarla el propietario de la base de datos, o bien el usuario que tenga permiso
«DBA» sobre ella. Asimismo, esta operación se tiene que realizar con la base de datos cerrada y sin que nadie la
tenga seleccionada. Por lo tanto, antes de ejecutar esta operación habrá que especificar la instrucción CLOSE
DATABASE:
CLOSE DATABASE
Por ejemplo, para comprobar que una base de datos no está seleccionada o abierta por algún usuario se puede
ejecutar la instrucción DATABASE con la cláusula «EXCLUSIVE»:
DATABASE base_datos [EXCLUSIVE]
Si la ejecución de esta instrucción no devuelve ningún error, la base de datos podrá cambiar de fichero transac-
cional. En conclusión, el proceso quedaría de la siguiente forma:
database almacen exclusive; {SI NO DEVUELVE NINGÚN ERROR}
close database;
start database almacen with log in "/usr/ctldemo/martes"
En caso de que deseásemos convertir una base de datos transaccional en no transaccional habría que ejecutar
igualmente la instrucción START DATABASE, pero con el nombre del fichero transaccional vacío (válido única-
mente a partir de la versión 2.0 de MultiBase):
start database base_datos with log in ""
En versiones de MultiBase anteriores a la 2.0, la única forma de eliminar las transacciones de una base de datos
era eliminando la fila grabada en la tabla del catálogo «systables». Por ejemplo:
database almacen exclusive; {SI NO DEVUELVE NINGÚN ERROR}
delete from systables
where tabname = "syslog";
close database;
database almacen;
Pág. 111
MultiBase. Manual del SQL
Como ya se ha indicado en el caso anterior, la ejecución de estas instrucciones sólo puede realizarla el propie-
tario de la base de datos, o aquel que tenga permiso «DBA» sobre la misma.
Control de transacciones
Una vez que la base de datos seleccionada dispone de un fichero transaccional es posible controlar las transac-
ciones. Una transacción consiste en controlar un determinado proceso, de tal forma que, en el hipotético caso
de que éste finalizase de manera anómala, tuviésemos la posibilidad de deshacer dicha operación. Para ello hay
que marcar explícitamente el principio y el fin de dicho proceso (grupo de instrucciones) con las siguientes ins-
trucciones del CTSQL:
BEGIN WORK Inicia la transacción.
COMMIT WORK Finaliza la transacción haciendo definitivos los cambios realizados por el proce-
so en cuestión.
ROLLBACK WORK Finaliza la transacción deshaciendo los cambios realizados en la base de datos
por dicho proceso.
La consecuencia práctica de estas instrucciones es que en el fichero transaccional («log file») se registrarán
tanto el comienzo de la transacción como su finalización. Esta operación permite que el programador decida en
base a condiciones de error de determinadas instrucciones la realización o no de las modificaciones incluidas
en la transacción como un todo.
Así pues, mediante este mecanismo se puede establecer un nuevo nivel de integridad de datos de las tablas
bajo control del programador en operaciones tales como borrado de datos obsoletos de tablas relacionadas
por un enlace («join»), asegurando además el cumplimiento de todas las instrucciones implicadas.
Una transacción puede suponer el bloqueo de todas las filas implicadas en las operaciones que reciben este
tratamiento, siendo liberadas al finalizarla (instrucciones COMMIT WORK y ROLLBACK WORK) y cerrando todos
los «cursores» abiertos durante la misma. Dicho bloqueo no es automático, sino que, para cada una de dichas
filas, hay que provocar una actualización (UPDATE), aunque ésta sea absurda (por ejemplo, actualizar una fila
dejándole el mismo valor. A partir de ese momento dicha fila estaría bloqueada para el resto de usuarios que
tuviesen acceso a la base de datos).
Ejemplo:
database almacen;
create table prueba (
codigo smallint,
nombre char(10));
begin work;
insert into prueba (codigo, nombre) values (1, "Nombre 1");
insert into prueba (codigo, nombre) values (2, "Nombre 2");
insert into prueba (codigo, nombre) values (3, "Nombre 3");
insert into prueba (codigo, nombre) values (4, "Nombre 4");
insert into prueba (codigo, nombre) values (5, "Nombre 5");
rollback work;
select * from prueba;
En este ejemplo, y considerando que la base de datos «almacen» es transaccional, se crea una tabla denomi-
nada «prueba». Posteriormente, comienza una transacción (BEGIN WORK) y se insertan varias filas en ella. Por
último, la instrucción ROLLBACK WORK elimina dichas filas, quedando la tabla vacía como puede comprobarse
con la instrucción SELECT final.
Sin embargo, si en lugar de especificar la instrucción ROLLBACK WORK hubiésemos indicado COMMIT WORK,
dichas filas quedarían grabadas en la tabla al final de la operación. Estos ejemplos se podrían realizar con las
instrucciones DELETE y UPDATE del CTSQL.
Ejemplo:
database almacen;
begin work;
update articulos set pr_vent = pr_vent * 1,05
commit work;
En caso de especificar la instrucción ROLLBACK WORK en lugar de COMMIT WORK la actualización no se reali-
zaría.
NOTAS:
1.— Téngase en cuenta que, en función de cada fabricante, cada sistema operativo UNIX/LINUX tiene un núme-
ro máximo de bloqueos («tunable parameters»), por lo que habrá que ajustar éste o utilizar, en su caso, la ins-
trucción LOCK TABLE, que inhibe el bloqueo de fila.
2.— En Windows sólo existe la posibilidad de bloqueo en las versiones para red de área local.
Recuperación de datos
Como ya se ha indicado anteriormente, la instrucción START DATABASE asigna un nuevo fichero transaccional a
la base de datos indicada en su sintaxis. Esta operación debe realizarse cada vez que se obtenga una copia de
seguridad de la base de datos. Dado que las operaciones de actualización, inserción y borrado sobre las tablas
de la base de datos se reflejan sobre dicho fichero transaccional, en caso de pérdida de información en la base
de datos aquélla podría recuperarse. La instrucción que se encarga de esta operación es la siguiente:
ROLLFORWARD DATABASE base_datos [TO fichero_resultado]
Mediante la ejecución de esta instrucción sería posible la reconstrucción de la base de datos partiendo de la
información registrada en un fichero transaccional sobre una copia de seguridad. La instrucción ROLLFORWARD
DATABASE sólo recuperará aquellas transacciones que hayan finalizado con COMMIT WORK y no con ROLL-
BACK WORK.
Pág. 113
MultiBase. Manual del SQL
La base de datos indicada en esta instrucción («base_datos») tiene que ser transaccional. El fichero indicado en
la cláusula «TO» («fichero_resultado») sirve para almacenar el resultado de la ejecución de dicha instrucción.
A continuación se expone un supuesto práctico con el procedimiento lógico a la hora de asignar ficheros tran-
saccionales al realizar diariamente copias de seguridad.
• Viernes:
El fichero transaccional que se encuentra activo en la base de datos el viernes es: «/usr/ctldemo/viernes»
• Lunes:
start database almacen with log in "/usr/ctldemo/lunes";
Hacer copia de seguridad de la base de datos junto con el fichero transaccional antiguo
«/usr/ctldemo/viernes».
Procesos de actualización, inserción y borrado sobre las tablas de la BD.
• Martes:
start database almacen with log in "/usr/ctldemo/martes";
Hacer copia de seguridad de la base de datos junto con el fichero transaccional antiguo «/usr/ctldemo/lunes».
Procesos de actualización, inserción y borrado sobre las tablas de la BD.
• Miércoles:
start database almacen with log in "/usr/ctldemo/miercole";
Hacer copia de seguridad de la base de datos junto con el fichero transaccional antiguo
«/usr/ctldemo/martes».
Procesos de actualización, inserción y borrado sobre las tablas de la BD.
¡CAÍDA DEL SISTEMA CON PÉRDIDA DE INFORMACIÓN DE ALGUNAS TABLAS!
Recuperar la copia de seguridad del «martes». Inicialmente, parece que todo el trabajo del «miércoles» se ha
perdido.
Ejecutar:
rollforward database almacen
Con esta instrucción se recuperarán todas las operaciones realizadas en la base de datos que se encuentren
registradas en el fichero transaccional «miercole».
En principio, la base de datos queda en estado normal de explotación, habiendo recuperado todo el trabajo del
miércoles excepto la última transacción (BEGIN WORK), que no finalizó por caída del sistema.
• Jueves:
start database almacen with log in "/usr/ctldemo/jueves";
Hacer copia de seguridad de la base de datos junto con el fichero transaccional antiguo
«/usr/ctldemo/miercole».
Procesos de actualización, inserción y borrado sobre las tablas de la BD.
etc., etc.
Pág. 115
MultiBase. Manual del SQL
Introducción
El catálogo de una base de datos está compuesto por un conjunto de tablas donde se recoge información sobre
qué tablas, columnas, índices, permisos, etc. tiene la base de datos a la que pertenecen. El mantenimiento de
estas tablas es automático por parte del CTSQL, y el nombre de todas ellas comienza por «sys*». Físicamente
se encuentran ubicadas dentro del subdirectorio generado para la base de datos («.dbs»). Estas tablas son las
siguientes:
systables Tablas que componen la base de datos.
syscolumns Columnas de cada una de las tablas.
sysindexes Índices de cada tabla.
systabauth Permisos de acceso a tablas.
syscolauth Permisos de acceso a columnas.
sysusers Permisos de acceso a la base de datos.
sysviews Composición de «views».
sysdepend Estructura de las «views».
syssynonyms Sinónimos de tablas.
sysforeign Claves referenciales («foreign keys») de cada tabla.
syscolattr Atributos CTSQL de cada columna.
syscollating Secuencia de ordenación.
A la hora de trabajar con CTSQL, estas tablas son comunes bajo cualquiera de los entornos operativos sobre los
que funciona MultiBase (UNIX/LINUX, y Windows). Por lo que respecta a instalaciones con «gateways», la única
diferencia radica en que el nombre de las tablas comienza por «mb» en lugar de «sys»
En los epígrafes que siguen se comentan en detalle las características, funciones y estructura de cada una de las
tablas del catálogo de la base de datos.
rowsize smallint,
ncols smallint,
nindexes smallint,
nrows integer,
created date,
version integer,
tabtype char( 1),
nfkeys smallint,
part1 smallint,
part2 smallint,
part3 smallint,
part4 smallint,
part5 smallint,
part6 smallint,
part7 smallint,
part8 smallint) ;
Las características de cada una de las columnas de esta tabla son las siguientes:
tabname Esta columna contiene los nombres de todas las tablas y «views» que compo-
nen la base de datos. Asimismo, en caso de que la base de datos sea transac-
cional, el nombre que se asigna a esta columna es «syslog», para indicar todas
las características del fichero transaccional.
owner [Propietario de la tabla]. El propietario de una tabla es el usuario que la crea.
Estos propietarios se fijan para permitir o no el acceso al resto de usuarios de la
máquina. En UNIX/LINUX, el nombre de este propietario es el del usuario
UNIX/LINUX que la crea. Por su parte, en las versiones monopuesto de Win-
dows el nombre del propietario es «system», mientras que en instalaciones en
entornos de red de área local o en arquitecturas del tipo cliente-servidor el
nombre del propietario se fija mediante la variable de entorno DBUSER (consul-
te el capítulo sobre «Variables de Entorno» en el Manual del Administrador).
Las tablas del catálogo de la base de datos («sys*») siempre adquieren el pro-
pietario «trans».
dirpath Esta columna tomará o no valor dependiendo de si la información que corres-
ponde a la fila en curso se refiere a una tabla, a una «view» o a un fichero tran-
saccional. A continuación se indica el valor que toma en cada uno de estos ca-
sos:
tablas «Path» o «ruta» donde se crea físicamente la tabla. Esta co-
lumna toma el valor indicado en la cláusula «IN» de la instruc-
ción CREATE TABLE del CTSQL. En caso de no indicar dicha
cláusula, esta columna adquiere el nombre del fichero físico
que representa a la tabla indicada en «tabname». El nombre
de este fichero siempre estará compuesto por ocho caracte-
res: Parte del nombre de la tabla y un número. Este número se
corresponde con el número de identificación de la tabla «ta-
bid» en esta misma tabla «systables». Este número «tabid»
cambia de valor en el momento que se produce una alteración
de la estructura de la tabla. Por lo tanto, cada alteración de
tabla implica el cambio de nombre del fichero físico que la re-
presenta.
Pág. 117
MultiBase. Manual del SQL
nfkeys Número de claves referenciales («foreign keys») de la tabla en curso. Esta co-
lumna sólo tendrá un valor real en el caso de las tablas, nunca en el de las
«views» o en el del fichero transaccional.
part1-8 Estas columnas contendrán el número de columna de la tabla que se está defi-
niendo componente de la clave primaria («primary key»). Dicho número de co-
lumna se corresponde con la columna «colno» de la tabla «syscolumns». Aque-
lla columna que contenga como valor un cero indicará el final de la clave prima-
ria. Hemos de tener en cuenta que una clave primaria es un índice único y que
éste puede estar compuesto como máximo por 8 columnas, o bien que entre
todas ellas totalicen como máximo 120 bytes.
Ejemplo:
select * from systables;
Pág. 119
MultiBase. Manual del SQL
collength Esta columna contiene el número de bytes que ocupa cada una de las columnas
«colno» que se corresponden con la tabla «tabid». El número de bytes que
ocupa cada uno de los tipos de datos CTSQL es el siguiente:
VALORES
Tipo Mínimo Máximo Nº bytes
CHAR(n) 1 carácter 32.767 caracteres n
SMALLINT -32.767 +32.767 2
INTEGER -2.147.483.647 +2.147.483.647 4
TIME 00:00:01 24:00:00 4
VALORES
-130 125
DECIMAL(m,n) 10 10 1 + m/2
SERIAL(n) -2.147.483.647 +2.147.483.647 4
DATE 01/01/0001 31/12/9999 4
-130 125
MONEY(m,n) 10 10 1 + m/2
Sin embargo, en el caso de los tipos de datos DECIMAL y MONEY esta columna
«collength» se incrementa con respecto al siguiente algoritmo.
(PRECISION * 256) + ESCALA
Por ejemplo, si se define una columna como «decimal(8,2)», el valor que se al-
macenará en la columna «collength» será:
8 * 256 + 2 = 2306
Ejemplo:
select colname, tabid, colno, coltype, collength from syscolumns
Pág. 121
MultiBase. Manual del SQL
idxtype Indica el tipo de índice creado para la tabla «tabid». Esta columna puede tomar
los siguientes valores:
U [Único]. No admite valores duplicados.
D ó «-» [Duplicado]. Admite valores duplicados.
clustered Método para extraer información ordenada. Los valores que puede tomar esta
columna son los siguientes:
C «Clustered».
Espacio o «-» No «clustered».
Esta columna se utiliza únicamente por compatibilidad.
part1-8 Estas columnas contendrán el número de columna de la tabla que se está defi-
niendo componente del índice especificado en «idxname». Dicho número de
columna se corresponde con la columna «colno» de la tabla «syscolumns».
Aquella columna que contenga como valor un cero indicará el final del índice. Si
el identificador es positivo, el índice será ascendente por dicha columna, y si es
negativo, descendente.
Ejemplo:
select idxname, owner, tabid, idxtype ty, clustered cl,
part1 p1, part2 p2, part3 p3, part4 p4, part5 p5,
part6 p6, part7 p7, part8 p8
from sysindexes
where tabid <= 12
Pág. 123
MultiBase. Manual del SQL
tabid Esta columna contiene el número de identificación de una tabla. Este número
se genera automáticamente en la tabla «systables» y varía cada vez que se eje-
cuta una instrucción ALTER TABLE del CTSQL sobre la tabla que se está defi-
niendo. Por lo tanto, se trata de la columna de enlace con la tabla «systables».
tabauth Esta columna indica los permisos sobre la tabla especificada en la columna «ta-
bid». Esta columna tiene tipo CHAR con una longitud de siete bytes. Cada una
de las posiciones (bytes) de esta columna tiene el siguiente significado:
Pos. 1.-s(ELECT) Permiso de lectura de la tabla indicada en «tabid» para el
usuario «grantee».
Pos. 2.-u(PDATE) Permiso de actualización de la tabla indicada en «tabid» para
el usuario «grantee».
Pos. 3.-(*) Este tercer carácter sólo admite un asterisco, que sirve para
indicar que dicha tabla tiene columnas sobre las que existen
permisos especiales de lectura o modificación con respecto a
un usuario «grantee». En caso de aparecer dicho carácter en
esta posición, hay que consultar la tabla «syscolauth» para
comprobar que existen permisos sobre ciertas columnas de la
tabla que se está definiendo («tabid»).
Pos. 4.-i(NSERT) Permiso para insertar filas en la tabla indicada en «tabid» para
el usuario «grantee».
Pos. 5.-d(ELETE) Permiso para borrar filas de la tabla indicada en «tabid» para
el usuario «grantee».
Pos. 6.-(INDE)x Permiso para crear índices sobre la tabla indicada en «tabid».
Pos. 7.-a(LTER TABLE) Permiso para alterar la estructura de la tabla es-
pecificada en «tabid», así como renombrar la tabla o alguna
de sus columnas.
Los valores que pueden tomar cada una de las posiciones son los que se en-
cuentran en minúsculas en su definición. En caso de no conceder dicho permiso
debe aparecer un guión («-»). Si dichos valores se especifican en mayúsculas
significará que el usuario «grantee» tiene posibilidad de concesión de permisos
a otros usuarios sobre la tabla indicada en «tabid».
En el caso de las tablas del catálogo de la base de datos, el permiso que toman por defecto es el de lectura para
todos los usuarios que tengan acceso a la base de datos. La información grabada en la tabla «systabauth» para
todas las tablas del catálogo de la base de datos es la siguiente:
select grantor, grantee, tabid, tabauth
from systabauth
where tabid <= 12
Pág. 125
MultiBase. Manual del SQL
Por su parte, la información grabada en la tabla «systabauth» para el resto de tablas de la base de datos es la
siguiente:
select grantor, grantee, tabid, tabauth
from systabauth
where tabid >= 150
La palabra genérica «PUBLIC» indica que todos los usuarios tienen los permisos o restricciones expuestos en la
columna «tabauth».
«DBA» por parte del administrador de la base de datos para conceder permisos
al resto de usuarios.
En el caso de las «views» esta columna no contiene ningún valor.
grantee Indica el nombre del usuario al que se conceden o revocan permisos sobre las
columnas «colno» de la tabla.
Si se indica la palabra reservada «public» en esta columna significará que todos
los permisos que se especifiquen en la columna «colauth» serán válidos para
todos los usuarios que tengan acceso a la base de datos.
tabid Esta columna contiene el número de identificación de una tabla a la que perte-
necen las columnas sobre las que se especifican los permisos «colno». Este
número «tabid» se genera automáticamente en la tabla «systables» y varía ca-
da vez que se ejecuta una instrucción ALTER TABLE del CTSQL sobre la tabla que
se está definiendo. Por lo tanto, se trata de la columna de enlace con la tabla
«systables».
colno Número que identifica a la columna sobre la que se asignan los permisos especi-
ficados en la columna «colauth». Esta columna es el enlace con la columna
«colno» de la tabla «syscolumns».
colauth Esta columna contiene los permisos sobre la columna especificada en «colno».
Esta columna tiene dos posiciones, ya que se trata de un tipo de dato «char(2)».
Los valores que admite esta columna son combinaciones de los siguientes:
su Permite leer y modificar la columna «colno».
s- Sólo permite leer la columna «colno».
-u Sólo permite modificar la columna «colno».
Si estos valores se indican en mayúsculas significará que el usuario «grantee»
tiene posibilidad de concesión de permisos a otros usuarios sobre la tabla indi-
cada en «tabid». Esta modificación la provoca la cláusula «WITH GRANT OP-
TION» de la instrucción GRANT del sublenguaje de control de datos (DCL) del
CTSQL.
Ejemplo:
select grantor, grantee, tabid, colno, colauth
from syscolauth
Pág. 127
MultiBase. Manual del SQL
En este ejemplo, el usuario «manuel» tiene permiso «DBA», siendo a su vez el propietario de la base de datos;
por su parte, el usuario «trans» tiene el permiso «RESOURCE», mientras que el resto de usuarios de la máquina
tienen permiso «CONNECT».
Pág. 129
MultiBase. Manual del SQL
En este ejemplo, la tabla derivada indica que la «view» identificada por el «tabid» «167» está compuesta por la
lectura de tres tablas.
En este ejemplo existen dos sinónimos sobre la misma tabla. La tabla será aquella que tenga asignado el núme-
ro «183» en la columna «tabid» de la tabla «systables».
Pág. 131
MultiBase. Manual del SQL
referencial referenciada dtabid rtabid fkname on_update on_delete part1 part2 part3 part4 part5 part6 part7 part8
proveedores provincias 158 150 for1_pro R R 8 0 0 0 0 0 0 0
articulos proveedores 168 158 for1_art R R 2 0 0 0 0 0 0 0
articulos unidades 168 152 for2_art R R 11 0 0 0 0 0 0 0
albaranes clientes 162 164 for1_alb R R 2 0 0 0 0 0 0 0
albaranes formpagos 162 151 for2_alb R R 6 0 0 0 0 0 0 0
clientes provincias 164 150 for1_cli R R 8 0 0 0 0 0 0 0
clientes formpagos 164 151 for2_cli R R 11 0 0 0 0 0 0 0
Pág. 133
MultiBase. Manual del SQL
R RIGHT
L LEFT
Z ZEROFILL
E NOENTRY
M NOUPDATE
"Literal" (LABEL, CHECK, FORMAT, DEFAULT)
Este literal sólo tendrá lugar cuando en la columna «type»
aparezca cualquiera de los valores siguientes: «L»ABEL,
«C»HECK, «F»ORMAT y «D»EFAULT.
Ejemplo:
select * from syscolattr;
Tabla «syscollating»
Esta tabla es generada y mantenida de forma automática por el gestor de la base de datos CTSQL. Su finalidad
es contener los caracteres ASCII que se emplearán en las operaciones de ordenación y comparación de datos.
Esta tabla se construye a partir de un fichero de secuencia de ordenación (la forma de construir este tipo de
ficheros se explica en el capítulo «Otros Ficheros de Administración» en el Manual del Administrador). Este
fichero se asocia a la base de datos en el momento de su creación (instrucción CREATE DATABASE).
La estructura de esta tabla es la siguiente:
create table syscollating(
secuence char( 256)) ;
Pág. 135
MultiBase. Manual del SQL
• Al igual que en el caso anterior, si periódicamente va a cargar datos mediante la instrucción LOAD y
tiene la certeza de que los registros a cargar no van a generar duplicidades en la clave primaria de la
tabla receptora, borrar los índices antes de iniciar la carga, regenerándolos posteriormente. Este
proceso será más rápido. Adicionalmente, se puede bloquear el acceso a esa tabla por otros usuarios
con la instrucción LOCK TABLE.
• No construir índices sobre tablas pequeñas (menos de 200 filas), ya que su rendimiento no sería sa-
tisfactorio. El acceso secuencial en este número de filas es inmediato.
• No construir índices duplicados sobre columnas con un conjunto pequeño de valores posibles (por
ejemplo: sexo, estado civil, respuestas sí/no, etc.), puesto que el acceso al índice ya consume tiempo
y se convierte en este caso en una búsqueda secuencial (todas las filas que contengan el mismo va-
lor).
• Utilice condiciones sobre columnas indexadas en la cláusula «WHERE» y, si desea una ordenación
específica, defina un índice con las columnas de la cláusula «ORDER BY» de la instrucción SELECT.
El optimizador de CTSQL
El optimizador de «queries» de CTSQL decide una estrategia de búsqueda sobre las tablas incluidas en las sen-
tencias en base a los siguientes factores:
• Índices existentes.
• Uso del «ROWID».
• Número de filas en la tabla.
• Enlaces («joins»).
• Orden de las condiciones en la cláusula «WHERE».
• Orden de las tablas en la cláusula «FROM» de la instrucción SELECT.
Para tomar una decisión el optimizador realiza tres pasos:
• Paso 1.º. Selección de rangos de acceso a cada tabla. Analiza las condiciones incluidas en la cláusula
«WHERE» y las agrupa por tabla, evaluando los rangos de valores de cada tabla.
• Paso 2.º. En base a los rangos encontrados selecciona para cada tabla la mejor estrategia de
búsqueda, es decir, acceso por índice, por «ROWID» o secuencial. Para cada tabla evalúa la mejor
forma de acceso correspondiente a dichos rangos. Para ello comprueba si las columnas implicadas
forman parte de algún índice, si alguna condición es más restrictiva que las demás, si incluye condi-
ciones por «ROWID» o bien, en el peor de los casos, determinará si es necesario acceder secuen-
cialmente a toda la tabla.
• Paso 3.º. Selecciona en qué secuencia se accederá a las diferentes tablas involucradas. Evalúa cuál
será la primera tabla a la que se deberá acceder. Idealmente, esta tabla debe ser la que devuelva un
menor número de filas de una forma más eficaz (con el menor número de lecturas posible). Para ello
se utiliza la información obtenida en el paso anterior. Una vez seleccionada una tabla, se vuelve al
Paso 2.º con el fin de revisar los «joins» entre la tabla seleccionada y el resto. Este proceso conti-
nuará hasta terminar con todas las tablas de la sentencia.
En los siguientes apartados se explica en detalle la forma en la que actúa el optimizador bajo diferentes condi-
ciones.
El optimizador sólo se decantará por una estrategia determinada —índice seleccionado o secuencia de tablas—
si ésta es mejor que el resto. Por tanto, si no existe ningún motivo que lo justifique utilizará la primera estrate-
gia encontrada. Esto se refiere tanto al índice seleccionado para cada tabla como a la secuencia de acceso a las
tablas. De este modo se permite que el programador pueda forzar al SQL a utilizar una estrategia determinada,
como se verá más adelante.
Pág. 137
MultiBase. Manual del SQL
8.— Una condición optimizable por «ROWID» es siempre mejor que un acceso por índice. Por ejemplo:
select * from clientes
where cliente > 15
and rowid in (13, 23, 1, 45)
no utilizará el índice «cliente», sino que accederá directamente a las filas indicadas por el «ROWID».
9.— En general, una cláusula «ORDER BY» hace que sobre el resultado obtenido de la SELECT se realice una
ordenación posterior, salvo si esta ordenación puede ser optimizada. Esto ocurre si se dan las siguientes condi-
ciones (todas):
a) La SELECT se realiza sobre una única tabla.
Tabla a Tabla b
a1 b1
a2 b2
a3
a4
a5
La sentencia: «SELECT * FROM a, b», necesitará el siguiente número de lecturas de disco dependiendo de la
tabla por la que se empiece:
a) Tabla «a», Tabla «b»: Las lecturas necesarias serán a1, b1, b2, a2, b1, b2, …a5, b1, b2. En total 15 lecturas.
b) Tabla «b», Tabla «a»: Las lecturas serán, b1, a1, a2, a3, a4, a5, b2, a1, a2, a3, a4, a5. En total 12 lecturas.
Esta diferencia crece proporcionalmente a la diferencia de filas entre las tablas y exponencialmente al número
de tablas.
Para establecer qué tabla es la que devuelve menor número de filas, CTSQL utilizará las condiciones más res-
trictivas. Es decir, una tabla que pueda ser accedida mediante un índice o «ROWID» es mejor que otra que no
pueda serlo. Si existen dos tablas que no pueden ser accedidas por índice, se elegirá la que tenga menor núme-
ro de filas. CTSQL obtendrá este número del campo «nrows» de la tabla «systables» del catálogo del sistema.
Este campo solamente se actualiza por medio de la instrucción UPDATE STATISTICS del SQL. Por tanto, convie-
ne tener en cuenta que para algunas sentencias donde intervenga un gran número de tablas con grandes dife-
rencias en el número de filas será conveniente tener este valor razonablemente actualizado.
Si dos tablas no van a ser accedidas por índices y tienen el mismo número de filas en la columna «nrows»,
CTSQL elegirá el orden de acceso definido en la cláusula «FROM».
A continuación se incluyen ejemplos de cómo actúa el optimizador con diferentes sentencias SELECT.
Pág. 139
MultiBase. Manual del SQL
• Ejemplo 1:
select provincia, descripcion from provincias
En este caso, el acceso a la tabla «provincias» es secuencial, ya que al tener que leer toda la tabla resultará el
procedimiento más rápido.
• Ejemplo 2:
select provincia, descripcion from provincias
where provincia > 5
En este caso, la columna «provincia» constituye la clave primaria de la tabla «provincias». Por lo tanto, al indi-
car una condición sobre dicha columna, el índice seleccionado para este acceso será automáticamente la clave
primaria.
• Ejemplo 3:
select provincia, descripcion from provincias
where provincia > 5 or 1 = 0
Siguiendo con el ejemplo anterior, al añadir una condición OR cuya evaluación es falsa, el optimizador anula el
acceso por dicha clave primaria.
• Ejemplo 4:
select provincia, descripcion from provincias
where descripcion matches "A*"
En este ejemplo, tanto si la columna «descripcion» es o no índice, el acceso siempre será secuencial. Ello es
debido a que la condición no es optimizable.
Para los ejemplos que siguen a continuación emplearemos la siguiente estructura de tabla:
create table pruebas (codigo smallint not null,
descripcion char(20) not null,
direccion char(30),
provincia smallint)
primary key (codigo, descripcion);
• Ejemplo 6:
select codigo, descripcion from pruebas
where codigo = 1
and descripcion > "valor"
En este ejemplo, el índice que se utilizará será la clave primaria, compuesta por las columnas «codigo» y «des-
cripcion» La razón de elegir la clave primaria radica en que se hace referencia a ella en primer lugar mediante la
condición «codigo=1».
Lo mismo sucedería si se cambiase el orden de las condiciones, es decir:
select codigo, descripcion from pruebas
where descripcion > "valor"
and codigo = 1
La razón en este caso es que, aunque ambos índices son referenciados al mismo tiempo por medio de la condi-
ción: «descripción > ‘valor’» se elige la clave primaria (ver sección 11.4, punto 6).
• Ejemplo 7:
Para forzar el uso del índice «i1_pruebas» en lugar de la clave primaria en el ejemplo anterior, es necesario
obligar a que el optimizador obvie la condición por «codigo»:
select codigo, descripcion from pruebas
where (codigo = 1 or 1 = 0)
and descripcion > "valor"
• Ejemplo 8:
select codigo, descripcion from pruebas
where codigo > 1
and descripcion = "valor"
En este ejemplo, la condición que emplea el operador igual («=») es la más restrictiva (hace referencia a todos
los componentes del índice «i1_pruebas» por igual (ver sección 11.4, punto 4).
Los siguientes ejemplos toman como base este esquema de la tabla «pruebas1»:
create table pruebas1 (codigo smallint not null,
empresa char(30) not null,
nombre char(20),
apellido1 char(20),
apellido2 char(20),
direccion char(30),
provincia smallint,
telefono char(7))
primary key (codigo, empresa);
• Ejemplo 9:
select * from pruebas1
where empresa > ""
and codigo > 10
and nombre matches "A*"
En este ejemplo están referenciadas las primeras columnas de los tres índices por condiciones de «>» o «>=»,
por lo que los tres índices son igual de válidos. La primera condición («empresa > ""») referencia a dos índices
(la clave primaria e «i1_pruebas1»). Se seleccionará la clave primaria (ver sección 11.4, punto 6).
Pág. 141
MultiBase. Manual del SQL
• Ejemplo 10:
select * from pruebas1
where empresa = "TransTOOLs"
and codigo > 10
and nombre = "Alfonso"
En este ejemplo los tres índices son válidos, ya que en cada uno de ellos se referencia su primera columna. El
índice elegido será «i1_pruebas1» ya que todas las columnas que lo componen están condicionadas por «=».
Si se quiere forzar al optimizador para utilizar otro índice (la clave primaria) bastará con anular la posibilidad de
optimizar por «i1_pruebas1»:
select * from pruebas1
where (empresa = "TransTOOLs" or 1 = 0)
and codigo > 10
and nombre = "Alfonso"
• Ejemplo 11:
select * from pruebas1
where codigo > 1
and empresa > "TransTOOLs"
and nombre = "Manuel"
and apellido1 = "Ruiz"
and apellido2 = "Lopez"
En este caso, al estar referenciadas por el operador igual «=» las tres columnas del índice «i2_pruebas1», éste
será el que se utilice.
• Ejemplo 12:
select * from pruebas1
where nombre > "Manuel"
and apellido1 = "Ruiz"
and codigo > 1
and empresa >= "TransTOOLs"
En este ejemplo no existe ningún índice que tenga condicionadas todas sus columnas por el operador igual. El
índice elegido es «i2_pruebas1», ya que es el primero referenciado.
A continuación se comenta por bloques la información escrita por el optimizador en el fichero indicado en la
variable de entorno «OUTOPT3» (ejemplo: «OUTOPT3=fichero»):
Sentence :
database ?
;
Sentence :
update statistics
;
Sentence :
select * from clientes, provincias, albaranes, lineas, articulos, proveedores, provincias provpr
where clientes.provincia = provincias.provincia
and clientes.cliente = albaranes.cliente
and albaranes.albaran = lineas.albaran
and lineas.articulo = articulos.articulo
and articulos.proveedor = proveedores.proveedor
and proveedores.provincia = provpr.provincia
;
Table Order:
Comentario: Hasta este momento, el optimizador no puede determinar ningún índice que sea válido para acce-
der a ninguna de las tablas implicadas en la sentencia. El motivo es que todas las condiciones incluidas en la
sentencia se refieren únicamente a los «joins» establecidos entre las tablas. Al no encontrar ninguna condición
del tipo «columna = valor», el optimizador no puede seleccionar aún ningún índice.
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100)
provincias (Keyparts 0 Unique, Rows 50) <- (num rows)
Compare Tables:
provincias (Keyparts 0 Unique, Rows 50) <-
albaranes (Keyparts 0 Unique, Rows 50)
Compare Tables:
provincias (Keyparts 0 Unique, Rows 50) <-
lineas (Keyparts 0 Unique, Rows 512)
Compare Tables:
provincias (Keyparts 0 Unique, Rows 50) <-
articulos (Keyparts 0 Unique, Rows 194)
Pág. 143
MultiBase. Manual del SQL
Compare Tables:
provincias (Keyparts 0 Unique, Rows 50) <-
proveedores (Keyparts 0 Unique, Rows 100)
Compare Tables:
provincias (Keyparts 0 Unique, Rows 50) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : provincias —
Comentario: A continuación se comprueba cuál es la primera tabla que deberá ser accedida. En este caso la
tabla seleccionada es «provincias», ya que es la que aparece en primer lugar en la cláusula «FROM» de entre
las que cuentan con el menor número de filas (50).
Index clientes.primary = (1) Cols: 1 Used 0 EQ 0
Index clientes.i2_cliente= (2) Cols: 1 Used 0 EQ 0
— Index Selected : None —
Comentario: Se vuelve a repetir el proceso de selección de índice para el resto de tablas (exceptuando la ya se-
leccionada —«provincias»—). En nuestro ejemplo, la única tabla que hace «join» con «provincias» es «clientes»,
pero su columna de enlace no constituye un índice simple ni tampoco es la primera de un índice compuesto, por
lo que el optimizador continúa sin poder seleccionar ningún índice.
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100, (ATTR=VAL)) <-
albaranes (Keyparts 0 Unique, Rows 50)
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100, (ATTR=VAL)) <-
lineas (Keyparts 0 Unique, Rows 512)
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100, (ATTR=VAL)) <-
articulos (Keyparts 0 Unique, Rows 194)
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100, (ATTR=VAL)) <-
proveedores (Keyparts 0 Unique, Rows 100)
Compare Tables:
clientes (Keyparts 0 Unique, Rows 100, (ATTR=VAL)) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : clientes —
Comentario: El hecho de haber seleccionado ya la tabla de «clientes» ha permitido valorar la condición «clien-
tes.cliente=albaranes.cliente» como si fuese «albaranes.cliente=VALOR». Como la columna «cliente» de la tabla
«albaranes» lleva asociado un índice («i2_albaran»), éste será seleccionado para acceder a la tabla.
Compare Tables:
albaranes (Keyparts 1 , Rows 50, (ATTR=VAL)) <-
lineas (Keyparts 0 Unique, Rows 512)
Compare Tables:
albaranes (Keyparts 1 , Rows 50, (ATTR=VAL)) <-
articulos (Keyparts 0 Unique, Rows 194)
Compare Tables:
albaranes (Keyparts 1 , Rows 50, (ATTR=VAL)) <-
proveedores (Keyparts 0 Unique, Rows 100)
Compare Tables:
albaranes (Keyparts 1 , Rows 50, (ATTR=VAL)) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : albaranes —
Comentario: Por el mismo motivo que en el caso anterior, ahora ha sido posible seleccionar la tabla «albara-
nes».
Index lineas.primary = (1, 2) Cols: 2 Used 1 EQ 1 <-
Index lineas.alblin_ind = (1) Cols: 1 Used 1 EQ 1 <-
Index lineas.i_artlineas = (3, 4) Cols: 2 Used 0 EQ 0
— Index Selected : alblin_ind —
Comentario: El hecho de haber seleccionado ya la tabla de «albaranes» ha permitido valorar la condición «al-
baranes.albaran=lineas.albaran» como si fuese «lineas.albaran=VALOR». Como la columna «albaran» de la
Pág. 145
MultiBase. Manual del SQL
tabla «lineas» lleva asociado un índice («alblin_ind»), éste será seleccionado para acceder a la tabla. En este
caso no se elige la clave primaria.
Compare Tables:
lineas (Keyparts 1 , Rows 512, (ATTR=VAL)) <-
articulos (Keyparts 0 Unique, Rows 194)
Compare Tables:
lineas (Keyparts 1 , Rows 512, (ATTR=VAL)) <-
proveedores (Keyparts 0 Unique, Rows 100)
Compare Tables:
lineas (Keyparts 1 , Rows 512, (ATTR=VAL)) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : lineas —
Comentario: Por el mismo motivo que en el caso anterior, ahora ha sido posible seleccionar la tabla «lineas».
Index articulos.primary = (1, 2) Cols: 2 Used 1 EQ 1 <-
Index articulos.i3_articulo = (1) Cols: 1 Used 1 EQ 1 <-
Index articulos.i4_articulo = (2) Cols: 1 Used 0 EQ 0
Index articulos.i2_articulo = (3) Cols: 1 Used 0 EQ 0
— Index Selected : i3_articulo —
Comentario: El hecho de haber seleccionado ya la tabla de «lineas» ha permitido valorar la condición «line-
as.articulo=articulos.articulo» como si fuese «articulos.articulo=VALOR». Como la columna «articulo» de la ta-
bla «articulos» lleva asociado un índice («i3_articulo»), éste será seleccionado para acceder a la tabla. En este
caso no se ha seleccionado la clave primaria.
Compare Tables:
articulos (Keyparts 1 , Rows 194, (ATTR=VAL)) <-
proveedores (Keyparts 0 Unique, Rows 100)
Compare Tables:
articulos (Keyparts 1 , Rows 194, (ATTR=VAL)) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : articulos —
Comentario: Por el mismo motivo que en el caso anterior, ahora ha sido posible seleccionar la tabla «articulos».
Index proveedores.primary = (1) Cols: 1 Used 1 EQ 1 <-
Index proveedores.i2_proveedor = (2) Cols: 1 Used 0 EQ 0
— Index Selected : primary —
Comentario: El hecho de haber seleccionado ya la tabla de «articulos» ha permitido valorar la condición «arti-
culos.proveedor=proveedores.proveedor» como si fuese «proveedores.proveedor=VALOR». Como la columna
«proveedor» de la tabla «proveedores» lleva asociado un índice (clave primaria), éste será seleccionado para
acceder a la tabla. En este caso sí se elige la clave primaria.
Compare Tables:
proveedores (Keyparts 1 Unique, Rows 100, (ATTR=VAL)) <-
provpr (Keyparts 0 Unique, Rows 50)
— Table Selected : proveedores —
Comentario: Por el mismo motivo que en el caso anterior, ahora ha sido posible seleccionar la tabla «proveedo-
res».
Comentario: Por último, el optimizador escribe en el fichero un resumen del resultado de la optimización. Este
resumen indica que la estrategia de acceso será la siguiente: Se accederá en primer lugar a la tabla «provin-
cias» de forma secuencial («0 Ranges»); para cada fila leída se leerá toda la tabla «clientes» («0 Ranges»); por
cada una de las filas válidas accederá a la tabla «albaranes» por un índice («1 Range»); por cada una de aqué-
llas accederá a la tabla «lineas» por un índice («1 Range»), etc.
Como se puede observar, la tabla «clientes» va a ser leída en su totalidad por cada fila de «provincias». Esto se
podría evitar añadiendo una condición que fuerce al optimizador a seleccionar como primera tabla la tabla
«clientes»:
select * from clientes, provincias, albaranes,
lineas, articulos, proveedores, provincias provpr
where clientes.provincia = provincias.provincia
and clientes.cliente = albaranes.cliente
and albaranes.albaran = lineas.albaran
and lineas.articulo = articulos.articulo
and articulos.proveedor = proveedores.proveedor
and proveedores.provincia = provpr.provincia
and clientes.cliente > 0
Pág. 147
MultiBase
MANUAL DEL SQL
Pág. 149
MultiBase. Manual del SQL
Estadísticas CTSQL
El gestor de base de datos generá opcionalmente un fichero de estadísticas donde se muestra información de
la ejecución de instrucciones SQL en la conexión a la base de datos:
El fichero se genera cuando se ejecuta la instrucción «set statistics to 0» o cuando el cliente se desconecta del
servidor.
2. Mediante la variable de entorno CTSQLSTATISTICS:
Los posibles valores son:
TRUE/YES Activa las estadísticas
FALSE/NO Desactiva las estadísticas
Se debe definir en el fichero de configuración del motor (ctsql.ini).
El fichero de estadísticas se genera cuando finaliza la conexión a la base de datos. En el caso de estar definida la
variable de entorno y también se utilice la instrucción SQL, el fichero se generará cuando se ejecute la senten-
cia SQL (set statistics to 0).
Tiempo Ejecución (ms)
Tiempo Prepare (ms)
Índice Usado
Nº de Fetch
Instrucción
Nº de Open
Statement
ID
Pág. 151
MultiBase
MANUAL DEL SQL
Pág. 153
MultiBase. Manual del SQL
Introducción
El lenguaje de cuarta generación de MultiBase, CTL, tiene estructura de lenguaje con SQL embebido. Esto signi-
fica que la única forma en que el CTL permite el manejo de datos de la base de datos es a través del SQL. Para
ello, CTL dispone de un conjunto de instrucciones que permiten comunicarse con el SQL que se esté emplean-
do. Cuando CTL encuentra una de estas instrucciones, la enviará al CTSQL para su ejecución, y éste, una vez
procesada, devolverá el resultado al CTL.
Además de su propio gestor de base de datos, el CTSQL, MultiBase puede utilizar también otros de los existen-
tes en el mercado (Oracle, Informix, Ingres), tal y como se ha explicado en el capítulo anterior de este mismo
volumen.
Para poder realizar la comunicación con SQL de forma efectiva, CTL dispone de ciertos elementos que son los
que se comentan en los epígrafes siguientes.
Variables receptoras
Las «variables receptoras» son variables de programa referenciadas en la instrucción de lectura SELECT del SQL.
Estas variables de programación se encargan de recoger la información devuelta por la instrucción SELECT, y su
ubicación es dentro de la cláusula «INTO» de dicha instrucción. Por lo tanto, la sintaxis de la instrucción SELECT
embebida en CTL quedaría de la siguiente manera:
Formato 1:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
[ [INTO lista_variables | nombre_frame.*] |
[ BY NAME [ON nombre_frame]]]
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Formato 2:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
[[INTO lista_variables | nombre_frame.*] |
[BY NAME [ON nombre_frame]]]
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT …
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Esta cláusula «INTO», como puede comprobarse en las instrucciones anteriores, tiene que estar situada obliga-
toriamente antes de la cláusula «FROM» de la instrucción SELECT. Dentro de esta cláusula se pueden especifi-
car cualquiera de los tipos de variables que se pueden definir en CTL (para más información consulte el capítulo
9 del Manual del Programador).
El número de variables receptoras dependerá del número de columnas que contenga la tabla derivada devuelta
por la instrucción SELECT. Por ejemplo:
select provincia, descripcion
into prov1, descri
from provincias
where descripcion = "MADRID"
Las variables denominadas «prov1» y «descri», que se habrán definido en un programa CTL, se cargarán res-
pectivamente con los valores «28» y «MADRID». Esta instrucción tiene que incluirse obligatoriamente en un
programa CTL. En caso de que la tabla derivada devuelta por esta instrucción tuviese más de una fila se tendr-
ían que recoger los datos mediante el empleo de otro elemento CTL, denominado «cursor», el cual se explica
más adelante dentro de este mismo capítulo.
Asimismo, en el caso de que las variables receptoras se denominen de la misma forma que las columnas que
componen la tabla derivada, puede utilizarse la cláusula «BY NAME» en lugar de indicar los nombres de todas
las variables.
Variables «hosts»
Una variable «host» es aquella que se define en un programa CTL y que está referenciada en una instrucción
del SQL. Mediante el empleo de estas variables el SQL recibe parámetros enviados por el programa CTL. Cuan-
do se desea introducir en una de estas instrucciones de SQL el valor de una variable de programa, bastará con
introducir en dichas instrucciones el nombre de la variable precedida por el signo dólar («$»). Por ejemplo:
select cliente, empresa
into codigo, empre
from clientes
where cliente = $cli
Esta instrucción SELECT tiene que incluirse dentro de un programa CTL. En dicho programa tendrán que definir-
se obligatoriamente tres variables: «codigo», «empre» y «cli». En las dos primeras variables («variables recep-
toras») se recogerá la información devuelta por la lectura en la base de datos, mientras que la tercera («varia-
ble host») será la que se encargue de enviar el valor al SQL para detectar la lectura sobre la tabla de «clientes».
Si se especifican variables «hosts» con el mismo nombre que las columnas que se condicionan en la cláusula
«WHERE» de la instrucción SELECT, puede utilizarse la cláusula «USING». Esta cláusula tiene la siguiente sin-
taxis:
Formato 1:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
[[INTO lista_variables | nombre_frame.*] |
[BY NAME [ON nombre_frame]]]
FROM lista_tablas
[USING lista_variables [ON tabla]]
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Pág. 155
MultiBase. Manual del SQL
Formato 2:
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
[[INTO lista_variables | nombre_frame.*] |
[BY NAME [ON nombre_frame]]]
FROM lista_tablas
[USING lista_variables [ON tabla]]
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[UNION [ALL]]
SELECT …
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
[INTO TEMP tabla_temporal]
Por ejemplo:
select cliente, empresa
into codigo, empre
from clientes
using cliente
Este ejemplo es idéntico al anterior, pero con la diferencia que la variable «host» en este caso se denomina
«cliente» en lugar de «cli». Esta columna («cliente» de la tabla «clientes») condiciona con aquella variable de
programación que se llame igual («cliente»). Es decir, la cláusula «USING» equivaldría a introducir la siguiente
instrucción:
select cliente, empresa
into codigo, empre
from clientes
where cliente = $cliente
NOTA: Una variable de tipo «SERIAL» no puede ser una variable «host».
Cursores
Un cursor es una tabla virtual asociada al programa que lo define, cuyo contenido será el resultado de la ins-
truccion SELECT utilizada en su definición.
Cuando en un programa de CTL se emplea una instrucción SELECT para realizar una consulta («query»), pueden
ocurrir dos cosas: que el resultado (es decir, la «tabla derivada») incluya una única fila de la base de datos, o
bien que incluya varias filas. En este último caso es donde se hace necesaria la utilización de un CURSOR, en el
que CTL incluirá todas las filas resultado de la instrucción SELECT. Evidentemente, el CTL dispone de instruccio-
nes para poder moverse a través de las distintas filas del CURSOR. Para más información sobre este objeto con-
sulte el capítulo 10 del Manual de Programador.
FORM
El FORM es un objeto no procedural que permite manipular los datos de las tablas de la base de datos a través
de una serie de variables, con una cierta correspondencia con columnas de la base de datos y de operaciones
que dependen de esas variables. Además, pueden utilizarse variables auxiliares que no tengan correspondencia
con columnas de las tablas de la base de datos. Las funciones básicas de un FORM son las siguientes:
Instrucción UNLOAD:
UNLOAD TO "fichero_ascii"
SELECT [ALL | DISTINCT | UNIQUE ] lista_select
FROM lista_tablas
[USING lista_variables [ON tabla]]
[WHERE {condición_comparación | condición_subselect}]
[GROUP BY lista_grupos]
[HAVING condición_grupos]
[ORDER BY columna [ASC | DESC] [,columna [ASC | DESC] [, …]]
El formato que debe tener un fichero para la instrucción LOAD es la representación ASCII (en texto) de los valo-
res a cargar, seguido cada uno de ellos de un carácter delimitador, de tal forma que por cada línea haya tantos
caracteres de delimitación como columnas a cargar. El delimitador que utiliza por defecto es el carácter ASCII
124, que equivale a la barra vertical partida («|»). No obstante, este carácter se puede definir mediante la va-
riable de entorno DBDELIM (para más información consulte el capítulo sobre «Variables de Entorno» en el Ma-
nual del Administrador). Si el separador se encuentra como carácter en uno de los valores de una columna,
aquél debe ir precedido de un «backslash» («\») al objeto de que no sea considerado como separador sino co-
mo un carácter más. A su vez, cada registro viene separado por un carácter de nueva línea. Este mismo formato
es el que genera la instrucción UNLOAD. Su aspecto es el siguiente:
Un|Unidades|
m|metros|
Kg|Kilogramos|
Cj|Cajas|
l|litros|
cc|cm cubicos|
En este ejemplo, el fichero ASCII sólo contiene dos campos que podrán cargarse en dos columnas de tipo carác-
ter («CHAR») de una tabla.
Respecto a la instrucción LOAD conviene advertir que su ejecución se abortará cuando se produzca, entre
otros, alguno de los siguientes supuestos:
• Cuando se actualice la variable interna de CTL «errno» (por ejemplo, si el fichero ASCII no existe).
• Cuando un registro (línea) no tenga el mismo número de campos que de columnas expresado en
«lista_columnas» en la instrucción INSERT.
Pág. 157
MultiBase. Manual del SQL
Si se produce algún error (por ejemplo como los anteriormente expuestos), se podrá deshacer la operación de
carga mediante un programa CTL. Por ejemplo:
database almacen
main begin
lock table tabla in exclusive mode
begin work
load from "fichero.unl" insert into tabla
if errno <> 0 then rollback work
else commit work
unlock table tabla
end main
Por lo que respecta a la instrucción UNLOAD, no es preciso que exista el fichero sobre el que se va a realizar la
descarga. En el caso de que exista lo sobreescribirá. Las únicas posibilidades de error en esta instrucción son la
definición incorrecta de la instrucción SELECT y la falta de permisos de creación del fichero a nivel de sistema
operativo UNIX/LINUX o de red. Por ejemplo:
database almacen;
unload to "fichero.unl" select cliente, empresa
from clientes
where clientes.provincia in (select provincia from provincias
where provincias.descripcion matches "M*");
El ejemplo anterior genera un fichero, de nombre «fichero.unl», con las filas que cumplan las condiciones espe-
cificadas en la instrucción SELECT sobre la tabla de «clientes». El aspecto del fichero generado es el siguiente:
4|P.P.B.|
5|ARCO|
6|FCP S.A.|
8|DRAPOSA|
10|DEL OLMO|
11|CIFERSA|
14|ZENITH|
23|DETECSA|
25|CIFERSA|
27|DSE|
31|AOL|
32|DSE|
34|ALBISA|
35|ATC|
37|GRIFFIN S. A.|
44|FCP S.A.|
48|HARSHAW|
52|HIDROPONIA|
53|MOTOROLA|
54|LLORENS|
58|FITNESS|
59|INFERSA|
60|MACARRON S.A.|
61|GYM|
67|PALCOSA|
73|TACIME|
76|PEREIRA|
77|LEM|
78|FEAGAS|
81|TACIME|
82|RODIO|
84|ARCONES|
85|ADA|
89|MASIDER|
90|SCOTCHMAN|
93|ARCO|
94|SPOT IBERICA|
98|CASAZUL|
El siguiente ejemplo cargará las filas descargadas previamente por la instrucción UNLOAD. Este ejemplo produ-
cirá un error si no se cumplen las condiciones básicas expuestas anteriormente para la carga de datos:
load from "fichero.unl" insert into clientes (cliente,empresa)
NOTAS:
1.— Para más información sobre las instrucciones LOAD y UNLOAD consulte el Manual de Referencia.
2.— La ejecución de estas instrucciones se puede simular mediante el manejo de STREAMS en un programa
CTL.
Instrucción PREPARE
Esta instrucción se encarga de «preparar» o examinar la sintaxis de una instrucción de tipo SQL embebida en
un programa de tipo CTL. Dicha instrucción SQL se puede construir mediante la utilización de literales y expre-
siones. Posteriormente, una instrucción SQL definida en una PREPARE se podría ejecutar mediante la instruc-
ción EXECUTE (se comenta en el siguiente epígrafe de este mismo capítulo), o bien podría declararse un CUR-
SOR. La sintaxis de la instrucción PREPARE es la siguiente:
PREPARE nombre_prepare FROM expresión
Donde:
nombre_prepare Identificador de la instrucción de CTSQL. Este identificador será posteriormente
utilizado por las instrucciones EXECUTE o DECLARE (a la hora de declarar un
Pág. 159
MultiBase. Manual del SQL
CURSOR si la instrucción preparada es una SELECT). Este nombre debe ser uní-
voco en un mismo módulo CTL.
expresión Expresión que devuelve una cadena de caracteres que representa la instrucción
a preparar. El resultado de esta cadena debe ser una instrucción válida de
CTSQL, que puede incluir signos de interrogación «?» en los lugares en que se
vaya a pasar una variable «host».
Si la instrucción preparada es una SELECT, ésta no podrá incluir la cláusula de recepción de valores («INTO» o
«BY NAME»). La única forma de recoger valores de una instrucción SELECT preparada sería mediante la decla-
ración de un CURSOR e incluyendo en su lectura ( instrucciones «FOREACH» o «FETCH») la cláusula «INTO».
NOTA: Para más información acerca de esta instrucción consulte el Manual de Referencia.
Instrucción EXECUTE
Esta instrucción es la encargada de ejecutar otra previamente «preparada» mediante la instrucción PREPARE.
Su sintaxis es la siguiente:
EXECUTE nombre_prepare [USING lista_variables]
Donde:
nombre_prepare Identificador de la instrucción de CTSQL. Este identificador coincide con el
nombre asignado a la instrucción SQL «preparada» previamente en la instruc-
ción PREPARE.
lista_variables Nombres de variables del programa, separados por comas, que sustituirán a las
interrogaciones («?») especificadas en la definición de la instrucción SQL prepa-
rada previamente mediante PREPARE.
Si la instrucción «preparada» es una SELECT, después de ejecutarla mediante EXECUTE es posible detectar si
dicha instrucción produce como salida una tabla derivada con o sin filas. Esta comprobación se realiza con las
variables internas de CTL «found» y «ambiguous».
NOTA: Para más información acerca de esta instrucción consulte el Manual de Referencia.
Instrucción TSQL
Esta instrucción permite la ejecución de una instrucción de tipo SQL previamente almacenada en un fichero o
producida como resultado de una expresión. Su sintaxis es la siguiente:
TSQL [FILE] expresión [[ TO | THROUGH] salida
[APPEND]
[líneas LINES]
[LABEL {literal | número}] [AT línea, columna]] [LOG]
Donde:
expresión Expresión válida cuyo resultado será el nombre del fichero que contiene la(s)
instrucción(es) de SQL en el caso de indicarse la opción «FILE». En caso contra-
rio, dicha «expresión» deberá dar como resultado una sentencia válida de SQL.
TO salida Nombre del fichero que se creará con la ejecución de la(s) instrucción(es) in-
cluidas en «expresión» como fichero o como sentencia SQL.
THROUGH salida Nombre del programa al que se envía el resultado. Esta opción no funciona en
Windows.
main begin
tsql "select cliente, nombre, " &&
" apellidos, empresa from clientes"
end main
Este programa CTL presentará una ventana en la que se mostrará la tabla derivada devuelta por la instrucción
SQL confeccionada a base de literales.
NOTA: Para más información acerca de esta instrucción consulte el Manual de Referencia.
Instrucción WINDOW
Esta instrucción presenta una ventana con el resultado (tabla derivada) de una instrucción SELECT, en el que,
opcionalmente, se podrán recoger uno o varios valores correspondientes a una fila de dicha tabla derivada. Su
sintaxis es la siguiente:
WINDOW FROM {instrucción_select | CURSOR nombre_cursor}
[BY COLUMNS]
líneas LINES [columnas COLUMNS]
[STEP salto] [LABEL {literal | número}]
AT línea, columna
[SET lista_variables [FROM números]]
La instrucción WINDOW tiene otras posibilidades, si bien en este apartado sólo estamos contemplando aque-
llas instrucciones que manejan el SQL de forma dinámica. Por lo tanto, como ya se ha indicado anteriormente
en la instrucción PREPARE, se puede preparar o examinar la sintaxis de una instrucción SQL. Si esta instrucción
SQL es una SELECT, ésta puede declararse como un CURSOR. Por último, un CURSOR puede presentarse en una
ventana definida por esta instrucción WINDOW. Por lo tanto, en esta ventana se presentará la información
devuelta por una instrucción de lectura SELECT, pero compuesta de forma dinámica.
Ejemplo:
database almacen
main begin
prepare inst_1 from "select cliente, nombre, " &&
" apellidos, empresa from clientes"
declare cursor lista_clientes for inst_1
window from cursor lista_clientes
10 lines label "Listado de Clientes"
Pág. 161
MultiBase. Manual del SQL
at 5,1
end main
En este ejemplo se prepara una instrucción SELECT mediante concatenación de literales (también podrían ser
variables de programa). Dicha instrucción «preparada» se declara como un CURSOR y, por último, la tabla deri-
vada generada por éste se presenta en una ventana (WINDOW). Con la ejecución de estas tres instrucciones se
maneja una tabla derivada en pantalla formada dinámicamente por la instrucción PREPARE.
NOTA: Para más información acerca de esta instrucción consulte el Manual de Referencia.