top of page

ARREGLOS SQL

​

El uso de arreglos (o arrays) es tan necesario en ciertas ocasiones que ha hecho que su aparición sea tan inmediata como los primeros lenguajes declarativos, y hasta la actualidad, los lenguajes más modernos no los han descartado.

mov [BX],AX;Assember

array = (int *)malloc (N*sizeof(int));C

type Int_Buffer is array (1..10) of Integer;Adda

int *iarray;iarray = new int [10];C++

DIM vars$(52)Basic

Dim c(3 to 82) as IntegerMicrosoft Visual Basic

int[] numbers = new int[10];C#

Entonces ¿podrían ser los arreglos tan necesarios en el T-SQL de Sql Server? La respuesta es que depende para quien, como suelen ser las respuestas a casi todas las preguntas formulables. Quizás esta necesidad no se manifieste tanto dentro del propio motor de base de datos, ya que siempre es posible acceder a los datos necesarios de alguna u otra forma, pero dentro del mundo de los desarrolladores la necesidad es innegable, como lo veremos con un ejemplo posteriormente. Claro que todo este preámbulo no tendría sentido si el T-SQL de Sql Server tuviese arreglos, pero de hecho no los posee (dejemos de lado por ahora la llegada de SQL Server 2008). ¿Entonces como se puede resolver este problema?, plantearemos entonces el siguiente caso que es, como acostumbramos, un caso del mundo real.

Hace un tiempo desarrollamos una aplicación que empleaba SQL Server 2005 como gestor para el soporte de datos. No tardó en aparecer la siguiente situación, entre nuestras tablas se daba, por las reglas de negocios el caso de que cuando se daba de alta un registro en una tabla del tipo “cabecera” debían asociársele unos cuantos registros del tipo “detalle” conjuntamente. Supongamos el caso de las tablas que se muestran a continuación que representan la estructura del problema abstrayendo complicaciones adicionales.

​

En nuestro caso estábamos empleando C# sobre el Framework 3.5 y ADO.NET para que nuestra capa de datos accediera al motor de base de datos SQL Server 2005, ante el problema analizamos las siguientes opciones:

  1. Emplear procedimientos almacenados (haciendo tantas llamadas como fueran necesarias).

  2. Emplear consultas dinámicas.

  3. Emplear un procedimiento almacenado que resolviera todo de alguna forma.

Recordemos que nuestro problema a resolver era que cuando se creaba un registro cabecera, en conjunto se creaban siempre varios registros detalle.

La primera aproximación fue la más inocente, poseer un procedimiento almacenado que inserta una cabecera y otro que inserta un detalle, llamémoslos ins_cabecera e ins_detalle para darle nombres intuitivos, entonces si había que crear una cabecera con, por ejemplo, 10 detalles. La metodología era llamar a los procedimientos almacenados desde nuestra capa de datos en .NET de la siguiente forma

ins_cabecera “datos cabecera”
ins_detalle “datos detalle 1”
ins_detalle “datos detalle 2”
ins_detalle “datos detalle 3”
ins_detalle “datos detalle 4”
ins_detalle “datos detalle 5”
ins_detalle “datos detalle 6”
ins_detalle “datos detalle 7”
ins_detalle “datos detalle 8”
ins_detalle “datos detalle 9”
ins_detalle “datos detalle 10”

Claro está que funciona perfectamente, pero hay algo que no se ve muy bien y es cada vez que se llama a una inserción en la capa de datos de .NET se produce una larga cantidad de llamadas contra el motor de base de datos, a pesar de ser procedimientos almacenados, claro está que hay una gran pérdida de tiempo. Inmediatamente se pensó que había que evitar hacer tantas llamadas.

​

​

FUNCIONES DE CADENA EN SQL

​

Las funciones de cadena trabajan con campos char y varchar por lo que los literales que escribamos se deben encerrar entre comillas simples. 

Estas funciones que vamos a explicar a continuación pueden manipular cadenas de letras u otros caracteres por lo que las vamos a dividir en dos grupos: 

Funciones que devuelven caracteres 

Este tipo de funciones devuelven un carácter o varios caracteres. 

 

FunciónPropósito

CHR(n)Nos devuelve el carácter cuyo valor en binario es n

CONCAT(cad1, cad2)Nos devuelve cad1 concatenada con cad2

UPPER(cad)Convierte cad a mayúsculas

LOWER(cad)Convierte cad a minúsculas

LPAD(cad1,n[,cad2])Con esta función añadimos caracteres a cad1 por la izquierda hasta una longitud máxima dada por n

INITCAP(cad)Convierte la primera letra de cad a mayúscula

LTRIM(cad [,set])Elimina un conjunto de caracteres a la izquierda de cad, siendo set el conjunto de caracteres a eliminar

RPAD(cad1, n[,cad2])Con esta función añadimos caracteres de la misma forma que con la función LPAD pero esta vez los añadimos a la derecha

RTRIM(cad[,set])Hace lo mismo que LTRIM pero por la derecha

REPLACE(cad,cadena_buscada [,cadena_sustitucion] )Sustituye un conjunto de caracteres de 0 o más caracteres, devuelve cad con cada ocurrencia de cadena_buscada sustituida por cadena_sustitucion

SUBSTR(cad, m[,n])Devuelve la subcadena de cad que abarca desde m hasta el numero de caracteres dados por n.

TRANSLATE(cad1,cad2,cad3)Convierte caracteres de una cadena en caracteres diferentes. Devuelve cad1 con los caracteres encontrados en cad2 y sustituidos por los caracteres de cad3


Ponemos algunos ejemplos de utilización de estas funciones: 

Sentencia sql que nos devuelve las letras cuyo valor asccii es el 45 y el 23 

select CHR(45), CHR(23) FROM TABLA;

Sentencia sql que obtiene el nombre de los alumnos sacando por pantalla la siguiente frase: el nombre del alumno es (nombre que esta almacenado en la tabla) 

select CONCAT ('el nombre de alumno es', nombre) from alumno;

Sentencia sql que me devuelve los nombres de los alumnos en mayúsculas 

select UPPER(nombre) from alumno;

Sentencia sql que obtiene de un campo nombre, las 3 primeras letras 

select SUBSTR(nombre,0,3) from alumno;

Y asi con el resto de funcionesÂ… 

Funciones que devuelven valores numéricos 

Estas funciones nos devuelven números a modo de información. 

 

FunciónPropósito

ASCII(cad)Devuelve el valor ASCII de la primera letra de cad

INSTR(cad1, cad2[,comienzo[,m]])Función que busca un conjunto de caracteres dentro de una cadena. Nos devuelve la posición de cad2 en cad1 empezando a buscar en comienzo

LENGTH(cad)Devuelve en número de caracteres de cad



Como con las funciones anteriores dejamos unos ejemplos para que veáis su funcionamiento. 

Sentencia sql que nos devuelve el valor ASCII de la letra ('s') 

select ASCII('s') from tabla;

Sentencia que nos devuelve la posición de la ocurrencia 'pe' dentro de la cadena 'Los perros están bien' a partir de la posición 2 

select INSTR('Los perros están bien','pe',2) from tabla;

Sentencia sql que nos devuelve el numero de caracteres de los nombres de los alumnos 

select LENGTH(nombre) from alumnos;

​

FUNCIONES PARA EL USO DE CADENAS

Microsoft SQL Server tiene algunas funciones para trabajar con cadenas de caracteres. Estas son algunas:

- substring(cadena,inicio,longitud): devuelve una parte de la cadena especificada como primer argumento, empezando desde la posición especificada por el segundo argumento y de tantos caracteres de longitud como indica el tercer argumento. Ejemplo:

select substring('Buenas tardes',8,6);

retorna "tardes".

- str(numero,longitud,cantidaddecimales): convierte números a caracteres; el primer parámetro indica el valor numérico a convertir, el segundo la longitud del resultado (debe ser mayor o igual a la parte entera del número más el signo si lo tuviese) y el tercero, la cantidad de decimales. El segundo y tercer argumento son opcionales y deben ser positivos. String significa cadena en inglés.

Ejemplo: se convierte el valor numérico "123.456" a cadena, especificando 7 de longitud y 3 decimales:

select str(123.456,7,3); select str(-123.456,7,3);

retorna '-123.46';

Si no se colocan el segundo y tercer argumeno, la longitud predeterminada es 10 y la cantidad de decimales 0 y se redondea a entero. Ejemplo: se convierte el valor numérico "123.456" a cadena:

select str(123.456);

retorna '123';

select str(123.456,3);

retorna '123';

Si el segundo parámetro es menor a la parte entera del número, devuelve asteriscos (*). Ejemplo: select str(123.456,2,3);

retorna "**".

- stuff(cadena1,inicio,cantidad,cadena2): inserta la cadena enviada como cuarto argumento, en la posición indicada en el segundo argumento, reemplazando la cantidad de caracteres indicada por el tercer argumento en la cadena que es primer parámetro. Stuff significa rellenar en inglés. Ejemplo:

select stuff('abcde',3,2,'opqrs');

retorna "abopqrse". Es decir, coloca en la posición 2 la cadena "opqrs" y reemplaza 2 caracteres de la primer cadena.

Los argumentos numéricos deben ser positivos y menor o igual a la longitud de la primera cadena, caso contrario, retorna "null".

Si el tercer argumento es mayor que la primera cadena, se elimina hasta el primer carácter.

- len(cadena): retorna la longitud de la cadena enviada como argumento. "len" viene de length, que significa longitud en inglés. Ejemplo:

select len('Hola');

devuelve 4.

- char(x): retorna un caracter en código ASCII del entero enviado como argumento. Ejemplo:

select char(65);

retorna "A".

- left(cadena,longitud): retorna la cantidad (longitud) de caracteres de la cadena comenzando desde la izquierda, primer caracter. Ejemplo:

select left('buenos dias',8);

retorna "buenos d".

- right(cadena,longitud): retorna la cantidad (longitud) de caracteres de la cadena comenzando desde la derecha, último caracter. Ejemplo:

select right('buenos dias',8);

retorna "nos dias".

-lower(cadena): retornan la cadena con todos los caracteres en minúsculas. lower significa reducir en inglés. Ejemplo:

select lower('HOLA ESTUDIAnte');

retorna "hola estudiante".

-upper(cadena): retornan la cadena con todos los caracteres en mayúsculas. Ejemplo:

select upper('HOLA ESTUDIAnte');

-ltrim(cadena): retorna la cadena con los espacios de la izquierda eliminados. Trim significa recortar. Ejemplo:

select ltrim(' Hola ');

retorna "Hola ".

- rtrim(cadena): retorna la cadena con los espacios de la derecha eliminados. Ejemplo:

select rtrim(' Hola ');

retorna " Hola".

- replace(cadena,cadenareemplazo,cadenareemplazar): retorna la cadena con todas las ocurrencias de la subcadena reemplazo por la subcadena a reemplazar. Ejemplo:

select replace('xxx.sqlserverya.com','x','w');

retorna "www.sqlserverya.com'.

- reverse(cadena): devuelve la cadena invirtiendo el order de los caracteres. Ejemplo:

select reverse('Hola');

retorna "aloH".

- patindex(patron,cadena): devuelve la posición de comienzo (de la primera ocurrencia) del patrón especificado en la cadena enviada como segundo argumento. Si no la encuentra retorna 0. Ejemplos:

select patindex('%Luis%', 'Jorge Luis Borges');

retorna 7.

select patindex('%or%', 'Jorge Luis Borges');

retorna 2.

select patindex('%ar%', 'Jorge Luis Borges');

retorna 0.

- charindex(subcadena,cadena,inicio): devuelve la posición donde comienza la subcadena en la cadena, comenzando la búsqueda desde la posición indicada por "inicio". Si el tercer argumento no se coloca, la búsqueda se inicia desde 0. Si no la encuentra, retorna 0. Ejemplos:

select charindex('or','Jorge Luis Borges',5);

retorna 13.

select charindex('or','Jorge Luis Borges');

retorna 2.

select charindex('or','Jorge Luis Borges',14);

retorna 0.

select charindex('or', 'Jorge Luis Borges');

retorna 0.

- replicate(cadena,cantidad): repite una cadena la cantidad de veces especificada. Ejemplo:

select replicate ('Hola',3);

retorna "HolaHolaHola";

- space(cantidad): retorna una cadena de espacios de longitud indicada por "cantidad", que debe ser un valor positivo. Ejemplo:

select 'Hola'+space(1)+'que tal';

retorna "Hola que tal".

Se pueden emplear estas funciones enviando como argumento el nombre de un campo de tipo caracter.

​

​

PASO DE ARREGLOS A FUNCIONES

​

Supongamos que queremos pasar un array a una función. La solución es pasar punteros. El nombre de un array es un puntero al primer elemento del array. Así, si hemos definido int taco[10] como un array de diez enteros, taco será un puntero al array. Por ejemplo, sea guacamole un array de 6 enteros: 6, 63, 112, 18, -52, 16. guacamole es simplemente un puntero al primer elemento. Entonces, *guacamole equivale al valor 6. Si tratamos de acceder al elemento guacamole[6], estamos accediendo a lo que haya después del último elemento del array. Los resultados son inesperados.
Veamos un ejemplo de cómo pasar un array a una función:
# include <iostream.h>

void eat_at_joes(int guacamole[])
{
  guacamole[0] = 1;
  guacamole[1] = 2;
  guacamole[2] = 3;
}

main()
{
  int taco[3];
  int nacho[2];

  eat_at_joes(taco);
  eat_at_joes(nacho);
}

Al definir la función, no hemos puesto ningún número entre los corchetes. Esto significa que estamos permitiendo que un array de cualquier tamaño pueda ser pasado a la función. En la función main, hemos declarado dos arrays de enteros, taco y nacho. Primero pasamos taco a la función, que en realidad es la direccion del array taco. Ahora bien, el segundo array, nacho, tiene sólo dos elementos. Cuando la función trate de acceder al tercer elemento del array, que realmente no existe. Ya que los arrays son muchas variables almacenadas una detrás de otra en la memoria del ordenador, tratará y accederá a la variable almacenada despueés del segundo elemento del array, que no existe.
Además, el programa compilará y correrá sin mensajes de error. Esto sucedía ya en C, que permite leer y escribir en la memoria del ordenador sin saber si realmente hay variables allí. Se deben evitar casos como el del ejemplo, porque, o bien el programa correrá sin mensajes de error, o bien casca.
Una forma de evitarlo es añadiendo un segundo parámetro que indique el tamaño del array:
# include <iostream.h>

void eat_at_joes(int guacamole[], int size)
{
  if (size > 0)
    guacamole[0] = 1;
  if (size > 1)
    guacamole[1] = 2;
  if (size > 2)
    guacamole[2] = 3;
}

main()
{
  int taco[3];
  int nacho[2];

  eat_at_joes(taco,3);
  eat_at_joes(nacho,2);
}

2
bottom of page