En modo transaccional la instrucción LOAD irá bloqueando incrementalmente cada uno de los registros que inserta en la tabla. Por tanto, si el número de filas a insertar es muy elevado, se puede saturar la tabla de bloqueos del sistema operativo. Para evitar esta situación y además acelerar el proceso es recomendable bloquear la tabla durante el proceso de LOAD.
Ejemplo:
...
begin work
lock table mitabla in share mode
load from "mitabla.unl" insert into mitabla
unlock table mitabla
commit work
...
Al incluir un carácter de 8 bits en una consulta de un FORM o en cualquier instrucción SQL se puede producir el siguiente error: «Carácter ilegal encontrado en la sentencia». Este error se debe que el carácter indicado no está definido como carácter alfabético en la tabla de caracteres de MultiBase.
Esta tabla se puede modificar incluyendo una entrada para ese carácter en la sección «UPPER/LOWER» del fichero «TMAPPING», que se encuentra en el subdirectorio «etc» de MultiBase. Por ejemplo:
select * from provincias
where descripcion > "ë"
Para que esta instrucción funcione correctamente debe incluirse la siguiente línea en el fichero «TMAPPING» para el terminal en concreto:
:AT386
.UPPER/LOWER
\211 \211 e con diéresis
Este ejemplo es válido para un terminal que utilice el juego de caracteres «GCS#2» de IBM.
La forma más rápida de actualizar una fila devuelta por un CURSOR se realiza por medio de la instrucción «UPDATE... WHERE CURRENT OF...».
Para ello, deberá definirse el CURSOR como «CURSOR de actualización» («FOR UPDATE»). Dado que un CURSOR definido «FOR UPDATE» debe estar asociado a sentencias SELECT simples, es decir, no pueden incluir ni más de una tabla, ni agregados, ni «ORDER BY», ni «GROUP BY», etc., para actualizar a través de un CURSOR con algún tipo de ordenación no puede utilizarse, por tanto, la sentencia «UPDATE... WHERE CURRENT OF...».
En estos casos se puede emplear la siguiente estrategia: Añadir a la sentencia SELECT de la definición del CURSOR la columna ROWID y utilizar en la sentencia UPDATE como única condición «WHERE ROWID=...».
Ejemplo:
database almacen
define
variable numero integer
variable pr_vent like articulos.pr_vent
variable articulo like articulos.articulo
end define
main begin
declare cursor actualiza for select rowid,
articulo, pr_vent
into numero, articulo, pr_vent
from articulos
order by articulo
foreach actualiza begin
update articulos set pr_vent = pr_vent * 1,05
where rowid = $numero
end
end main
La ejecución del programa anterior será más rápida que si se incluyesen las condiciones de búsqueda de la fila a actualizar en la instrucción UPDATE.
La definición de una variable no puede realizarse con la cláusula «AT». El compilador no produce ningún error, pero en ejecución dará un «Error no recuperable en ejecución».
La definición de una variable multilínea en un FRAME (cláusula «n LINES») deja sin efecto el resto de atributos de la variable.
Para cambiar de tabla en un FORM con relación cabeceras-líneas (sección JOINS) hay que utilizar las instrucciones HEAD y LINES en lugar de NEXT TABLE. Con esta última puede haber problemas de «s roll» de las filas que intervienen en las listas en curso de las respectivas tablas.
En la impresión de una variable con «PUT STREAM» cuyo valor sea «NULL» se escriben tantos espacios en blanco como longitud tenga la variable. Si no se desea este comportamiento, se puede optar por hacer concatenaciones dependiendo del resultado que se desee.
Ejemplo:
variable var1 char (10)
| INSTRUCCIÓN | RESULTADO var="1234" |
RESULTADO (var=NULL) |
| put stream standard "[", var1, "]" | [1234 ] | [ ] |
| put stream standard var1 & "" | [1234] | [ ] |
| put stream standard var1 && null | [1234 ] | [ ] |
En instalaciones con arquitectura cliente-servidor, desde una máquina «cliente» (MS-DOS, Windows o UNIX) no se pueden «chequear» las tablas de la base de datos residente en el «servidor». El error que se produce es: «Imposible abrir el fichero «......»».
La instrucción WINDOW convierte todos los datos que muestra en CHAR. Por lo tanto, cuando se muestra un dato numérico formateado (atributo FORMAT), para seleccionar algún valor de esta ventana la variable receptora deberá ser de tipo CHAR. Si la variable receptora es numérica, CTL intentará cambiar las comas decimales por punto decimal, con lo cual una columna de la WINDOW que tenga un formato con puntos de miles no se podrá convertir a numérico sin necesidad de escribir una función «ad hoc».
El formato interno de almacenamiento de decimales negativos es diferente entre las versiones 1.0 y 2.0 . La variable de entorno DECNEG puede tomar como valor 1 ó 2 para forzar uno u otro formato. Esta variable apareció por la necesidad de mantener bases de datos construidas con un SQL de versiones 1.0. Así, un SQL de una versión 2.0 puede mantener columnas con valores decimales negativos de versiones 1.0 definiendo simplemente «DECNEG=1». El valor por defecto de esta variable en todas las versiones 2.0 es «DECNEG=2» para UNIX y «DECNEG=1» para MS-DOS y Windows. A partir de la release 5 esta variable se comporta igual en todos los sistemas operativos, es decir, por defecto «DECNEG=2».
Por lo tanto, si se va a instalar una versión 2.0.05 sobre bases de datos MS-DOS o Windows de versiones anteriores que contengan alguna columna de tipo DECIMAL con valores negativos, deberá, o bien poner la variable «DECNEG=1», o bien descargar la tabla con el valor «DECNEG=1», borrarla, poner «DECNEG=2», crearla y cargarla de nuevo.
La variable de compatibilidad general «MBCOMPAT=1» asume también «DECNEG=1».
En esta release se ha modificado el optimizador de «queries» del CTSQL. A continuación se proporciona información sobre la forma en que actúa.
El optimizador de «queries» de CTSQL decide una estrategia de búsqueda sobre las tablas incluidas en las sentencias en base a los siguientes factores:
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 condiciones por OWID, o bien, en el peor de los casos, determinará si es necesario acceder secuencialmente 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 continuará hasta terminar con todas las tablas de la sentencia.
Más adelante se explica en detalle la forma en la que actúa el optimizador bajo diferentes condiciones.
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 estrategia 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.
Los elementos que intervienen en la decisión de si un índice es más adecuado que otro son los siguientes:
Número de partes del índice referenciadas en la «WHERE» mediante condiciones de «=», «>=», «>», o bien las condiciones «MATCHES», «LIKE», «IN», «BETWEEN» que provoquen condiciones equivalentes a las anteriores.
En un índice compuesto la referencia a una de sus partes sólo se tendrá en cuenta si además se referencian todas las componentes anteriores a ésta en otras condiciones de la «WHERE».
Un índice referenciado en todas sus partes por las condiciones anteriormente citadas es mejor que un índice del que no se referencien todas las componentes.
Un índice referenciado en todas sus partes por condiciones de igualdad es mejor que otro referenciado en todas sus partes por cualquier otra condición.
Un índice referenciado en todas sus partes por condiciones de igualdad es mejor que otro en su mismo caso si el primero es índice único y el segundo no.
A igualdad de condiciones de elegibilidad entre dos índices, el optimizador seleccionará en primer lugar el primero al que se haga referencia (que en la cláusula «WHERE» aparezca uno de sus componentes con anterioridad); en el caso de igualdad (la primera referencia se hace mediante una columna que está en ambos índices) se elegirá el que sea clave primaria (si lo fuese) o el que se haya creado antes.
Una condición es optimizable si es única o está unida
al resto de condiciones de esta tabla con un nexo «AND».
Por ejemplo, la sentencia «SELECT * FROM clientes WHERE cliente > 20
OR provincia > 10» no es optimizable, y por tanto será necesario
acceder secuencialmente a toda la tabla para obtener el resultado.
Por el contrario, la sentencia «SELECT * FROM clientes WHERE
cliente > 20 AND provincia > 10» sí es optimizable,
y se accederá por «cliente» si hay un índice
en la columna «cliente», o bien si hay un índice
compuesto cuya primera componente sea la columna «cliente»;
y se accederá por «provincia» si ocurre esto mismo
para «provincia». Si están definidos los dos índices
se accederá por el primero referenciado en la «WHERE» (en
este ejemplo «cliente»).
Una forma de forzar al acceso por «provincia» mediante
su índice, si sabemos que también existe un índice
por «cliente», puede ser haciendo que la primera condición
no sea optimizable, por ejemplo:
select * from clientes where (cliente > 20 or
1=0)
and provincia > 10
o bien cambiando el orden en la «WHERE»:
select * from clientes where provincia > 10
and cliente > 20
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.
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 la siguientes condiciones (todas):
La SELECT se realiza sobre una única tabla.
Existe un índice que coincide con las columnas de ordenación.
No se ha podido determinar ningún índice de acceso a la tabla o bien el índice seleccionado coincide con el del «ORDER BY».
Si se dan todas estas condiciones, CTSQL no necesitará realizar una ordenación posterior a la selección.
Siempre es mejor acceder a una tabla antes que a otra si el número de filas válidas de la primera es menor que el de la segunda. De este modo, el número de accesos a disco, en principio, es inferior. Por ejemplo, supongamos dos tablas, «a» y «b», con las filas que se muestran a continuación:
| 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:
Tabla «a», Tabla «b»: Las lecturas necesarias serán a1, b1, b2, a2, b1, b2, _a5, b1, b2. En total 15 lecturas.
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 restrictivas. Es decir, una tabla que pueda ser accedida mediante un índice o ROWID es mejor q e otra que no pueda serlo. Si existen dos tablas que no pueden ser accedidas por índice, se elegirá la que tenga menor número de filas. CTSQL obtendrá este número del campo «nrows» de la tabla «syst bles» del catálogo del sistema. Este campo solamente se actualiza por medio de la instrucción UPDATE STATISTICS del SQL. Por tanto, conviene tener en cuenta que para algunas sentencias donde intervenga un gran número de tablas con grandes diferencias 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».
En el Apéndice 3 de estas notas se incluyen ejemplos de cómo actúa el optimizador con diferentes sentencias SELECT.