Está en la página 1de 101

ÍNDICE DEL CURSO SQL

Índice de contenido Capítulo 1


CAPÍTULO 1.......................................................................................................................................1
INTRODUCCIÓN................................................................................................................................2
¿POR QUÉ ESTE CURSO?............................................................................................................2
CONFESIONES ÍNTIMAS.............................................................................................................2
Y EN ESTE PRIMER CAPÍTULO.................................................................................................3
EL LENGUAJE SQL...........................................................................................................................3
RÁPIDAMENTE... ¿QUÉ ES?.......................................................................................................3
TIPOS DE DATOS..........................................................................................................................4
DATOS TIPO TEXTO................................................................................................................4
DATOS TIPO MEMO.................................................................................................................5
DATOS TIPO NÚMERO............................................................................................................6
DATOS TIPO FECHA/HORA....................................................................................................7
DATOS TIPO MONEDA............................................................................................................8
DATOS TIPO AUTONUMÉRICO.............................................................................................8
DATOS SÍ/NO (DATOS BOOLEANOS)...................................................................................8
DATOS OBJETO OLE...............................................................................................................8
DATOS BINARIOS....................................................................................................................9
CUADRO RESUMEN................................................................................................................9
ALGUNOS COMPONENTES DE SQL.......................................................................................10
COMANDOS............................................................................................................................10
CLÁUSULAS...........................................................................................................................11
PREDICADOS..........................................................................................................................11
OPERACIONES.......................................................................................................................11
OPERADORES........................................................................................................................12
FUNCIONES AGREGADAS...................................................................................................12
PREPARACIÓN Y EXPLICACIONES VARIAS PARA SEGUIR EL RESTO DEL CURSO.........13
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................18

Índice de contenido Capítulo 2


INTRODUCCIÓN................................................................................................................................2
MANOS A LA OBRA.....................................................................................................................2
PRIMERA INCURSIÓN: CONSULTAS DE SELECCIÓN...............................................................2
UN INCISO SOBRE LA LLAMADA A LOS CAMPOS...............................................................2
SELECCIÓN DE TODOS LOS CAMPOS DE UNA TABLA.......................................................3
SELECCIÓN DE CAMPOS DE VARIAS TABLAS NO RELACIONADAS...............................3
ORDENANDO NUESTROS RESULTADOS................................................................................3
PRACTIQUEMOS CON UN EJEMPLO............................................................................................4
SQL SIMPLE..............................................................................................................................4
SQL “COMPUESTA”.................................................................................................................5

1
Visítame en http://siliconproject.com.ar/neckkito/
SQL ORDENADA......................................................................................................................5
PREDICADOS.....................................................................................................................................7
TOP..................................................................................................................................................7
DISTINCT.......................................................................................................................................8
OTRA MANERA DE CONSEGUIR EL MISMO RESULTADO...........................................10
DISTINCTROW............................................................................................................................10
UTILIZACIÓN DE LA CLÁUSULA WHERE.................................................................................11
CARACTERES COMODÍN.........................................................................................................11
OPERADORES.............................................................................................................................12
MARCO SEGÚN EL TIPO DE DATOS.......................................................................................12
CLÁUSULA WHERE, COMODINES Y OPERADORES...........................................................12
EJEMPLOS CON OPERADORES MATEMÁTICOS............................................................13
EJEMPLOS CON OPERADORES DE COMPARACIÓN......................................................14
BETWEEN... AND..............................................................................................................14
IN..........................................................................................................................................14
LIKE.....................................................................................................................................15
EJEMPLOS CON COMPARADORES LÓGICOS..................................................................17
AND.....................................................................................................................................17
OR........................................................................................................................................18
XOR......................................................................................................................................18
IMP / EQV............................................................................................................................19
NOT......................................................................................................................................20
ISNULL / NOT ISNULL.....................................................................................................21
PARA ACABAR ESTE CAPÍTULO.................................................................................................22

Índice de contenido Capítulo 3


COMPLIQUEMOS UN POCO LAS CONSULTAS DE SELECCIÓN..............................................2
VAMOS ALLÁ................................................................................................................................2
CREACIÓN DE “LITERALES”.....................................................................................................2
UTILIZACIÓN DE “ALIAS”.........................................................................................................3
ALIAS DIRECTO COMO CAMPO..........................................................................................3
ALIAS DIRECTO COMO TABLA............................................................................................4
ALIAS INDIRECTO: USO DE FUNCIONES AGREGADAS.................................................5
OPERACIONES MATEMÁTICAS Y OTRAS “POSIBILIDADES”..................................6
FUNCIONES DE VALORES SIMPLES...............................................................................7
ABS().................................................................................................................................8
SQR().................................................................................................................................8
MOD()...............................................................................................................................8
ROUND()..........................................................................................................................8
FUNCIONES DE GRUPOS DE VALORES (FUNCIONES DE AGREGADO)..................8
COUNT()...........................................................................................................................8
SUM()................................................................................................................................9
AVG()................................................................................................................................9
STDEV() / STDEVP().......................................................................................................9
FIRST() / LAST()............................................................................................................10

2
Visítame en http://siliconproject.com.ar/neckkito/
MAX() / MIN()................................................................................................................10
CONSULTAS ANIDADAS, O SUBCONSULTAS...........................................................................10
UTILIZANDO “IN”......................................................................................................................10
UTILIZANDO “ANY”/”SOME”..................................................................................................12
UTILIZANDO “EXISTS”.............................................................................................................12
COMBINACIÓN DE ELEMENTOS EN SQL..................................................................................13
COMBINACIÓN EXTERNA (O LLÁMAME... “OUTER JOINS”)...........................................13
CORRESPONDENCIA CON LAS CONSULTAS-OBJETO EN ACCESS. TIPOS DE
COMBINACIÓN......................................................................................................................15
COMBINACIÓN INTERNA (O LLÁMAME... “INNER JOIN”)...............................................16
CONSULTA “UNION” / “UNION ALL”.....................................................................................17
CONSULTA “TRANSFORM”......................................................................................................19
AUTOCOMBINACIÓN................................................................................................................21
SELF JOIN....................................................................................................................................22
CLÁUSULA “HAVING”...................................................................................................................24
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................25

Índice de contenido Capítulo 4


LAS CONSULTAS DE ACCIÓN........................................................................................................2
TRES Y... ¡ACCIÓN!......................................................................................................................2
CONSULTA DE DATOS ANEXADOS..........................................................................................3
ANEXAR EN UNA TABLA EXISTENTE (un único registro).................................................3
ANEXAR EN UNA TABLA EXISTENTE (todos los registros / conjunto de registros)...........4
ANEXAR HACIA UNA BD EXTERNA...................................................................................5
ANEXAR DESDE UNA BD EXTERNA..................................................................................6
CONSULTA DE ACTUALIZACIÓN.............................................................................................6
CONSULTA DE ELIMINACIÓN...................................................................................................7
CONSULTA PARA BÚSQUEDA DE REGISTROS DUPLICADOS............................................8
CONSULTAS PARAMETRIZADAS..................................................................................................9
PROCEDURES..................................................................................................................................10
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................10

Índice de contenido Capítulo 5


TRABAJAR CON TABLAS................................................................................................................2
DESEO UNA TABLA “A MEDIDA”.............................................................................................2
CREAR UNA TABLA.....................................................................................................................2
LA CLÁUSULA “CONSTRAINT”............................................................................................3
UNA PEQUEÑA ACLARACIÓN (PARA NO TENER QUE “RASCARNOS LA
CABEZA”).............................................................................................................................3
DEFINIR UN CAMPO QUE NO ADMITE DUPLICADOS: UNIQUE..............................3
DEFINIR UN CAMPO COMO CLAVE PRINCIPAL: PRIMARY KEY.............................4
DEFINIR UN CAMPO COMO REQUERIDO: NOT NULL...............................................5
DEFINIR UN CAMPO COMO AUTONUMÉRICO Y DEFINIR SU INTERVALO DE
INCREMENTO......................................................................................................................6
COMBINAR DEFINICIÓN DE PROPIEDADES................................................................8

3
Visítame en http://siliconproject.com.ar/neckkito/
DEFINIR UNA CLAVE EXTERNA: FOREING KEY.........................................................8
BORRAR UNA TABLA................................................................................................................10
CREAR ÍNDICES (con CREATE INDEX)..................................................................................10
ÍNDICES CON REGISTROS DUPLICADOS.........................................................................11
PROHIBIR VALORES NULOS EN EL ÍNDICE...............................................................12
IGNORAR VALORES NULOS EN EL ÍNDICE................................................................13
ÍNDICE CON REGISTROS NO DUPLICADOS....................................................................14
CREAR UN ÍNDICE CON CLAVE PRIMARIA................................................................15
ELIMINAR ÍNDICES...................................................................................................................15
MODIFICAR UNA TABLA..........................................................................................................15
AÑADIR CAMPOS..................................................................................................................16
BORRAR CAMPOS.................................................................................................................16
CREAR ÍNDICES (con ALTER TABLE)................................................................................17
CREAR UNA CLAVE PRINCIPAL....................................................................................17
CREAR UNA CLAVE EXTERNA......................................................................................17
MODIFICAR EL TIPO DE RELACIÓN........................................................................18
AÑADIR UNA REGLA DE VALIDACIÓN............................................................................20
ELIMINAR ÍNDICES..............................................................................................................20
PARA FINALIZAR EL CAPÍTULO (¡Y EL CURSO!)....................................................................21

4
Visítame en http://siliconproject.com.ar/neckkito/
CURSO DE SQL

CAPÍTULO 11

Índice de contenido
CAPÍTULO 1.......................................................................................................................................1
INTRODUCCIÓN................................................................................................................................2
¿POR QUÉ ESTE CURSO?............................................................................................................2
CONFESIONES ÍNTIMAS.............................................................................................................2
Y EN ESTE PRIMER CAPÍTULO.................................................................................................3
EL LENGUAJE SQL...........................................................................................................................3
RÁPIDAMENTE... ¿QUÉ ES?.......................................................................................................3
TIPOS DE DATOS..........................................................................................................................4
DATOS TIPO TEXTO................................................................................................................4
DATOS TIPO MEMO.................................................................................................................5
DATOS TIPO NÚMERO............................................................................................................6
DATOS TIPO FECHA/HORA....................................................................................................7
DATOS TIPO MONEDA............................................................................................................8
DATOS TIPO AUTONUMÉRICO.............................................................................................8
DATOS SÍ/NO (DATOS BOOLEANOS)...................................................................................8
DATOS OBJETO OLE...............................................................................................................8
DATOS BINARIOS....................................................................................................................9
CUADRO RESUMEN................................................................................................................9
ALGUNOS COMPONENTES DE SQL.......................................................................................10
COMANDOS............................................................................................................................10
CLÁUSULAS...........................................................................................................................11
PREDICADOS..........................................................................................................................11
OPERACIONES.......................................................................................................................11
OPERADORES........................................................................................................................12
FUNCIONES AGREGADAS...................................................................................................12
PREPARACIÓN Y EXPLICACIONES VARIAS PARA SEGUIR EL RESTO DEL CURSO.........13
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................18

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://siliconproject.com.ar/neckkito/
INTRODUCCIÓN

¿POR QUÉ ESTE CURSO?

Cuando profundizamos un poco en Access, y sobre todo


cuando profundizamos en VBA para Access, nos
encontramos con una “palabrita” extraña que se escribe
“SQL”. Con toda probabilidad, por poco que hayamos
toqueteado las consultas de Access, nos habremos
encontrado con algo que se llama “vista SQL”.
Ahí nuestra gran consulta se nos habrá convertido en un grupo de palabras, a cual más
extraña, y pensando “no entiendo nada de esto” nos habremos vuelto a la vista diseño de la
consulta y tan frescos.
Por otra parte, si ya hablamos de código, quizás hayamos encontrado algún código donde se
nos dice que “construimos nuestra consulta a través de la variable miSQL”, y también
habremos podido ver que nuestra variable String se confeccionaba con un conjunto de
“palabrejas” que bueno... ni Dios que lo entienda, pero funciona.

Entonces se enciende una bombilla en eso que llaman cerebro y pienso: “¿por qué no hacer un
curso para que los sufridos usuarios de Access puedan entender qué están haciendo?”

Y, como diría Aristóteles, de la potencia al acto: de ahí que ahora los “afortunados” tengáis en
vuestras manos este curso (modestia aparte, claro... je, je...).

Como habréis podido intuir, y quienes me conocéis un poco ya habréis adivinado, la idea de
este curso no es meterse a fondo en teoría sobre SQL; mi intención es darle un carácter
práctico, y el objetivo final es que al ver una sentencia SQL sepáis qué está haciendo, además
de poder construiros vosotros mismos vuestras propias instrucciones para “esos códigos que
tan bien funcionan en nuestras BD's”.

Finalmente lo que se explicará en este curso sobre SQL está enfocado, principalmente, a su
funcionamiento con Access. Cierto es que SQL es un lenguaje estándar, pero bueno, digamos
que cada sistema gestor de bases de datos puede tener sus propias particularidades. Sin
embargo, e insisto en ello, el enfoque que se dé será siempre de cara a su desarrollo para
Access.

Doy por supuesto que el lector tiene un cierto control sobre VBA para Access, dado que los
ejemplos van a estar basados prácticamente en este lenguaje de programación. Aunque
intentaré explicar qué estoy haciendo hay cosas que se darán por sabidas. Si tenemos dudas
siempre podemos consultar este fantástico manual sobre VBA para Access (je, je...)

Espero que este curso os sea útil. Y, si encontráis algún error o “desastre”, además de pedir
disculpas por adelantado, os rogaría que me lo comunicarais para poder “enmendar la pifia”.

¡Suerte!

CONFESIONES ÍNTIMAS
No me considero un “monstruo” de SQL. De hecho, a mis efectos, diría que soy un neófito en
esto de SQL. ¿Por qué? Porque Access nos facilita muchísimo el trabajo de construcción de
SQL's que son las que, generalmente, nos funcionan perfectamente en nuestras BD's.

2
Visítame en http://siliconproject.com.ar/neckkito/
Supongo que por esta “falta de necesidad” no se me ha ocurrido profundizar hasta las entrañas
“más oscuras” de SQL. En fin...

¿Y qué quiero decir con esto? Que quien espere encontrar,


en este manual, “grandes construcciones SQL” con una
“complejidad rayana en la locura”... pues se ha equivocado
de manual.

¿Y qué encontraremos en este manual, entonces? Pues la


finalidad, como os comentaba antes, es simplemente que
“entendamos”, al leer o construir una SQL, qué estamos
haciendo. Y lo que encontraremos serán las construcciones
SQL más comunes (bueno, quizá alguna de no tan común)
y cómo podemos implementarlas en nuestros códigos VBA.
Insisto: para grandes complicaciones... pues a buscar un
buen manual (y caro) sobre SQL.

Y EN ESTE PRIMER CAPÍTULO...


Vamos a dividir este capítulo en dos partes bien diferenciadas: teoría y preparación de datos
para poder desarrollar y practicar en los próximos capítulos.

Como casi todo en la vida no se puede empezar la casa por el techo. Por ello este primer
capítulo será un poco (mucho) teórico. De hecho, pienso que es mejor un sólo capítulo (este)
fundamentalmente teórico que no teoría por aquí, teoría por allá...

Por eso os pido un poco de paciencia, puesto que es imprescindible sentar las bases de
terminología, conceptos y “otros chascarrillos” antes de meternos de lleno en temas prácticos.

Pero tranquilos, que ya sabéis que a mí me gusta más la práctica que la teoría, por lo que
intentaré ser 3B: bueno, bonito y breve.

En cuanto a la preparación de los datos la idea es crearse una BD de ejemplo y que con ella
seamos capaces de desarrollar todas las diferentes explicaciones que se llevarán a cabo en los
siguientes capítulos. Sé que también es un esfuerzo, pero estoy convencido de que el mismo
tendrá una buena recompensa.

Evidentemente se puede seguir el curso sin realizar la parte práctica, pero si en alguna parte
nos perdemos y no hemos seguido los ejemplos probablemente este esfuerzo que hacemos al
principio lo tengamos que realizar tarde o temprano si queremos entender bien qué estamos
haciendo. En definitiva, que es una decisión personal de cada uno.

Finalmente, recordad que todos los contenidos estarán basados en la aplicación de sentencias
SQL dentro de VB para Access. No debemos perder de vista lo anterior.

EL LENGUAJE SQL

RÁPIDAMENTE... ¿QUÉ ES?


Si nos ceñimos a la definición que podemos leer en Wikipedia 2, nos encontramos con que SQL
es:

<<El lenguaje de consulta estructurado o SQL (por sus siglas en inglés structured query language) es
2 [http://es.wikipedia.org/wiki/SQL ]. Consulta en línea el 21/04/12

3
Visítame en http://siliconproject.com.ar/neckkito/
un lenguaje declarativo de acceso a bases de datos relacionales que permite especificar diversos tipos de
operaciones en estas. Una de sus características es el manejo del álgebra y el cálculo relacional permitiendo
efectuar consultas con el fin de recuperar -de una forma sencilla- información de interés de una base de
datos, así como también hacer cambios sobre ella.>>

En resumidas cuentas, SQL es:

– Un lenguaje de programación...
– para bases de datos relacionales... (¡Caramba!
Access es un GBDR3)
– que a través de unos instrumentos...
– nos permite efectuar consultas...
– para extraer información de la base de datos.

Se utiliza SQL porque es un lenguaje estándar de una gran aceptación y que, salvo honrosas
excepciones, nos permite manipular bases de datos independientemente del sistema gestor. Es
decir, que en principio nos da igual si es Access quien gestiona la BD, SQL-Server, Oracle o
phpMyAdmin, entre otros.

Otra de las ventajas de SQL es su relativa sencillez, de manera que con el conocimiento de
unas pocas palabras “claves” podemos efectuar potentes consultas para conseguir la
información necesaria.

Finalmente, nuestro “querido” VBA acepta la implementación de SQL dentro del código, con lo
que si sabemos programar en VBA y SQL podemos obtener una sinergia importante para una
mejor utilización de nuestra BD.

Como ya comentaba en la introducción, a partir de aquí las referencias que se hagan serán
todas referidas a su utilización en Access y en VBA para Access (insisto en que lo anterior no
excluye su uso, en un alto porcentaje, en otros sistemas de gestión). Veremos cómo
complementar estos dos lenguajes de programación para conseguir las sinergias que
comentábamos antes.

TIPOS DE DATOS
Vamos a partir, para este epígrafe, de lo “conocido” para llegar a lo “desconocido”. Y Access
será nuestra “luz y guía” para ello.

Cuando creamos una tabla en Access y


definimos un campo debemos definir también
su tipo de dato. Es decir, que nos
encontramos con una cosa así:

Vamos a ir recorriendo estos tipos de campos


para ver sus características y su relación con
SQL

DATOS TIPO TEXTO


Un dato tipo texto es simplemente una
cadena de caracteres, con una longitud máxima de 255 caracteres.
Hasta aquí muy bien. Pero nuestro campo tipo texto, ¿es de longitud variable o de longitud
fija?

3 GBDR: Gestor de Bases de Datos Relacional

4
Visítame en http://siliconproject.com.ar/neckkito/
Que un dato sea de longitud variable o fija tiene su repercusión en el tamaño de
almacenamiento en bites del dato. Eso quiere decir que:

– Si el texto es de longitud fija el tamaño de almacenamiento


será el doble del tamaño definido

– Si el texto es de longitud variable el tamaño de


almacenamiento será el doble del tamaño real.
Para que nos entendamos: si yo defino un campo con un
tamaño máximo de 25 caracteres de longitud y el valor que
hay dentro de ese campo tiene 15 caracteres mis tamaños
de almacenamiento en bites serán los siguientes:
– Si el texto es fijo: 25*2=50
– Si el texto es variable: 15*2=30
Conclusión: si sabemos de antemano que los valores que vamos a introducir en ese campo van
a tener todos una longitud similar sería mejor utilizar un texto fijo; al contrario, si esa longitud
va a ser dispar utilizaríamos un texto variable.
Más cosas: ¿qué tipo de texto utiliza Access? Access utiliza por defecto, un tamaño variable. ¿Y
cómo podemos saber “visualmente” qué tipo de texto se está utilizando? Pues abriendo la
tabla en vista hoja de datos y mirando qué selección (marcado en negro) se hace del valor del
campo. Si vemos que se selecciona sólo el valor estaremos ante un texto variable; si se
selecciona todo el campo será un texto fijo4.
Es decir:

¿Cómo definimos uno y otro en SQL? Pues utilizando las “palabras reservadas” que tenemos
para ello. Disponemos de varias palabras reservadas en algunos casos (es decir, como si fueran
sinónimos). La siguiente tabla muestra de qué opciones disponemos:

Texto – Longitud fija CHAR


Texto – Longitud variable ALPHANUMERIC / STRING / TEXT / TEXT (N) /
VARCHAR

DATOS TIPO MEMO


Los datos tipo memo no son más que datos de tipo texto, pero sin la limitación de los 255
caracteres. De hecho, un campo memo puede acumular hasta un máximo de 65.536
caracteres. Si el campo no contiene datos binarios nuestro límite aumenta hasta el límite
máximo que se puede alcanzar con un archivo Access, que como bien sabemos está en los 2
GB.
Si el campo es memo ya no hablamos ni de longitud fija ni variable, ya que la única posibilidad
es esta última, es decir, longitud variable. Si seguimos la sistemática del apartado anterior
podremos deducir que el tamaño de almacenamiento es el doble de la longitud real del valor
almacenado en el campo.

4 Como habréis imaginado esta definición de tipo dato no se puede establecer a través de la interface de Access, sino que debemos
hacerlo a través de una SQL. En capítulos posteriores veremos cómo realizar esta operación.

5
Visítame en http://siliconproject.com.ar/neckkito/
Para definir un campo como memo utilizamos:

Texto - Memo LONGTEXT / LONGCHAR / MEMO / NOTE

DATOS TIPO NÚMERO


Y aquí empieza el festival, porque existen diferentes
tipologías de números. La primera gran división que
podemos hacer de los números es aquella que diferencia
entre valores sin decimales (valores enteros) y valores con
decimales (valores decimales).
Una vez establecida esta primera división podemos dividir ambos tipos en función del intervalo
de datos admitido.
Veamos:
VALORES ENTEROS
– Tenemos un tipo de campo que admite valores positivos del 0 al 255. En VB serían los
datos tipo BYTE
– Tenemos un tipo de campo que admite valores positivos y negativos en el intervalo que
va desde el -32.768 hasta el 32.767. En VB serían los datos tipo INTEGER
– Tenemos un tipo de campo que admite valores positivos y negativos en el intervalo que
va desde el -2.147.483.648 hasta el 2.147.483.647. En VB serían los datos tipo LONG
¿Y cuáles son las palabras reservadas en SQL para estos tipos de datos? Pues son las
siguientes:

TIPO DATO VB SQL


Byte BYTE / INTEGER1
Integer SMALLINT / SHORT / INTEGER2
Long INTEGER / LONG / INT / INTEGER4

Conclusión: debemos ir con mucho cuidado y “centrarnos” en si definimos el tipo de dato en


VB o en SQL, puesto que, como podéis ver, las palabras reservadas pueden llevarnos a
confusión por su similitud entre diferentes tipos.
Si volvemos a recurrir a la interfaz de Access estaríamos hablando de:

VALORES DECIMALES
– Tenemos un tipo de datos, denominados “de coma flotante de precisión simple”, que
abarcan un intervalo de valores positivos y negativos que va desde/hasta:
– Para valores negativos: -3.402823E38 y -1,401298E-45
– Para valores positivos: 1,401298E-45 y 3,402823E38
Es lo que en VB conocemos somo SINGLE
– Tenemos otro tipo de datos, denominados “de coma flotante de precisión doble”, que

6
Visítame en http://siliconproject.com.ar/neckkito/
abarcan un intervalo de valores positivos y negativos que va desde/hasta:
– Para valores negativos: -1,79769313486231E308 y
-4,940656458441247E-324
– Para valores positivos: 4,940656458441247E-324 y
1,79769313486231E308
Es lo que en VB conocemos como DOUBLE
– Tenemos otro tipo de valores, que denominamos “datos
decimales”, en los que no profundizaremos en este capítulo,
debido a sus características “especiales”. Para que nos
entendamos, y por decirlo de alguna manera, a no ser que
desarrollemos una base de datos para la NASA, en la que
necesitemos un factor de precisión decimal altísimo (¡pero
que muy muy alto!), mejor no utilizar este tipo de datos.
creo que “tenemos de sobra”.
Para definir estos tipos de datos en SQL tenemos:

TIPO DATO VB SQL


Single REAL / SINGLE / FLOAT4 / IEEESINGLE
Double FLOAT / DOUBLE / FLOAT8 / IEEEDOUBLE /
NUMBER
Decimal DECIMAL

Y si volvemos a la interfaz gráfica de Access estaríamos hablando de:

VALORES DE ID. DE RÉPLICA


Los valores de id. de réplica representan un número de identificación global de 16 bites, y
sirven para poder establecer un identificador único y no repetible cuando, precisamente,
replicamos una base de datos Access.
Si queremos definir este tipo de datos con SQL utilizaríamos:

Id. de réplica GUID

DATOS TIPO FECHA/HORA


Los datos de tipo fecha/hora no tienen mayor dificultad. Resaltar que pueden almacenar
valores que van desde el 01/01/100 a 31/12/9999.
Son los datos que en VB conocemos como DATE.

En SQL tendríamos:

7
Visítame en http://siliconproject.com.ar/neckkito/
TIPO DATO VB SQL
Date DATETIME / DATE / TIME / TIMESTAMP

DATOS TIPO MONEDA


Estos tipos de datos tienen, lógicamente, formato
monetario, y permiten manipular valores que contengan
entre uno y cuatro decimales. Su nivel de precisión es de
hasta quince dígitos a la izquierda del separador de
decimales y, como hemos comentado, cuatro dígitos a su
derecha.
El rango que abarca va desde -922.337.203.685.477,5808 hasta 922.337.203.685.477,5807.
En VB los conocemos como CURRENCY.
En SQL tendríamos:

TIPO DATO VB SQL


Currency MONEY / CURRENCY

DATOS TIPO AUTONUMÉRICO


Los datos de este tipo son en realidad datos numéricos, enmarcados dentro de la categoría de
“Enteros”, y de tipo LONG. Como imagino que todos sabréis, su particularidad reside en que
permite un incremento automático de valor en cada registro nuevo (ojo, que no tiene por que
ser de una unidad en una unidad).
Por esta peculiaridad son idóneos para utilizarlos como clave principal en las tablas.
Para definirlos en SQL utilizamos:

Autonumérico COUNTER / AUTOINCREMENT

DATOS SÍ/NO (DATOS BOOLEANOS)


Los datos sí/no almacenan sólo uno de dos valores posibles: verdadero o falso. Si el valor es
falso se almacena en valor 0, mientras que si es verdadero se almacena el valor -1 (ojo, esto
en Access. Hay otros gestores de bases de datos que pueden tomar el valor 1).
Son los que en VB conocemos como BOOLEAN
Para definirlos en SQL utilizamos:

Booleano BIT / LOGICAL / LOGICAL1

DATOS OBJETO OLE


Este tipo de datos se utiliza para guardar datos binarios largos. Por ejemplo, un documento
Word, o un libro de Excel, corresponden a esta categoría.
Debemos tener en cuenta que su tamaño máximo viene determinado por el tamaño máximo
de archivo que admite Access, que como ya sabemos es de 2GB (para ser más exactos
2,14GB).

8
Visítame en http://siliconproject.com.ar/neckkito/
No hay una equivalencia exacta en VB para un objeto OLE. Si tuviéramos que almacenarlo
utilizaríamos un tipo VARIANT.
Y para definirlos en SQL tendríamos:

Objeto OLE IMAGE / LONGBINARY / GENERAL /


OLEOBJECT

DATOS BINARIOS
Los he dejado para lo último porque este tipo de datos no se
puede definir desde la interfaz de Access (no tenemos la opción al
crear la tabla). ¿Y qué es un dato binario?
Un dato binario no es más que cualquier tipo de dato que se muestra en su formato nativo, sin
sufrir ninguna transformación por parte de Access. En pocas palabras, lo que se escribe es lo
que se muestra.
Hay que decir que los valores binarios son datos de longitud fija (echad un vistazo al apartado
de los datos de texto si no nos acordamos de qué significa “longitud fija”). Cierto es que se
pueden almacenar datos binarios de longitud variable, pero necesitaríamos utilizar ADO para
poder manipular este tipo de datos (no entraremos en esto, de todas maneras).
Para representar estos datos en SQL tenemos:

Binarios BINARY

CUADRO RESUMEN
Después de esta “disertación sobre tipos de datos” probablemente tengamos la cabeza más
que hinchada... Os pongo pues aquí un cuadro resumen con todo lo que acabamos de
comentar:

TIPO DE DATO (según Access) SQL


Texto – Longitud fija (no disponible en la interfaz CHAR
gráfica de Access)

Texto – Longitud variable (String en VBA) ALPHANUMERIC / STRING / TEXT / TEXT (N) /
VARCHAR
Texto - Memo LONGTEXT / LONGCHAR / MEMO / NOTE
Numérico Byte (Byte en VBA) BYTE / INTEGER1
Numérico entero (Integer en VBA) SMALLINT / SHORT / INTEGER2
Númerico entero largo (Long en VBA) INTEGER / LONG / INT / INTEGER4
Numérico simple (Single en VBA) REAL / SINGLE / FLOAT4 / IEEESINGLE
Numérico doble (Double en VBA) FLOAT / DOUBLE / FLOAT8 / IEEEDOUBLE /
NUMBER
Numérico Id. de réplica GUID
Numérico decimal DECIMAL
Fecha/Hora (Date en VBA) DATETIME / DATE / TIME / TIMESTAMP
Moneda (Currency en VBA) MONEY / CURRENCY
Autonumérico COUNTER / AUTOINCREMENT

9
Visítame en http://siliconproject.com.ar/neckkito/
Sí/No (Boolean en VBA) BIT / LOGICAL / LOGICAL1

Objeto OLE IMAGE / LONGBINARY / GENERAL /


OLEOBJECT
Binarios (no disponible en la interfaz gráfica de Access) BINARY

ALGUNOS COMPONENTES DE SQL


Vamos a ver algunos componentes que conforman el lenguaje SQL simplemente para “sentar
bases terminológicas”. Las iremos desarrollando a lo largo del curso.

COMANDOS
Si partimos de la base que una instrucción la definimos como una expresión de consulta SQL
generada por un comando y sus argumentos o parámetros podremos decir que un comando es
aquella instrucción que se puede ejecutar directamente en una consulta SQL.
En el lenguaje SQL podemos encontrar dos tipos de comandos:
– Los pertenecientes al lenguaje de definición de datos (DDL)
– Los pertenecientes al lenguaje de manipulación de datos (DML)
Los primeros permiten crear y definir tablas, campos, índices, procedimientos almacenados,
vistas, usuarios y grupos de trabajo.
De las anteriores acciones hay algunas que son exclusivas de trabajo en ADO. En este curso no
se van a ver estos comandos.
Los segundos permiten crear consultas para ordenar, filtrar y extraer datos de la BD.
Por ejemplo, entre los primeros tendríamos:

CREATE TABLE
ALTER TABLE
CREATE INDEX
DROP TABLE
DROP INDEX

Entre los segundos tendríamos:

SELECT
UPDATE

10
Visítame en http://siliconproject.com.ar/neckkito/
INSERT
DELETE

CLÁUSULAS
Podríamos decir que las cláusulas son condiciones que
permiten la modificación la definición de los datos que
vamos a manipular o seleccionar.
Por ejemplo, son cláusulas:

AS
FROM
WHERE
GROUP BY
CONSTRAINT
HAVING
ORDER BY
PROCEDURE

PREDICADOS
Como predicados podríamos entender aquellas expresiones que, de alguna manera, modifican
la acción de los comandos en cuanto al resultado que se va obtener.
Serían predicados:

ALL
DISTINCT
DISTINCTROW
TOP
UNION

OPERACIONES
Mediante las operaciones definimos alguna “operación específica” (valga la redundancia) que
condiciona la ejecución de la SQL y, por extensión, los resultados mostrados.
Serían operaciones:

UNION
INNER JOIN
LEFT JOIN
RIGHT JOIN

11
Visítame en http://siliconproject.com.ar/neckkito/
OPERADORES
Dentro de los operadores podemos encontrar dos tipos:
– Operadores lógicos
– Operadores matemáticos y de comparación
Dentro de los operadores lógicos tendríamos:

AND
OR
XOR
NOT

Y dentro de los operadores matemáticos y de comparación tendríamos:

< >
<= >=
= <>
BETWEEN...AND IN
LIKE

FUNCIONES AGREGADAS
Las funciones agregadas permiten manipular grupos de registros con la finalidad de retornar
un sólo valor, que será el que defina la propia función.

Dentro de las funciones agregadas podemos encontrar:

COUNT Devuelve el conteo de registros


AVG Devuelve el promedio
SUM Devuelve la suma
VAR Devuelve la varianza de una muestra de población
VARP Devuelve la varianza de una población
STDEV Devuelve la desviación estándar de una muestra de
población
STDEVP Devuelve la desviación estándar de una población
FIRST Devuelve el valor del primer registro
LAST Devuelve el valor del último registro
MAX Devuelve el valor máximo
MIN Devuelve el valor mínimo

12
Visítame en http://siliconproject.com.ar/neckkito/
PREPARACIÓN Y EXPLICACIONES VARIAS PARA SEGUIR EL
RESTO DEL CURSO
Vamos a desarrollar el curso sobre un ejemplo de una base
de datos5, para poder ver aplicaciones prácticas de las
sentencias SQL.
Los ejemplos de sentencias SQL los vamos a ver
incardinados dentro de código VBA, de manera que, en la
mayoría de casos, no tendremos una SQL “directa”, sino
que la pasaremos por una variable, a los simples efectos de
poder ver con mayor claridad las diferentes estructuras de
las SQL.
Las consultas de acción no tienen mayor problema, en este aspecto, porque las vamos a
ejecutar y vamos a poder ver los resultados de manera directa o indirecta; para las consultas
de selección, sin embargo, no podemos apreciar directamente sus resultados. Para poder
verlos tendremos que convertir nuestra SQL en un objeto-consulta de Access, y ello nos
obligará a seguir la siguiente estructura en los códigos:
– Borramos el objeto-consulta por si existiera (si ya existe obtendríamos un error de
código)
– Creamos la SQL
– La convertimos en un objeto-consulta, para poder ver los resultados.
Para poder realizar lo anterior utilizaremos un módulo, que nos ejecutará el primer y el tercer
paso, dejando el segundo en el código del botón que programemos (las programaremos a
través de clicks de botón porque considero que es un proceso muy sencillo), lo cual nos
permitirá centrarnos en esta segunda parte, esto es, en la SQL propiamente dicha.
Para hacer lo anterior vamos a seguir estos pasos:
1.- Creamos una BD en blanco, y sacamos el editor de VB (ALT+F11)
2.- Insertamos un módulo (VBE menú → Insertar → Módulo)
3.- Guardamos ese módulo con el nombre de mdlProcesos
4.- En ese módulo escribimos los siguientes procedimientos6:

Public Sub eliminaConsulta(nomConsulta As String)
Dim qry As Object
For Each qry In CurrentData.AllQueries
If qry.Name = nomConsulta Then
DoCmd.DeleteObject acQuery, qry.Name
Exit For
End If
Next
End Sub

Public Sub creaConsulta(nomConsulta As String, laSql As String)

5 Esta BD inicial os la podéis bajar aquí.


6 Si utilizamos Access 2003 deberemos asegurarnos de que tenemos registrada la librería “Microsoft DAO 3.6 Object Library” (VBE
Menú → Herramientas → Referencias...)

13
Visítame en http://siliconproject.com.ar/neckkito/
Dim qryDef As DAO.QueryDef
Set qryDef = CurrentDb.CreateQueryDef(nomConsulta)
qryDef.SQL = laSql
End Sub

Vamos a seguir con nuestra BD. Vamos a crearnos una
tabla donde introduciremos los datos personales de los
trabajadores. A esta tabla la llamaremos TTrabPersonal. Le
daremos la siguiente estructura:

A continuación nos crearemos una segunda tabla, con los datos laborales de los trabajadores,
y la llamaremos TTrabLaboral, y tendrá la siguiente estructura:

Debemos tener en cuenta que el campo [IdTrab] lo hemos definido a través de un asistente
para búsquedas, que nos ha buscado, en la tabla TTrabPersonal, sus tres primeros campos en
este orden: [Id], [ApellTrab] y [NomTrab]; hemos ordenado por apellidos ascendente,
ocultando la clave principal.
Es decir, que aunque este campo nos muestre apellidos-nombre el valor que almacena es el
código del trabajador.
El mismo proceso se ha seguido para el campo [DependeDe]
Vamos a crearnos una tabla con los datos de los clientes de la empresa, que llamaremos
TClientes. Tendrá la siguiente estructura:

14
Visítame en http://siliconproject.com.ar/neckkito/
Ahora vamos a crear una tabla de ventas donde reflejaremos las
ventas realizadas a los clientes. La llamaremos TVentas, y tendrá
la siguiente estructura:

Teniendo en cuenta que para el campo [IdCliVta] hemos utilizado el asistente para búsquedas,
que ha buscado en la tabla TClientes el nombre del cliente. Es decir, nos muestra el nombre del
cliente, pero almacena su código.
Si hemos realizado correctamente los pasos anteriores la ventana de relaciones nos debería
haber quedado así:

Vamos a crearnos seguidamente una tabla auxiliar, que llamaremos TMeses, que tendrá la
siguiente estructura (no vamos a poner ningún campo como clave principal):

15
Visítame en http://siliconproject.com.ar/neckkito/
Finalmente crearemos una tabla muy simple, cuya idea es que nos recoja el usuario que está
operando en ese momento en la BD. Esta BD tendrá siempre un solo registro (el nombre del
usuario activo), que llamaremos TUsuarioAct, y la construiremos con esta simple estructura:

Ya tenemos las estructuras de las tablas preparadas. Ahora sólo hay que introducir datos.
Evidentemente podéis dar de alta los datos que queráis, pero como yo voy a mostrar los
resultados en base a la información que haya en estas tablas o bien os bajáis la BD de ejemplo
y anexáis los datos a vuestras tablas o bien operáis directamente con la BD de ejemplo...
Como siempre, a gusto del consumidor.
Yo os indico los registros que he dado de alta:
Para la tabla TClientes:

Para la tabla TMeses

Para la tabla TTrabPersonal

16
Visítame en http://siliconproject.com.ar/neckkito/
Para la tabla TTrabLaboral

Para la tabla TVentas

Y para la tabla TUsuarioAct podemos escribir el nombre que queramos. Lógicamente y he

17
Visítame en http://siliconproject.com.ar/neckkito/
escrito:

PARA FINALIZAR ESTE CAPÍTULO

Creo que este capítulo ha sido interesante: hemos cogido dolor de cabeza al leer tanta teoría y
hemos cogido dolor de dedos al tener que prepararnos tantas tablas con datos. Como reza el
dicho: “Para lucir hay que sufrir”... je, je...
Sinceramente creo que era necesario realizar este esfuerzo inicial. En los próximos capítulos
veremos cómo sacar rendimiento a tantos “desvelos”.
Un saludo, y...
¡Suerte!

18
Visítame en http://siliconproject.com.ar/neckkito/
CURSO DE SQL

CAPÍTULO 21

Índice de contenido
INTRODUCCIÓN................................................................................................................................2
MANOS A LA OBRA.....................................................................................................................2
PRIMERA INCURSIÓN: CONSULTAS DE SELECCIÓN...............................................................2
UN INCISO SOBRE LA LLAMADA A LOS CAMPOS...............................................................2
SELECCIÓN DE TODOS LOS CAMPOS DE UNA TABLA.......................................................3
SELECCIÓN DE CAMPOS DE VARIAS TABLAS NO RELACIONADAS...............................3
ORDENANDO NUESTROS RESULTADOS................................................................................3
PRACTIQUEMOS CON UN EJEMPLO............................................................................................4
SQL SIMPLE..............................................................................................................................4
SQL “COMPUESTA”.................................................................................................................5
SQL ORDENADA......................................................................................................................5
PREDICADOS.....................................................................................................................................7
TOP..................................................................................................................................................7
DISTINCT.......................................................................................................................................8
OTRA MANERA DE CONSEGUIR EL MISMO RESULTADO...........................................10
DISTINCTROW............................................................................................................................10
UTILIZACIÓN DE LA CLÁUSULA WHERE.................................................................................11
CARACTERES COMODÍN.........................................................................................................11
OPERADORES.............................................................................................................................12
MARCO SEGÚN EL TIPO DE DATOS.......................................................................................12
CLÁUSULA WHERE, COMODINES Y OPERADORES...........................................................12
EJEMPLOS CON OPERADORES MATEMÁTICOS............................................................13
EJEMPLOS CON OPERADORES DE COMPARACIÓN......................................................14
BETWEEN... AND..............................................................................................................14
IN..........................................................................................................................................14
LIKE.....................................................................................................................................15
EJEMPLOS CON COMPARADORES LÓGICOS..................................................................17
AND.....................................................................................................................................17
OR........................................................................................................................................18
XOR......................................................................................................................................18
IMP / EQV............................................................................................................................19
NOT......................................................................................................................................20
ISNULL / NOT ISNULL.....................................................................................................21
PARA ACABAR ESTE CAPÍTULO.................................................................................................22

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://siliconproject.com.ar/neckkito/
INTRODUCCIÓN

MANOS A LA OBRA

Empezaremos nuestra andadura práctica por SQL con el


aprendizaje de los comandos DML que comentábamos en el
primer capítulo, creo yo que por ser los más “intuitivos”
para quien no ha visto SQL (o ha visto muy poco) en su
construcción y significado.

La BD que utilizaremos de ejemplo para ir desarrollando este capítulo fue la que construimos
(o nos bajamos) en el capítulo 1. De todas maneras ya habréis visto en la nota a pie de página
anterior que os podéis bajar la BD con los ejemplos desarrollados de este capítulo, por si
queréis usarla como “consulta” o “guía”.

Y dicho lo anterior no perdamos más tiempo con discursos y entremos de lleno ya en materia.

PRIMERA INCURSIÓN: CONSULTAS DE SELECCIÓN


Para realizar una consulta simple de selección debemos utilizar la siguiente estructura:

SELECT nomCampo FROM nomTabla

Es decir: “seleccióname el campo tal de esta tabla”.

Esta consulta es extremadamente simple. Si queremos seleccionar más campos debemos


indicarlo separándolos por una coma, de la siguiente manera:

SELECT nomCampo1, nomCampo2 FROM nomTabla

UN INCISO SOBRE LA LLAMADA A LOS CAMPOS

Si estamos trabajando sobre una sola tabla las estructuras anteriores nos funcionarán
perfectamente, pero si tenemos dos o más tablas la cosa se complica un poco.

Personalmente soy de la opinión que, por facilidad de lectura de código, es mejor indicar la
tabla de origen del campo, aunque la consulta sea del tipo más simple. Con ello conseguimos,
además, optimizar el rendimiento de la SQL. Evidentemente esto es una opinión “personal” y
no hay porque seguirla obligatoriamente.

Para indicar la tabla de origen debemos separar con un punto la tabla del campo. De esta
manera, nuestra estructura anterior quedaría de la siguiente manera:

SELECT nomTabla.nomCampo FROM nomTabla

o, si necesitamos más campos:

SELECT nomTabla.nomCampo1, nomTabla.nomCampo2 FROM nomTabla

2
Visítame en http://siliconproject.com.ar/neckkito/
SELECCIÓN DE TODOS LOS CAMPOS DE UNA TABLA

Para seleccionar todos los campos de una tabla tenemos


varias opciones:

– Escribir los nombres de todos los campos


– Utilizar el carácter asterisco (*) como comodín
– Utilizar el predicado ALL *

Parece claro que lo más práctico es utilizar el asterisco, por lo que


nuestra estructura SQL quedaría de la siguiente manera:

SELECT * FROM nomTabla

o también

SELECT ALL * FROM nomTabla

SELECCIÓN DE CAMPOS DE VARIAS TABLAS NO RELACIONADAS

Si queremos seleccionar varios campos de varias tablas no relacionadas el sistema,


afortunadamente, no se nos complica demasiado. La estructura que deberíamos utilizar sería:

SELECT nomTabla1.nomCampo1, nomTabla1.nomCampo2, nomTabla2.nomCampo1


FROM nomTabla1, nomTabla2

Como podemos observar, hemos separado utilizando comas (,) tanto los campos que
queremos como las tablas que queremos.

ORDENANDO NUESTROS RESULTADOS

Para ordenar nuestros registros debemos emplear la cláusula ORDER BY

Así, nuestra sentencia quedaría de la siguiente manera:

SELECT nomTabla.nomCampo1, nomTabla.nomCampo2 FROM nomTabla ORDER BY nomTabla.nomCampoX

Evidentemente, si queremos ordenar por dos campos, nuestra sentencia quedaría así:

SELECT nomTabla.nomCampo1, nomTabla.nomCampo2, nomTabla.nomCampo3


FROM nomTabla ORDER BY nomTabla.nomCampoX, nomTabla.nomCampoY

Lo anterior nos da una ordenación ascendente (de la A a la Z). ¿Qué ocurre si queremos una
ordenación descendente?

Pues que debemos indicárselo a través de la palabra DESC.

La estructura quedaría, pues, así:

SELECT nomTabla.nomCampo1, nomTabla.nomCampo2


FROM nomTabla ORDER BY nomTabla.nomCampoX DESC

3
Visítame en http://siliconproject.com.ar/neckkito/
PRACTIQUEMOS CON UN EJEMPLO
Para que “quede constancia” de nuestros esfuerzos vamos a
creamos un formulario en blanco, al que llamaremos
FConsultasSQL, y que nos servirá para meter los botones
para practicar los distintos tipos de SQL que hemos visto
hasta ahora. Recordad que los códigos para las consultas de
selección, para que podamos ver los resultados, tendrán la
siguiente estructura:

– Eliminamos la consulta, si existiera


– Creamos la consulta SQL (marcado en negrita)
– Creamos el objeto-consulta para poder ver los resultados.

Deberíamos centrarnos en la fase 2, es decir, en la SQL propiamente dicha.

SQL SIMPLE
Insertamos un botón de comando, al que llamaremos cmdSimpleSobreClientes. Lo que vamos
a hacer es extraer toda la información de la tabla TClientes. El código que debemos generar al
botón en el evento “Al hacer click” es el siguiente:


Private Sub cmdSimpleSobreClientes_Click()
'Definimos las variables
Dim miSql As String, nomQuery As String
'Indicamos qué nombre va a tener la consulta
nomQuery = "CClientes"
'La borramos si ya existe a través del código del módulo mdlProcesos
Call eliminaConsulta(nomQuery)

'-----Definimos la consulta SQL------------------------------------------


miSql = "SELECT * FROM TClientes"
'-------------------------------------------------------------------
'Creamos la consulta a través del código del módulo mdlProcesos
Call creaConsulta(nomQuery, miSql)
'Abrimos la consulta creada
DoCmd.OpenQuery nomQuery
End Sub

Si queremos podemos cambiar el asterisco por “ALL *”, y comprobaremos que los resultados
son los mismos.

Para abundar más en el tema vamos a realizar una consulta que nos dé el nombre del cliente y
su población. Para ello creamos un nuevo botón de comando, lo llamamos cmdNomyPoblCli y
le generamos el siguiente código (os recomiendo que intentéis crearla vosotros antes de echar
un vistazo al código):


Private Sub cmdNomyPoblCli_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CNombreYPoblacionClientes"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes"

4
Visítame en http://siliconproject.com.ar/neckkito/
Call creaConsulta(nomQuery, miSql)
DoCmd.OpenQuery nomQuery
End Sub

SQL “COMPUESTA”
Vamos a imaginarnos que necesitamos crear una consulta
como la anterior, pero que necesitamos, en cada registro,
que nos aparezca el nombre del usuario activo.

Recordemos que no existe ninguna relación entre TClientes y


TUsuarioAct (que es donde tenemos informado el nombre del
usuario activo).

Creamos el botón cmdClientesYUser y... ¿cómo sería el código? Pues así:


Private Sub cmdClientesYUser_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClienteYUsuario"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli, TUsuarioAct.NomUser" _


& " FROM TClientes, TUsuarioAct"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Fijaos que, en este caso, sí es necesario utilizar la estructura nomTabla.nomCampoX, y que


tras el FROM debemos indicar las dos tablas de dónde queremos obtener los datos.

Es decir:

SQL ORDENADA

Vamos a ordenar nuestros resultados. Para ello insertaremos un nuevo botón de comando
(cmdOrdenadoAsc) y vamos a generarle el siguiente código:


Private Sub cmdOrdenadoAsc_Click()
Dim miSql As String, nomQuery As String

5
Visítame en http://siliconproject.com.ar/neckkito/
nomQuery = "CNombreYPoblacionClientesAscendente"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli,


TClientes.PoblCli FROM TClientes" _
& " ORDER BY TClientes.NomCli"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Podemos realizar varias ordenaciones por diferentes campos en la misma consulta. El orden de
ordenación lo estableceremos nosotros por el orden en que escribamos tras el ORDER BY. Por
ejemplo, si queremos ordenar por población y, dentro de esta ordenación, a su vez ordenar por
cliente deberíamos escribir este código en el botón cmdOrdenadoAsc2:


Private Sub cmdOrdenadoAsc2_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CNombreYPoblacionClientesAscendente2"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _


& " ORDER BY TClientes.PoblCli,TClientes.NomCli"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Como vemos, la estructura es igual a nuestra consulta simple; simplemente lo que hacemos es
añadir el ORDER BY identificando a continuación el campo que nos dará la ordenación.

En la ilustración de la izquierda podemos ver los resultados de la primera ordenación, y en la


de la derecha los resultados de la segunda.

¿Y si queremos el orden descendente? Pues ya deberíamos saber hacerlo, pero por si las
moscas el código asignado al botón cmdOrdenadoDesc sería:

Private Sub cmdOrdenadoDesc_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CNombreYPoblacionClientesAscendente"
Call eliminaConsulta(nomQuery)

6
Visítame en http://siliconproject.com.ar/neckkito/
miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _
& " ORDER BY TClientes.NomCli DESC"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

¿Fácil, no?

PREDICADOS
Aprovecharemos lo que hemos aprendido anteriormente para aplicar predicados a nuestras
SQL's.

De hecho, ya hemos utilizado un predicado, que ha sido ALL.

La estructura de las SQL, en general, con los predicados, sería:

SELECT <PREDICADO> ...

Echemos un vistazo a otros predicados:

TOP
Supongamos que queremos ver el importe más alto de todas las ventas que hemos realizado.
Para ello necesitaríamos utilizar el predicado TOP. Como queremos ver una (1) sola venta (la
mayor) necesitaremos indicárselo al TOP. Es decir:

SELECT TOP 1 ...

El código que nos daría la consulta con dicho resultado sería:


Private Sub cmdTop_Click()
Dim miSql As String, nomQuery As String
nomQuery = "VentaImporteMasAlto"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TOP 1 TVentas.ImpVta FROM TVentas ORDER BY TVentas.ImpVta DESC"

Call creaConsulta(nomQuery, miSql)

7
Visítame en http://siliconproject.com.ar/neckkito/
DoCmd.OpenQuery nomQuery
End Sub

Fijaos en que debemos ordenar los resultados (ORDER BY)


porque, si no lo hacemos así, nos devolvería el primer
registro de la consulta. Además, la ordenación debe ser
descendente, para que los importes se nos sitúen de mayor
a menor.

Si cambiásemos el 1 por un 5, por ejemplo, obtendríamos las 5


ventas de importe más alto

Sigamos con el TOP. Imaginemos que no queremos utilizar valores absolutos, sino que
queremos utilizar porcentajes. Es decir, que queremos saber el 10% de las ventas más altas.
Para ello utilizaríamos la estructura:

SELECT TOP XX PERCENT …

Donde XX sería el porcentaje deseado.

Nuestro código debería quedar de esta manera:


Private Sub cmdTop10_Click()
Dim miSql As String, nomQuery As String
nomQuery = "VentaImporteMasAlto10xCiento"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TOP 10 PERCENT TVentas.ImpVta FROM TVentas ORDER BY TVentas.ImpVta DESC"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Y el resultado sería:

DISTINCT
Si realizáramos una selección de los clientes a los cuales hemos vendido género podríamos
utilizar la siguiente SQL:


Private Sub cmdDistinct_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesConVenta"

8
Visítame en http://siliconproject.com.ar/neckkito/
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta FROM TVentas"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Nos arrojaría un resultado así:

Como podemos ver, este listado no nos es demasiado práctico, porque se producen
repeticiones del nombre de cliente.

Para obtener valores únicos podemos utilizar el predicado DISTINCT, de acuerdo a la siguiente
estructura:

SELECT DISTINCT …

De manera que modificando ligeramente el código anterior por:


Private Sub cmdDistinct_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesConVenta"
Call eliminaConsulta(nomQuery)

miSql = "SELECT DISTINCT TVentas.IdCliVta FROM TVentas"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

El resultado que obtendríamos ya sería más manejable:

9
Visítame en http://siliconproject.com.ar/neckkito/
OTRA MANERA DE CONSEGUIR EL MISMO RESULTADO
Para este tipo de selección podemos utilizar la cláusula GROUP BY
para obtener el mismo resultado.

La estructura para hacer lo anterior sería:

SELECT nomTabla.nomCampo FROM nomTabla GROUP


BY nomTabla.nomCampo

Así, nuestro código quedaría de la siguiente manera:


Private Sub cmdGBy_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesConVenta2"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta FROM TVentas GROUP BY TVentas.IdCliVta"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

DISTINCTROW
Con DISTINCTROW, a diferencia de DISTINC (que sólo examina el contenido de los campos
seleccionados), se examina el contenido del registro completo independientemente de los
campos indicados en el SELECT.

Para poder realizar un ejemplo vamos, en nuestra tabla TTrabPersonal, a insertar un nuevo
trabajador, con la característica que va a compartir apellido con uno de los trabajadores ya
existentes. Por ejemplo, yo he añadido el siguiente registro:

Si utilizáramos el DISTINC se nos devolverían 10 registros, seleccionando por


apellido, porque eliminaría uno (Lugosi) por estar repetido

Es decir, la SQL sería:

miSql = "SELECT DISTINCT TTrabPersonal.ApellTrab FROM TTrabPersonal"

Sin embargo, si utilizamos DISTINCTROW nos devolvería 11 registros, puesto que


aunque en la SQL examinamos el campo [ApellTrab] se examinan también el resto
de campos.

Nuestro código sería:


Private Sub cmdDistincRow_Click()
Dim miSql As String, nomQuery As String

10
Visítame en http://siliconproject.com.ar/neckkito/
nomQuery = "CTrabDistinctRow"
Call eliminaConsulta(nomQuery)

miSql = "SELECT DISTINCTROW


TTrabPersonal.ApellTrab FROM TTrabPersonal"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Con el resultado:

UTILIZACIÓN DE LA CLÁUSULA WHERE


Hasta ahora hemos visto cómo seleccionar los datos, pero puede ocurrirnos que necesitemos
filtrar los datos existentes para “depurar” nuestra búsqueda de información. Un manera de
hacerlo es a través de la cláusula WHERE.

Sin embargo, antes de entrar de lleno con esta cláusula vamos a ver un par de curiosidades
sobre cómo maximizar la eficacia de los filtros.

CARACTERES COMODÍN
Podemos utilizar, en combinación con la cláusula WHERE, los operadores que vimos en el
primer capítulo (operadores matemáticos y operadores lógicos), y podemos también utilizar lo
que se denominan “caracteres comodín”.

Tenemos los siguientes caracteres comodín:

– * (asterisco): representa cero o más caracteres


– ? (interrogación de cierre): representa cualquier carácter, pero sólo uno.
– # (almohadilla): representa cualquier número, pero un sólo dígito
– [] (corchetes): si en el interior de los corchetes escribimos un símbolo especial,
estamos indicando que lo que queremos es la coincidencia con ese símbolo especial.
– [listaDeCaracteres]: representa que queremos coincidencia con cualquier carácter que
esté dentro de esa lista.
– ! (exclamación de cierre): representa la negación de lo que el signo antecede.

Bueno... Dicho así quizá no acabe de entenderse del todo. Vamos a ver...

● Si yo escribo ENE* podré obtener resultados como ENE (letra n), ENERO o ENERGIA, porque
* representa cero (letra n) o más caracteres.

● Si yo escribo LO?O podré obtener resultados como LOBO, o LORO, pero no LOBEZNO, porque
? representa cualquier carácter, pero sólo uno. Para que me devolviera LOBEZNO debería
buscar LO????O.

● Si yo escribo A#B podré obtener resultados como A1B, o A9B, pero no A11B, porque sólo he
utilizado una almohadilla (un dígito), y A11B contiene dos dígitos numéricos.

11
Visítame en http://siliconproject.com.ar/neckkito/
● Si yo escribo [a-c]* podré obtener resultados como azul, barco, casa, pero no dedo, porque
estoy buscando palabras que empiecen por a, b o c.

● Si yo escribo [a-c, f-g]* podré obtener todas las palabras


que empiecen por a, b, c, f y g.

● Si yo escribo [!w-z]* podré obtener todas las palabras


que empiecen por cualquier letra del abecedario,
exceptuando las que comiencen por w, x, y, z.

● Si yo escribo A[*]A sólo podré obtener el valor A*A, porque al


encerrar el asterisco entre corchetes estoy indicando que no quiero
el asterisco como comodín, sino como literal.

Veremos un par de ejemplos de utilización de estos caracteres comodín un poco más adelante.

OPERADORES
Como comentábamos en el capítulo primero, tenemos dos tipos de operadores: los operadores
matemáticos y los operadores lógicos.
Su combinación con la cláusula WHERE nos permite una gran versatilidad de filtros y de
búsquedas para extraer los datos que nos interesan.

Como ya los comentamos en el capítulo anterior los veremos a través de diversos ejemplos.

MARCO SEGÚN EL TIPO DE DATOS


Según el tipo de dato debemos enmarcar la condición de búsqueda o no entre unos caracteres
especiales.

La siguiente tabla muestra cómo debemos indicárselo a la SQL

TIPO DE CAMPO LO ENCERRAREMOS ENTRE


Numérico / Moneda No lo encerramos. El filtro es directo
Texto Comillas simples (')
Fecha Almohadillas (#)
Booleano No lo encerramos. Lo igualamos a TRUE o a FALSE

CLÁUSULA WHERE, COMODINES Y OPERADORES


La estructura de una construcción SQL con WHERE sería

SELECT nomTabla.nomCampo FROM nomTabla WHERE nomTabla.nomCampo <condición>

Vamos a empezar con una serie de ejemplos de cómo podemos utilizar los diferentes
elementos que hemos visto hasta ahora.

12
Visítame en http://siliconproject.com.ar/neckkito/
EJEMPLOS CON OPERADORES MATEMÁTICOS
Vamos a realizar una consulta sobre la tabla TVentas donde
obtengamos las ventas de importe mayor a 5000 euros.

Recordemos que, al ser un valor numérico, el filtro es


directo (no debemos enmarcarlo entre ningún carácter
especial).

El código que nos haría lo anterior sería:


Private Sub cmdVtasMas5000_Click()
Dim miSql As String, nomQuery As String
nomQuery = "VentasMas5000"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta FROM TVentas" _


& " WHERE TVentas.ImpVta>=5000"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Imaginemos que queremos conocer las ventas realizadas a partir de abril 2012. En este caso
estamos trabajando con un dato de tipo fecha, por lo que debemos recordar enmarcarlo entre
almohadillas.

La SQL que nos devolvería esos valores sería:


Private Sub cmdVtasSupAbril12_Click()
Dim miSql As String, nomQuery As String
nomQuery = "VentasSupAbril12"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta, TVentas.FechVta" _


& " FROM TVentas" _
& " WHERE TVentas.FechVta>=#04-01-12#"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

 Importante: fijaos que he tenido que escribir la fecha en formato inglés (mm/dd/yy) para
conseguir devolver unos resultados correctos. Hay varios factores que influyen en cómo
examina Access el formato de fecha que tenemos en nuestra BD (aunque en la interfaz gráfica
veamos las fechas en formato español -dd/mm/yy-). Es importante comprobar si los
resultados que nos devuelve la SQL para saber si, en nuestra BD, debemos emplear un
formato u otro.

Como podemos ver, la operativa con operadores matemáticos es sencilla y no creo que
necesitemos más ejemplos sobre el tema.

13
Visítame en http://siliconproject.com.ar/neckkito/
EJEMPLOS CON OPERADORES DE COMPARACIÓN

BETWEEN... AND...
La estructura, tras el WHERE, para utilizar este operador
sería:

WHERE nomTabla.nomCampo BETWEEN <valor1>


AND <valor2>

Supongamos que queremos saber qué ventas se han


realizado durante el primer trimestre de 2012.

Nuestra SQL sería la siguiente:


Private Sub cmdVtas1T12_Click()
Dim miSql As String, nomQuery As String
nomQuery = "Ventas1T12"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.ImpVta, TVentas.FechVta FROM TVentas" _


& " WHERE TVentas.FechVta BETWEEN #01-01-12# AND #03-31-12#" _
& " ORDER BY TVentas.FechVta"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Fijaos que he enmarcado las fechas entre almohadillas y que he utilizado el formato de fecha
inglés de nuevo.

IN
El operador IN nos permite seleccionar registros con valores que coincidan con la condición
marcada en el propio IN.

La estructura del WHERE con el operador IN sería:

WHERE nomTabla.nomCampo IN (<valor1>, <valor2>, …, <valorN>)

Por ejemplo, queremos saber qué clientes tenemos en las poblaciones de Valencia y Badajoz.
Nuestra SQL sería:


Private Sub cmdClientesValenciaBadajoz_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesValenciaYBadajoz"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _


& " WHERE TClientes.PoblCli IN ('Valencia', 'Badajoz')"

Call creaConsulta(nomQuery, miSql)

14
Visítame en http://siliconproject.com.ar/neckkito/
DoCmd.OpenQuery nomQuery
End Sub

Fijaos que al buscar cadenas de texto hemos enmarcado los


valores a buscar entre comillas simples.

Si operamos con números, por ejemplo, podríamos utilizar


los valores directos. Por ejemplo, vamos a ver los meses
impares utilizando IN:


Private Sub cmdMesesIN_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CMesesConIN"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TMeses.NumMes, TMeses.NomMes FROM TMeses" _


& " WHERE TMeses.NumMes IN (1,3,5,7,9,11)"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

LIKE
El operador LIKE nos sirve para buscar por aproximación, y lo utilizamos combinándolo con los
caracteres comodín que hemos visto unos apartados más arriba.

La estructura del WHERE con LIKE sería:

WHERE nomTabla.nomCampo LIKE <condición>

Supongamos que queremos saber qué trabajadores están relacionados con el departamento de
ventas. Podríamos pues filtrar por aquellos cuyo cargo contenga las palabras “ven”.

Nuestra SQL podría ser así:


Private Sub cmdTrabRelVtas_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabRelacionadosVentas"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TTrabLaboral.IdTrab, TTrabLaboral.Cargo FROM TTrabLaboral" _


& " WHERE TTrabLaboral.Cargo LIKE '*ven*'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Fijaos que, como estamos buscando una cadena de texto, debemos enmarcar la condición
entre comillas simples.

15
Visítame en http://siliconproject.com.ar/neckkito/
Supongamos que queremos mostrar todos los trabajadores cuyo apellido empiece por las
letras de la F a la J.

Nuestra SQL sería:


Private Sub cmdTrabFJ_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabFaJ"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TTrabPersonal.ApellTrab FROM TTrabPersonal" _


& " WHERE TTrabPersonal.ApellTrab LIKE '[F-J]*'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Del mismo modo, si quisiéramos añadir a la anterior búsqueda aquellos trabajadores cuyo
apellido empiece también por las letras que van de la V a la Z escribiríamos:


Private Sub cmdTrabFJ_VZ_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabFaJyVaZ"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TTrabPersonal.ApellTrab FROM TTrabPersonal" _


& " WHERE TTrabPersonal.ApellTrab LIKE '[F-J, V-Z]*'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si, por el contrario, lo que queremos es ver todos los trabajadores menos aquellos cuyo
apellido empiece por las letras de la F a la J escribiríamos:


Private Sub cmdTrabNoFJ_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabNoFaJ"
Call eliminaConsulta(nomQuery)

miSql = "SELECT DISTINCT TTrabPersonal.ApellTrab FROM TTrabPersonal" _


& " WHERE TTrabPersonal.ApellTrab LIKE '[!F-J]*'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Otro ejemplo con dígitos. Imaginemos que queremos conocer aquellos trabajadores cuyo
teléfono acabe con un 2 seguido de cualquier dígito. Nuestra SQL sería:

16
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdTrabTfno2x_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabTfno2x"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TTrabPersonal.ApellTrab,


TTrabPersonal.TfnoTrab FROM TTrabPersonal" _
& " WHERE TTrabPersonal.TfnoTrab LIKE '*2#'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Finalmente, imaginemos que queremos saber qué trabajadores tienen, en su apellido, la letra I
y la letra O, pero que sólo haya un carácter en medio de ambas letras.

Si utilizáramos el comodín asterisco obtendríamos estos resultados, pero vemos


que no cumplen nuestra condición

Utilizaremos pues el comodín ? para conseguir el resultado que queremos:


Private Sub cmdTrabIxO_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTrabIxO"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TTrabPersonal.ApellTrab FROM TTrabPersonal" _


& " WHERE TTrabPersonal.ApellTrab LIKE '*i?o*'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

EJEMPLOS CON COMPARADORES LÓGICOS


Vamos a ver cómo podemos utilizar los comparadores lógicos. Ni que decir tiene que podemos
combinarlo con todos los anteriores comparadores.

AND
El operador AND obliga a que se cumplan todas las condiciones especificadas para devolver
registros.

Su estructura sería:

WHERE nomTabla.nomCampoX <operador><condicion> AND


nomTabla.nomCampoY <operador><condicion> AND …

Por ejemplo, vamos a recuperar los clientes que sean de Valencia y en su nombre aparezcan
las letras “Frut”.

17
Visítame en http://siliconproject.com.ar/neckkito/
El código sería:


Private Sub cmdCliFrutValencia_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesFrutValencia"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli,


TClientes.PoblCli FROM TClientes" _
& " WHERE TClientes.NomCli LIKE '*Frut*' AND
TClientes.PoblCli='Valencia'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

OR
Con el operador se nos devuelven registros siempre que se cumpla una de las dos condiciones,
o ambas (pueden ser más de dos, ojo).

La estructura es idéntica a la de AND:

WHERE nomTabla.nomCampoX <operador><condicion> OR


nomTabla.nomCampoY <operador><condicion> OR …

Por ejemplo, si queremos saber qué clientes tenemos en Madrid o en Bilbao deberíamos
escribir:


Private Sub cmdCliMadridBilbao_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesMadridBilbao"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _


& " WHERE TClientes.PoblCli='Madrid' OR TClientes.PoblCli='Bilbao'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

XOR
Con el operador XOR obtenemos el registro si sólo se cumple una de las condiciones; si se
cumplen ambas o no se cumple ninguna no obtenemos ningún registro.

La estructura es la misma que la especificada para OR:

WHERE nomTabla.nomCampoX <operador><condicion> XOR

18
Visítame en http://siliconproject.com.ar/neckkito/
nomTabla.nomCampoY <operador><condicion> XOR …

Imaginemos que queremos mostrar o bien los clientes que


están en Barcelona o bien aquellos clientes cuyo CIF
empiece por A, pero no los que sean de Barcelona con NIF
empezando por A. Nuestra SQL sería:


Private Sub cmdXor_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CEjemploXOR"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.NIFCli, TClientes.PoblCli" _


& " FROM TClientes" _
& " WHERE TClientes.NIFCli LIKE 'A*' XOR TClientes.PoblCli='Barcelona'"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

El resultado que obtendríamos es:

Si miramos la tabla TClientes veremos que nos ha excluido el registro

Porque SÍ es de Barcelona y su Nif SÍ empieza por A

Y también nos ha excluido los registros:

porque NO son de Barcelona y su Nif NO empieza por A

IMP / EQV
A modo de información os comentaré que existen los operadores IMP y EQV. No realizaremos
ningún ejemplo sobre los mismos, pero sí os dejaré indicadas sus tablas de verdad, por si en
alguna ocasión os pudieran ser de utilidad.

19
Visítame en http://siliconproject.com.ar/neckkito/
IMP
Expresión1 Expresión2 Devuelve
Verdadero Verdadero Verdadero
Verdadero Falso Falso
Verdadero Null Null
Falso Verdadero Verdadero
Falso Falso Verdadero
Falso Null Verdadero
Null Verdadero Verdadero
Null Falso Null
Null Null Null

EQV
Expresión1 Expresión2 Devuelve
Verdadero Verdadero Verdadero
Verdadero Falso Falso
Falso Verdadero Falso
Falso Falso Verdadero

NOT
El uso del NOT es de lo más sencillo: se antepone a todos los operadores que hemos visto
anteriormente y convierte su significado en el contrario.

Es decir, que su estructura sería:

WHERE nomTabla.nomCampo NOT <expresión>

Por ejemplo, en un epígrafe anterior hemos visto cómo podíamos sacar los meses impares. Si
quisiéramos sacar los meses pares aprovechando el código que ya tenemos escrito podríamos
utilizar el operador NOT, con lo que obtendríamos:


Private Sub cmdNot_Click()
Dim miSql As String, nomQuery As String

20
Visítame en http://siliconproject.com.ar/neckkito/
nomQuery = "CMesesConNOTIN"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TMeses.NumMes, TMeses.NomMes


FROM TMeses" _
& " WHERE TMeses.NumMes NOT IN (1,3,5,7,9,11)"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

ISNULL / NOT ISNULL


Finalmente, si queremos filtrar por campos vacíos (o no vacíos, utilizando el NOT que
acabamos de ver) podemos emplear el operar ISNULL.

La estructura de este operador sería:

WHERE ISNULL(nomTabla.nomCampo)

o utilizando el NOT

WHERE NOT ISNULL(nomTabla.nomCampo)

Vamos, en nuestra tabla TClientes, a añadir un nuevo cliente, pero no le asignaremos ninguna
población. El registro nos quedaría una cosa así:

Entonces, para saber qué clientes no tienen población asignada, escribiríamos:


Private Sub cmdIsNull_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CClientesSinPoblacion"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _


& " WHERE ISNULL(TClientes.PoblCli)"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Lo que nos devolvería el registro que acabamos dar de alta.

Para devolver todos los registros menos el que acabamos de dar de alta simplemente
construiríamos la SQL así:

miSql = "SELECT TClientes.NomCli, TClientes.PoblCli FROM TClientes" _


& " WHERE NOT ISNULL(TClientes.PoblCli)"

21
Visítame en http://siliconproject.com.ar/neckkito/
PARA ACABAR ESTE CAPÍTULO...
Hemos hecho una primera “incursión” en el lenguaje SQL
con sentencias muy simples. El objetivo del mismo era que
os familiarizarais con las estructuras y con las palabras
clave que generalmente veréis en una sentencia SQL
(hablando muy en general). Así, si en un código os
encontráis con SELECT, FROM, ORDER BY... ya sabréis
inmediatamente que estamos hablando de este lenguaje.

Hemos aprendido a utilizar predicados para “depurar”


nuestras búsquedas.

Finalmente, hemos aprendido también cómo filtrar los datos mediante consultas de selección,
aplicando la cláusula WHERE y muchos operadores que tenemos a nuestra disposición.

En próximos capítulos iremos viendo otras posibilidades para manejar datos a través del
Structured Query Language

Un saludo, y

¡suerte!

22
Visítame en http://siliconproject.com.ar/neckkito/
CURSO DE SQL

CAPÍTULO 31

Índice de contenido
COMPLIQUEMOS UN POCO LAS CONSULTAS DE SELECCIÓN..............................................2
VAMOS ALLÁ................................................................................................................................2
CREACIÓN DE “LITERALES”.....................................................................................................2
UTILIZACIÓN DE “ALIAS”.........................................................................................................3
ALIAS DIRECTO COMO CAMPO..........................................................................................3
ALIAS DIRECTO COMO TABLA............................................................................................4
ALIAS INDIRECTO: USO DE FUNCIONES AGREGADAS.................................................5
OPERACIONES MATEMÁTICAS Y OTRAS “POSIBILIDADES”..................................6
FUNCIONES DE VALORES SIMPLES...............................................................................7
ABS().................................................................................................................................8
SQR().................................................................................................................................8
MOD()...............................................................................................................................8
ROUND()..........................................................................................................................8
FUNCIONES DE GRUPOS DE VALORES (FUNCIONES DE AGREGADO)..................8
COUNT()...........................................................................................................................8
SUM()................................................................................................................................9
AVG()................................................................................................................................9
STDEV() / STDEVP().......................................................................................................9
FIRST() / LAST()............................................................................................................10
MAX() / MIN()................................................................................................................10
CONSULTAS ANIDADAS, O SUBCONSULTAS...........................................................................10
UTILIZANDO “IN”......................................................................................................................10
UTILIZANDO “ANY”/”SOME”..................................................................................................12
UTILIZANDO “EXISTS”.............................................................................................................12
COMBINACIÓN DE ELEMENTOS EN SQL..................................................................................13
COMBINACIÓN EXTERNA (O LLÁMAME... “OUTER JOINS”)...........................................13
CORRESPONDENCIA CON LAS CONSULTAS-OBJETO EN ACCESS. TIPOS DE
COMBINACIÓN......................................................................................................................15
COMBINACIÓN INTERNA (O LLÁMAME... “INNER JOIN”)...............................................16
CONSULTA “UNION” / “UNION ALL”.....................................................................................17
CONSULTA “TRANSFORM”......................................................................................................19
AUTOCOMBINACIÓN................................................................................................................21
SELF JOIN....................................................................................................................................22
CLÁUSULA “HAVING”...................................................................................................................24
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................25

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://siliconproject.com.ar/neckkito/
COMPLIQUEMOS UN POCO LAS CONSULTAS DE SELECCIÓN

VAMOS ALLÁ...

Vamos a seguir con nuestras consultas de selección, las


cuales vamos a “complicar” un poco (¡pero no mucho!).

Aprenderemos, en este apartado, a realizar algunas


acciones interesantes para obtener valores que no están,
directamente, en las tablas, para pasar de lleno a la
utilización de funciones agregadas de SQL.

Os recuerdo que la BD de ejemplo con la que trabajaremos es la misma que creamos en el


capítulo 1, y con la que ya trabajamos en el capítulo 2.

Y, como se indica en el título de este epígrafe, ¡vamos allá...!

CREACIÓN DE “LITERALES”
Puede interesarnos, en un momento dado, mostrar una información puntual a través de una
consulta, e identificar claramente qué información estamos obteniendo.

Para estos casos podemos hacer uso de literales.

Un literal no es más que una cadena de texto que nos mostrará el valor literal que hayamos
escrito en cada registro de la consulta.

La estructura para un literal podría ser la siguiente:

SELECT nomTabla.nomCampo1, 'Literal', nomTabla.nomCampo2


FROM nomTabla

Supongamos que queremos ver las ventas, pero con impuestos incluidos, y que nuestro
impuesto es el 18%. Para no perder de vista que estamos trabajando con valores “impuesto
incluido” podríamos querer que en cada registro se mostrara dicha información.

Nuestro código con la SQL pertinente sería el siguiente:


Private Sub cmdLiteral_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CLiteral"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, 'Ventas impuesto incluido: ', TVentas.ImpVta*1.18" _


& " FROM TVentas"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Obtendríamos el siguiente resultado:

2
Visítame en http://siliconproject.com.ar/neckkito/
Fijaos que, aunque no sea una solución muy “elegante”, para una consulta puntual nos puede
sacar del apuro.

También cabe destacar que podemos realizar operaciones matemáticas con los valores de los
registros directamente sobre la SQL. En nuestro caso hemos multiplicado por 1,18:
TVentas.ImpVta*1.18

UTILIZACIÓN DE “ALIAS”
En ocasiones puede interesarnos crear un campo que no existe inicialmente en la tabla. Esta
creación puede ser realizada de dos maneras:

– De manera directa
– De manera indirecta, porque aplicamos alguna función.

¿Y qué es un alias? Para que nos entendamos, es el nombre que nos “inventamos” para poder
hacer referencia a ese campo que no existe, inicialmente.

Supongo que, hasta ahora, la cosa está clara (¿o no?). La verdad es que sin ver un ejemplo
este concepto puede parecer un poco difuso. Vamos pues a plantear una hipótesis de trabajo
para la creación de un alias de manera directa.

ALIAS DIRECTO COMO CAMPO


Imaginemos que el jefe de ventas quiere repasar, sobre papel, las ventas realizadas durante el
año 2011. Como es algo “no oficial” le basta que le imprimamos los resultados de una consulta
con la finalidad de que él pueda puntear dichas ventas.

Evidentemente podríamos crear una consulta como ya sabemos e imprimirla, pero como
queremos quedar bien con el jefe vamos a crearle un campo más a la consulta, en blanco, y
que además la columna esté identificada con el título “Punteado”.

Para ello vamos a crearnos una SQL que utilizará un alias, y ese alias será precisamente el
texto “Punteado”.

Nuestro código sería, entonces:

3
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdAliasPunteado_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CAliasPunteado"
Call eliminaConsulta(nomQuery)
miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta, NULL AS Punteado" _
& " FROM TVentas" _
& " WHERE TVentas.FechVta>#12-31-2011#"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Obtendríamos el siguiente resultado:

Como vemos, del código podríamos sacar varias conclusiones:

– Para definir el alias utilizamos la palabra reservada AS, y la estructura es:

<valor/expresión> AS <alias>

– El campo que hace de filtro NO tiene porqué mostrarse en los resultado (filtramos por
fecha de venta pero no se ve en la consulta)
– Para indicar que queremos un campo vacío utilizamos NULL

Es decir, que si nuestro jefe se pone “tonto” y dice que quiere la palabra NO para poder él ir
tachándola nuestra SQL debería quedar:

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta, 'NO' AS Punteado" _


& " FROM TVentas" _
& " WHERE TVentas.FechVta>#12-31-2011#"

ALIAS DIRECTO COMO TABLA


Por comodidad para escribir la SQL podríamos utilizar un alias sobre el nombre de la tabla en
la que estamos trabajando.

4
Visítame en http://siliconproject.com.ar/neckkito/
Para comodidad de lectura de código podemos utilizar AS, aunque en el caso de tablas
podemos poner el alias directamente, sin necesidad de utilizar el AS.

Por ejemplo, el siguiente código utiliza un alias sobre la


tabla:


Private Sub cmdAliasTabla_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CAliasTabla"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TV.IdCliVta, TV.ImpVta, 'NO' AS Punteado" _


& " FROM TVentas AS TV" _
& " WHERE TV.FechVta<=#12-31-2011# AND TV.ImpVta>8000"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si nos fijamos hemos definido el alias sobre la tabla tras la cláusula FROM, a través de ( FROM
TVentas AS TV), y todas las referencias a nomTabla han sido hechas con “TV”.

Si hubiéramos escrito nuestra SQL así (fijaos lo escrito tras el FROM) nuestra SQL funcionaría
igualmente bien:

miSql = "SELECT TV.IdCliVta, TV.ImpVta, 'NO' AS Punteado" _


& " FROM TVentas TV" _
& " WHERE TV.FechVta<=#12-31-2011# AND TV.ImpVta>8000"

ALIAS INDIRECTO: USO DE FUNCIONES AGREGADAS


La expresión “alias indirecto” la utilizo sólo a efectos de seguir un hilo argumental para el
capítulo, dado que en el fondo un alias es un alias. La diferencia entre “directo” e “indirecto”
estriba, en realidad, en si asignamos “directamente” algún valor al alias (ya sea NULL o ya sea
una cadena de texto o un número) o no asignamos valor alguno (al definir un alias para una
tabla), o si el valor del alias se obtiene de manera indirecta al realizar alguna operación
matemática o lógica o a través de la utilización de funciones agregadas.

Hago también esta separación a efectos pedagógicos, porque pienso que la sistemática del
alias se entiende mejor si realizamos esta categorización.

Quería hacer esta aclaración porque dudo que si alguien busca información por ahí sobre este
tema pueda encontrar estos términos de “alias directo” o “alias indirecto” (o, al menos, yo he
sido incapaz de encontrar algo parecido). Hay que ser originales, ¿verdad?

Clarificado lo anterior vamos a ver este tema de funciones agregadas, pero antes, para hacer
este apartado más completo, veremos el uso de operaciones matemáticas y otras
“posibilidades”.

5
Visítame en http://siliconproject.com.ar/neckkito/
OPERACIONES MATEMÁTICAS Y OTRAS “POSIBILIDADES”
Podemos realizar operaciones matemáticas en la propia SQL
y asignarles un alias. Por ejemplo, ¿nos acordamos de
nuestra SQL de “Ventas al 18%, unas páginas antes?
Vamos a hacer lo mismo, pero desglosando el impuesto y el
total final, utilizando alias para cada uno de ellos.

Si estructuramos un poco lo que sería una posible SQL,


veremos que la parte correspondiente al impuesto podría
ser:

TVentas.ImpVta*0.18 As Impuesto

y para la parte correspondiente al total tenemos dos posibilidades:

.- Una, realizar la operación matemática directamente sobre el importe de la venta:

TVentas.ImpVta*1.18

.- O bien utilizar la suma de dos campos: el existente (ImpVta) y el alias que acabamos
de crear (Impuesto):

(TVentas.ImpVta + Impuesto) AS ImpConImpto

En definitiva, nuestro código podría quedarnos de la siguiente manera:


Private Sub cmdVtasImpto_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CVtasImpto"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta," _


& " TVentas.ImpVta*0.18 AS Impuesto, (TVentas.ImpVta+Impuesto) AS ImpConImpto" _
& " FROM TVentas"

'-----Esta otra SQL también conseguiría lo mismo-----------------------------------------


' miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta," _
' & " TVentas.ImpVta*0.18 AS Impuesto, TVentas.ImpVta*1.18 AS ImpConImpto" _
' & " FROM TVentas"
'----------------------------------------------------------------------------------------

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

 Importante: recordad que para operar con exponentes debemos utilizar el símbolo (^),
siguiendo la estructura (valor^exponente). Para conseguir ese símbolo debemos localizar la
tecla que está a la derecha de la “P” y pulsar la combinación SHIFT+tecla, y a continuación
pulsar la barra espaciadora. Por ejemplo, para hallar el cubo de 25 escribiríamos: 25^3

Supongamos que necesitamos un campo donde se muestren, juntos, “apellido, nombre” de los
trabajadores.

6
Visítame en http://siliconproject.com.ar/neckkito/
Para conseguir eso debemos recurrir al concatenador ampersand (&), y construirnos la
estructura del campo como la deseamos, para después asignarle un alias.

Es decir, que nuestro código para hacer lo anterior sería:


Private Sub cmdApellNombre_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CApellNom"
Call eliminaConsulta(nomQuery)

miSql = "SELECT (TTB.ApellTrab & ', ' & TTB.NomTrab) AS Empleado," _


& " TTB.CIFTrab FROM TTrabPersonal AS TTB"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si nos fijamos, veremos que hemos “construido” el campo a través de la estructura:

<tabla.campo1> & <', '> & <tabla.campo2>


(TTB.ApellTrab & ', ' & TTB.NomTrab)

También, para no tener que escribir cada vez el “largo” nombre de la tabla, he utilizado un
alias sobre la tabla.

FUNCIONES DE VALORES SIMPLES


Vamos a ver una serie de funciones que podemos utilizar con SQL. Las dividiremos en
funciones de valores simples, es decir, funciones que operan sobre un solo valor, de las
siguientes, que son las que operan sobre grupos de valores.

El código “genérico” que utilizaremos para explicar estas funciones será el siguiente:


Private Sub cmdAbs_Click()
Dim miSql As String, nomQuery As String
nomQuery = "NombreConsulta"
Call eliminaConsulta(nomQuery)

miSql = <SQL mostrada en cada apartado>

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si queremos practicar, sólo deberemos copiar el anterior código, sustituyendo lo marcado en

7
Visítame en http://siliconproject.com.ar/neckkito/
negrita.

Así, en los siguientes epígrafes sólo os mostraré lo que debería ser


la SQL.

ABS()
Devuelve el VALOR ABSOLUTO de un número. En la tabla de
ejemplo no se van a producir variaciones en el resultado
porque no hay valores negativos, pero, si los hubiera,
quedarían convertidos en valores positivos.

miSql = "SELECT Abs(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

SQR()
Devuelve la RAÍZ CUADRADA de un valor.

miSql = "SELECT Sqr(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

MOD()
Devuelve el RESTO DE LA DIVISIÓN de un valor entre otro valor.

La SQL muestra el resto de dividir el importe de las ventas entre cinco

miSql = "SELECT (TVentas.ImpVta MOD 5) As Resultado" _


& " FROM TVentas"

ROUND()
La función Round() REDONDEA LOS DECIMALES DE UN VALOR AL NÚMERO DE DECIMALES
QUE INDICAMOS, siguiendo la estructura:

Round(<valor>,<nº de decimales>)

La siguiente SQL redondea los decimales del importe de ventas a un solo decimal.

miSql = "SELECT Round(TVentas.ImpVta,1) As Resultado" _


& " FROM TVentas"

FUNCIONES DE GRUPOS DE VALORES (FUNCIONES DE AGREGADO)


Estas funciones operan sobre un grupo de registros y devuelven un sólo registro de resultado.
Echémosles un vistazo.

COUNT()
Esta función devuelve la CUENTA de los registros.

Si queremos saber cuántos registros cumplen la condición de que las ventas sean superiores a

8
Visítame en http://siliconproject.com.ar/neckkito/
5000 euros nuestra SQL sería:

miSql = "SELECT Count(*) As Resultado" _


& " FROM TVentas" _
& " WHERE TVentas.ImpVta>=5000"

SUM()
Devuelve la SUMA de los valores seleccionados.

Si queremos saber el total de ventas realizadas tendríamos:

miSql = "SELECT Sum(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

AVG()
Devuelve el PROMEDIO de los valores seleccionados.

Así, el promedio de todas las ventas sería:

miSql = "SELECT Avg(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

VAR() / VARP()
La función Var() devuelve la VARIANZA DE UNA MUESTRA DE POBLACIÓN; la función Varp()
devuelve la VARIANZA DE UNA POBLACIÓN.

La SQL sería (ejemplo con Var(); con Varp() la estructura sería la misma):

miSql = "SELECT Var(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

STDEV() / STDEVP()
La función Stdev() devuelve la DESVIACIÓN ESTÁNDAR DE UNA MUESTRA DE POBLACIÓN; la
función Stdevp() devuelve la DESVIACIÓN ESTÁNDAR DE UNA POBLACIÓN.

La SQL sería (ejemplo con StDev(); con StDevp() la estructura sería la misma):

miSql = "SELECT Stdev(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

9
Visítame en http://siliconproject.com.ar/neckkito/
FIRST() / LAST()
First() devuelve el valor DEL PRIMER REGISTRO; Last() devuelve el valor DEL ÚLTIMO
REGISTRO.

Las SQL serían:

miSql = "SELECT First(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

---

miSql = "SELECT Last(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

MAX() / MIN()
Max() devuelve el valor MÁXIMO de un grupo de registros; Min() devuelve el valor MÍNIMO de
un grupo de registros.

La siguiente SQL nos muestra la venta mayor de las que se han realizado:

miSql = "SELECT Max(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

y la siguiente nos muestra la venta más pequeña que se ha conseguido:

miSql = "SELECT Min(TVentas.ImpVta) As Resultado" _


& " FROM TVentas"

CONSULTAS ANIDADAS, O SUBCONSULTAS


En ocasiones puede sernos útil realizar una subconsulta para filtrar los datos de una consulta
principal. Y también, en algunos casos, podrían existir métodos alternativos para conseguir el
mismo resultado.

Sin embargo, es interesante saber que existe esta sistemática de trabajo con las SQL, y que
podemos utilizarlas.

UTILIZANDO “IN”

Planteemos el problema: queremos saber el detalle del importe de las ventas realizadas por los
clientes (hasta aquí ya deberíamos saber hacerlo), pero sólo de los clientes que están en las
poblaciones de Badajoz y Cádiz.

Bueno, bueno... primer problema que tenemos: las ventas están en una tabla y las poblaciones

10
Visítame en http://siliconproject.com.ar/neckkito/
están en otra.

Si operáramos con la consulta como objeto de Access con


toda probabilidad sabríamos cómo hacerlo: añadiríamos
ambas tablas, las relacionaríamos por su identificador y
filtraríamos por los valores que queremos.

La anterior operación la podríamos realizar utilizando la


operación INNER JOIN (que veremos más adelante). Pero
en este apartado vamos a realizar una subconsulta para
conseguir el mismo efecto.

Vayamos por partes, empezando por detrás:

Si quisiéramos realizar una SQL que nos filtrara los clientes de Badajoz y Cádiz, cogiendo su
identificador de cliente, escribiríamos:

miSql = "SELECT TClientes.IdCli FROM TClientes" _


& " WHERE TClientes.PoblCli IN ('Badajoz','Cádiz')"

Y esto ya lo sabemos hacer.

La anterior consulta nos devuelve los valores {2,5,10}

Para construir la SQL principal podríamos pues escribir:

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta FROM TVentas" _


& " WHERE TVentas.IdCliVta IN (2,5,10)”

¿Estamos de acuerdo?

Pues básicamente se trata de sustituir los valores por la consulta que nos proporciona esos
valores.

Es decir, que nuestro código nos quedaría así:


Private Sub cmdSubconsultas_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CSubconsulta"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta FROM TVentas" _


& " WHERE TVentas.IdCliVta IN (" _
& "SELECT TClientes.IdCli FROM TClientes" _
& " WHERE TClientes.PoblCli IN ('Badajoz','Cádiz'))"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Sólo llamar la atención sobre el hecho de que no debemos olvidarnos el paréntesis de cierre
del IN () de la consulta principal.

11
Visítame en http://siliconproject.com.ar/neckkito/
UTILIZANDO “ANY”/”SOME”
Los predicados ANY o SOME son sinónimos, por lo que
podemos emplear tanto uno como otro. Se utilizan como
comparadores de los resultados que devuelve la
subconsulta.

Por ejemplo, imaginemos que queremos ver una lista con


todos los datos de los clientes que han realizado compras
durante el año 2011. Nuestra SQL podría quedar de la
siguiente manera:


Private Sub cmdSubconsultas2_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CSubconsulta2"
Call eliminaConsulta(nomQuery)

miSql = "SELECT * FROM TClientes" _


& " WHERE TClientes.IdCli= ANY (" _
& "SELECT TVentas.IdCliVta FROM TVentas" _
& " WHERE TVentas.FechVta<#01-01-12#)"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si utilizáramos el predicado ALL en lugar de ANY obtendríamos un resultado en el cual los


registros de la consulta principal satisfacen la comparación con todos los registros obtenidos de
la subconsulta.

UTILIZANDO “EXISTS”
Con el predicado EXISTS lo que obtenemos es un TRUE o FALSE; es decir, comprobamos si la
subconsulta nos dice Sí o No.

Por ejemplo, queremos saber qué clientes NO han realizado ninguna compra durante el año
2011. Será nuestra subconsulta la que nos dirá si sí han realizado compra o no, y la consulta
principal nos mostrará los resultados:


Private Sub cmdSubconsulta3_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CSubconsulta3"
Call eliminaConsulta(nomQuery)

miSql = "SELECT * FROM TClientes" _


& " WHERE NOT EXISTS (" _
& "SELECT * FROM TVentas" _
& " WHERE (TVentas.FechVta<#01-01-12# AND" _
& " TClientes.IdCli=TVentas.IdCliVta))"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

12
Visítame en http://siliconproject.com.ar/neckkito/

Fijaos en que:
– Para saber “si el cliente existe” hemos tenido que indicar la
correspondencia del identificador de cliente, necesario para
que la SQL funcione
(& " Tclientes.IdCli=TVentas.IdCliVta))")

– Para indicar que NO EXISTE hemos utilizado NOT


(NOT EXISTS). Si no ponemos el NOT obtendríamos los que
SÍ han realizado compras durante 2011.

COMBINACIÓN DE ELEMENTOS EN SQL


En este apartado vamos a ver cómo podemos combinar elementos de la misma o de distintas
tablas en una sola consulta.

COMBINACIÓN EXTERNA (O LLÁMAME... “OUTER JOINS”)


En SQL podemos encontrar dos tipos de Outer Join:

– LEFT OUTER JOIN


– RIGHT OUTER JOIN

La estructura de una OUTER JOIN sería, en general, la siguiente:

SELECT …
FROM tabla1 LEFT/RIGHT OUTER JOIN tabla2
ON <condición de relación entre campos de tablas>

¿Y qué es un “Outer Join”? Os pego lo que el propio Access dice sobre el tema:

<<La combinación externa (OUTER JOIN) se utiliza para recuperar registros de varias tablas mientras se conservan
los registros de una de las tablas, aun cuando en la otra no haya ningún registro que coincida. El motor de base de
datos de Access admite dos tipos de combinaciones externas: LEFT OUTER JOIN (izquierda) y RIGHT OUTER
JOIN (derecha). Imagine dos tablas situadas una junto a otra, una a la izquierda y otra a la derecha. La LEFT
OUTER JOIN selecciona todas las filas de la tabla derecha que cumplen los criterios de comparación relacionales,
además de seleccionar todas la filas de la tabla izquierda, aun cuando no existan coincidencias en la tabla derecha. La
RIGHT OUTER JOIN es sencillamente lo contrario de la LEFT OUTER JOIN: en ella se conservan todas las filas
de la tabla derecha>>

A ver si con un ejemplo clarificamos lo anterior. La finalidad de nuestra consulta: mostrar todos
los clientes dados de alta en nuestra BD (a través de TClientes) y la suma de las ventas
realizadas para cada uno de ellos.

¿Y qué pasa si hay algún cliente al que aún no se le ha vendido nada? Pues no hay problema,
porque:

– TClientes será nuestra tabla de la izquierda


– Utilizaremos LEFT OUTER JOIN
– En consecuencia, nos mostrará TODOS los registros de TClientes, aunque no haya
ventas realizadas a algún cliente.

Es decir, que si programamos la siguiente SQL:

13
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdOuterJoins_Click()
Dim miSql As String, nomQuery As String
nomQuery = "COuterJoins"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes.IdCli, TClientes.NomCli, Sum(TVentas.ImpVta) As Ventas" _


&" FROM TClientes LEFT OUTER JOIN TVentas" _
&" ON TClientes.IdCli = TVentas.IdCliVta" _
&" GROUP BY TClientes.IdCli, TClientes.NomCli"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

El resultado que obtendremos será el siguiente:

Como podemos apreciar, a “Rábanos Buenitos” aún no se le ha efectuado ninguna venta, por lo
que el cliente nos aparece, pero con un valor NULL en la columna “Ventas”.

Un par de comentarios sobre el código:

– Hemos utilizado una función de agregado (Sum())


– Por ello, debemos agrupar los campos que queremos mostrar mediante un GROUP BY

Si no lo hiciéramos así obtendríamos un error. Supongamos que en nuestra SQL nos olvidamos
de agrupar por NomCli, de manera que la última línea de la SQL fuera:

& " GROUP BY TClientes.IdCli"

Si intentamos ejecutar el código obtendríamos el siguiente error:

14
Visítame en http://siliconproject.com.ar/neckkito/
Conclusión: si obtenemos este error debemos comprobar nuestro GROUP BY

Sólo nos queda comentar que podemos omitir la palabra OUTER y Access seguirá entendiendo
que queremos realizar una combinación externa. Es decir, que si en el anterior código
hubiéramos escrito:

miSql = "SELECT TClientes.IdCli, TClientes.NomCli, Sum(TVentas.ImpVta) As Ventas" _


& " FROM TClientes LEFT JOIN TVentas" _
& " ON TClientes.IdCli = TVentas.IdCliVta" _
& " GROUP BY TClientes.IdCli, Tclientes.NomCli"

la consulta nos hubiera funcionado perfectamente.

CORRESPONDENCIA CON LAS CONSULTAS-OBJETO EN ACCESS.


TIPOS DE COMBINACIÓN
Como supongo todo el mundo sabrá (y si no lo sabe pues lo aprenderemos ahora... no
problem) existen tres tipos de combinaciones en una relación entre dos tablas en una
consulta-objeto de Access:

– Combinación tipo 1: la consulta devuelve sólo los registros coincidentes en ambas


tablas.
– Combinación tipo 2: la consulta devuelve todos los registros de la tabla de la izquierda,
y sólo los coincidentes de la tabla de la derecha
– Combinación tipo 2: la consulta devuelve todos los registros de la tabla de la derecha, y
sólo los coincidentes de la tabla de la izquierda.

Cuando realizamos una consulta en vista diseño entre dos tablas relacionadas Access utiliza la
combinación tipo 1 por defecto. Veámoslo:

.- Creamos una consulta en vista diseño sobre las tablas TClientes y TVentas.
Automáticamente se nos debería crear una relación entre ambas a través de los campos [IdCli]
e [IdCliVta]

15
Visítame en http://siliconproject.com.ar/neckkito/
.- Nos situamos sobre la línea de relación y, con mucho pulso, hacemos click con el botón de la
derecha. Nos saldrá un menú contextual con dos opciones:

.- Si seleccionamos la opción “Propiedades de la


combinación” veremos que, por defecto, tenemos marcada
la combinación tipo 1, pudiendo cambiar la combinación a
tipo 2 o tipo 3

Llegados aquí, si seleccionamos:

– La combinación tipo 2 estaremos realizando una LEFT OUTER JOIN


– La combinación tipo 3 estaremos realizando una RIGHT OUTER JOIN
– La combianción tipo 1 estaremos realizando un INNER JOIN

Y, en consecuencia, vamos a ver la relación INNER JOIN a continuación.

COMBINACIÓN INTERNA (O LLÁMAME... “INNER JOIN”)


Como hemos visto anteriormente, una combinación INNER JOIN devuelve sólo los registros
coincidentes.

La estructura de este tipo de consultas, en general, sería:

SELECT …
FROM tabla1 INNER JOIN tabla2
ON <condición de relación entre campos de tablas>

Siguiendo con el ejemplo anterior, si convertimos la consulta que hemos hecho en una INNER
JOIN obtendríamos:


Private Sub cmdInnerJoin_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CInnerJoin"
Call eliminaConsulta(nomQuery)

16
Visítame en http://siliconproject.com.ar/neckkito/
miSql = "SELECT TClientes.IdCli, TClientes.NomCli, Sum(TVentas.ImpVta) As
Ventas" _
& " FROM TClientes INNER JOIN TVentas" _
& " ON TClientes.IdCli = TVentas.IdCliVta" _
& " GROUP BY TClientes.IdCli, TClientes.NomCli"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Es decir, que no nos aparecerá la empresa “Rábanos Buenitos” porque no tiene


correspondencia con ningún registro en TVentas.

CONSULTA “UNION” / “UNION ALL”


Podemos combinar una o varias tablas para la consecución del resultado que nos interese a
través de una consulta de UNION.

La estructura de una consulta de Unión sería, para que nos entendemos, la unión de dos
consultas SELECT a través de la operación UNION.

SELECT … FROM …
UNION
SELECT … FROM …

Por ejemplo, supongamos que queremos obtener una consulta que nos devuelva los clientes
con ventas superiores a 15000 euros y también los clientes con ventas inferiores a 2000,
descartando los valores intermedios, podríamos realizar la siguiente consulta de unión:


Private Sub cmdUnion_Click()
Dim miSql As String, nomQuery As String

17
Visítame en http://siliconproject.com.ar/neckkito/
nomQuery = "CUnion"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, TVentas.ImpVta FROM TVentas" _


&" WHERE TVentas.ImpVta>=15000" _
&" UNION" _
&" SELECT TVentas.IdCliVta, TVentas.ImpVta FROM TVentas" _
&" WHERE TVentas.ImpVta<=2000"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Si analizamos el código, veremos que las dos primeras líneas


conforman una SQL de selección que ya sabemos construir sin
problemas, lo mismo que las dos últimas líneas. Entre ambas hemos
añadido la operación UNION para conseguir la información prevista.

Podemos realizar una consulta de unión entre dos tablas también,


aunque la estructura deberá ser consistente. Por poner un ejemplo
absurdo (o no... de todo hay en este mundo) imaginemos que queremos conseguir un listado
de empresas clientes y de trabajadores. Podríamos crear una consulta de unión entre
TTrabPersonal y TClientes. Por ejemplo, nuestra consulta podría ser:


Private Sub cmdUnion2_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CUnion2"
Call eliminaConsulta(nomQuery)

miSql = "SELECT (TTrabPersonal.NomTrab & ' ' & TTrabPersonal.ApellTrab) AS Listado" _


&" FROM TTrabPersonal" _
&" UNION" _
&" SELECT TClientes.NomCli FROM TClientes"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Como vemos he tenido que crear un campo que me uniera el nombre del trabajador y su
apellido porque el nombre de la empresa corresponde sólo a un campo, no a dos; en definitiva,
lo que os comentaba de conseguir una estructura consistente.

Para saciar vuestra segura curiosidad, aquí tenéis capturado un fragmento de lo que devolvería
la anterior consulta:

18
Visítame en http://siliconproject.com.ar/neckkito/
Os copio aquí algunas indicaciones que da la propia ayuda de Access sobre este tipo de
consultas que creo que pueden seros útiles:

<<De forma predeterminada, no se devuelven registros duplicados cuando se usa la operación UNION; sin embargo,
puede incluir el predicado ALL para asegurarse de que se devuelven todos los registros. Además, de esta manera, la
consulta se ejecuta más rápidamente.
Todas las consultas de una operación UNION deben solicitar el mismo número de campos; sin embargo, no es
necesario que los campos sean del mismo tamaño o tipo de datos.
Use alias sólo en la primera instrucción SELECT, ya que en las demás se omiten. En la cláusula ORDER BY, haga
referencia a los campos por el nombre que se utilice en la primera instrucción SELECT.>>

Y, para acabar, tened en cuenta que las consultas de unión NO ADMITEN el uso de campos tipo
hipervínculo, objeto OLE o Datos Adjuntos. Si los intentáis utilizar el campo os saldrá con
#ERROR

CONSULTA “TRANSFORM”
Una SQL con la instrucción TRANSFORM es lo que en Access conocemos como “consulta de
referencias cruzadas”

La estructura en general de una SQL con TRANSFORM sería:

TRANSFORM
<función de agregado>
SELECT … FROM …
GROUP BY …
PIVOT …

Vamos a ver cómo aplicamos lo anterior para construirnos una SQL. Lo haremos razonándolo
porque este tipo de consulta, para quienes la ven por primera vez, puede parecer muy
complicada, aunque no tiene por qué serlo.

Imaginemos que queremos ver la suma por clientes y por meses, correspondientes al año
2012. Quedémonos con este enunciado, pues será el hilo conductor para realizar la SQL.

Hemos dicho que queremos ver “la suma de las ventas”. Esto ya nos da el primer elemento de
la SQL. Y como contiene una función de agregado (suma) será lo primero que escribiremos en
la SQL. Así:

TRANSFORM
Sum(TVentas.ImpVta) As Ventas

19
Visítame en http://siliconproject.com.ar/neckkito/
Sigamos:

¿Cuál es el criterio de la vista que queremos obtener? Pues el criterio es “por clientes” y “por
meses”.

Vamos a por el primer criterio. Necesitamos los clientes...


pues vamos a seleccionarlos:

SELECT TVentas.IdCliVta FROM TVentas

pero vamos a necesitar un filtro, porque sólo queremos


incluir las ventas del año 2012. Pues sigamos como si
construyéramos una SQL filtrada:

WHERE TVentas.FechVta>=#01-01-12#

¿Recordamos qué ocurría cuando utilizábamos una función de agregado? Pues lo


comentábamos en el apartado anterior “Combinación externa”, con el error 3122. Así que... a
agrupar

GROUP BY TVentas.IdCliVta

Y vamos a por el segundo criterio. Queremos la información por meses. Y para sacar el mes de
una fecha utilizamos la función Datepart() -eso deberíamos saberlo de VB, pero si no lo
sabemos lo aprendemos aquí... sin problemas-. Y la función Datepart(), para que nos devuelva
el mes, tiene la siguiente sintaxis: Datepart('m',<campo con la fecha>).

Si seguimos la estructura indicada en el comienzo de este apartado escribiríamos pues:

PIVOT Datepart('m',TVentas.FechVta)

¿A que no es tan difícil? (¡Espero que respondáis con un SÍ unánime...!).

Nuestro código quedaría así:


Private Sub cmdTransform_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CTransform"
Call eliminaConsulta(nomQuery)

miSql = "TRANSFORM sum(TVentas.ImpVta) AS Ventas" _


&" SELECT TVentas.IdCliVta FROM TVentas" _
&" WHERE TVentas.FechVta>=#01-01-12#" _
&" GROUP BY TVentas.IdCliVta" _
&" PIVOT Datepart('m',TVentas.FechVta)"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Y la consulta nos devolvería la siguiente información:

20
Visítame en http://siliconproject.com.ar/neckkito/
AUTOCOMBINACIÓN
Supongamos que queremos obtener una información que pase por combinar elementos que
tenemos dentro de la propia tabla y que, de alguna manera, dependen los unos de los otros.

Para realizar la autocombinacion vamos a servirnos de la misma tabla pero, gracias a un alias,
vamos a tratarlas cómo si fueran dos tablas distintas.

Por ejemplo, vamos a ver una consulta que nos devolverá el empleado con su cargo junto con
el nombre del empleado del que depende, también con su puesto.

A la tabla de empleados (que saldrá de TTrabLaboral) le pondremos el alias “Empl”, y a la tabla


de superiores (que también saldrá de TTrabLaboral) le pondremos el alias “Sup”.

Nuestro código quedaría así:


Private Sub cmdAutocombinacion_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CAutocombinacion"
Call eliminaConsulta(nomQuery)

miSql = "SELECT Empl.IdTrab, Empl.Cargo, Sup.IdTrab, Sup.Cargo" _


& " FROM TTrabLaboral AS Empl, TTrabLaboral AS Sup" _
& " WHERE Empl.DependeDe=Sup.IdTrab"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Fijémonos cómo:

– Hemos utilizado dos alias sobre la misma tabla, de manera que “parece” que
manejamos dos tablas diferentes (TTrabLaboral AS Empl, TTrabLaboral AS Sup)
– Hemos seleccionado los campos que nos interesaban seleccionándolos como si fueran
campos de esas dos tablas diferentes (SELECT Empl.IdTrab, Empl.Cargo,
Sup.IdTrab, Sup.Cargo)
– Para obtener la relación hemos utilizado datos que tenemos en la misma tabla, y que
provienen de los campos [IdTrab] y [DependeDe] (WHERE
Empl.DependeDe=Sup.IdTrab)

Y como resultado obtenemos una consulta tan “hermosa” como la siguiente:

21
Visítame en http://siliconproject.com.ar/neckkito/
SELF JOIN
Para conseguir un producto cartesiano de una tabla consigo misma podemos utilizar una
estructura SELF JOIN. Un producto cartesiano establece una relación de cada uno de los
elementos de la tabla por sí mismo y por el resto de elementos de la tabla.

Para poder poner un ejemplo de SELF JOIN vamos a tener que crearnos una nueva tabla, que
llamaremos TRepresentantes. Esta tabla nos recogerá los clientes y los responsables dentro del
departamento de ventas que se encargan de ellos (tranquilos: haremos el ejemplo con pocos
registros).

La estructura de la tabla será la siguiente:

Teniendo en cuenta que en el campo [IdCliRepr] el tipo de datos viene dado por el asistente
para búsquedas, donde hemos seleccionado la tabla TClientes y como campo visible [NomCli[,
y que para el campo [IdTrabRepr] el tipo de datos se ha creado utilizando el asistente para
búsquedas, donde hemos seleccionado de la tabla TTrabPersonal y como campos visibles
[ApellTrab] y [NomTrabl] (por este orden).

Rellenamos la tabla con unos pocos datos. Yo la he rellenado así:

Es decir, que nuestra empresa siempre asigna dos representantes para cada cliente, para
poderse cubrir las “vacaciones” (porque en nuestra empresa SÍ damos vacaciones )

Vamos a realizar la misma “operación” que en el apartado anterior; es decir, vamos a “dividir”
nuestra tabla en dos utilizando un alias.

El código para una combinación SELF JOIN sobre esta tabla podría ser:

22
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdSelfJoin_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CSelfJoin"
Call eliminaConsulta(nomQuery)

miSql = "SELECT Uno.IdCliRepr, Uno.IdTrabRepr, Dos.IdTrabRepr" _


&" FROM TRepresentantes AS Uno, TRepresentantes AS Dos" _
&" WHERE Uno.IdCliRepr = Dos.IdCliRepr" _
&" AND Uno.IdTrabRepr > Dos.IdTrabRepr"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Y obtenemos:

Como podemos ver, a una “hipotética” tabla la hemos llamado “Uno”, y a la otra “Dos”, y las
hemos relacionado a través de (Uno.IdCliRepr = Dos.IdCliRepr). Si nos hubiéramos
quedado aquí (sin añadir el AND) tendríamos una SELF JOIN en toda regla. Es decir, que nos
saldría el “producto cartesiano” puro y duro. Para los incrédulos...

En nuestro ejemplo no tenía mucho sentido la información Filemón-Filemón, o Secreto-


Secreto, o Secreto-Zape/Zape-Secreto. Para eliminar estos “duplicados” hemos añadido el
AND, que los elimina.

23
Visítame en http://siliconproject.com.ar/neckkito/
CLÁUSULA “HAVING”
La cláusula HAVING tiene un comportamiento muy parecido
al WHERE, pero trabaja con grupos de registros (WHERE no
puede trabajar con grupos de registros). Hablando para que
nos entendamos, WHERE nos muestra un conjunto de filas
que cumplen un criterio como campos individuales, y
HAVING nos muestra grupos de registros que cumplen un
criterio como grupo de registros.

Y, como hablamos de grupos, no podemos olvidarnos de utilizar el GROUP BY con HAVING.

Vamos a “desgranar” un ejemplo con lo anterior.

Imaginemos que queremos obtener una consulta de los totales de ventas, pero que nos
muestre sólo aquellos totales que sean mayores que un límite que hemos establecido como
objetivo de ventas, que será de 8.500 euros.

Debemos crear pues una primera agrupación, que será la que nos calculará la suma de las
ventas agrupadas por cliente.

SELECT TVentas.IdCliVta, Sum(TVentas.ImpVta) AS VentasTotales FROM TVentas


GROUP BY TVentas.IdCliVta

Con esta primera agrupación ya tenemos los totales de todas las ventas, pero ahora queremos
realizar un filtrado sobre ese grupo, de manera que sólo nos muestre aquellos registros (que,
recordemos, están formados por la agrupación por suma de registros individuales) que
cumplen nuestra condición.

Para ello deberíamos agregar dicho filtro sobre grupo, es decir, HAVING

HAVING Sum(TVentas.ImpVta)>=8500

Si combinamos todo lo anterior en una SQL obtendríamos:


Private Sub cmdHaving_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CHaving"
Call eliminaConsulta(nomQuery)

miSql = "SELECT TVentas.IdCliVta, Sum(TVentas.ImpVta) AS VentasTotales" _


&" FROM TVentas" _
&" GROUP BY TVentas.IdCliVta" _
&" HAVING Sum(TVentas.ImpVta)>=8500"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

Como curiosidad, para rematar este apartado, os diré que, según Microsoft, una cláusula
HAVING puede contener hasta 40 expresiones vinculadas por operadores lógicos, como AND y
OR.

24
Visítame en http://siliconproject.com.ar/neckkito/
PARA FINALIZAR ESTE CAPÍTULO
Creo que con este capítulo ya tenemos abundante material
para poder realizar unas SQL “alucinantes” en nuestras BD's

Sólo quisiera recalcar que, lógicamente, por motivos


pedagógicos hemos visto apartado por apartado diferentes
elementos, operaciones y demás que podemos utilizar con
SQL. No perdáis de vista que todo lo anterior se puede
combinar en una “enorme” SQL, que, por decirlo
poéticamente, vendrá limitada por las características del
propio lenguaje SQL y por nuestra imaginación.

Espero que todo lo anterior os pueda ser de utilidad.

Un saludo, y...

¡suerte!

25
Visítame en http://siliconproject.com.ar/neckkito/
CURSO DE SQL

CAPÍTULO 41

Índice de contenido
LAS CONSULTAS DE ACCIÓN........................................................................................................2
TRES Y... ¡ACCIÓN!......................................................................................................................2
CONSULTA DE DATOS ANEXADOS..........................................................................................3
ANEXAR EN UNA TABLA EXISTENTE (un único registro).................................................3
ANEXAR EN UNA TABLA EXISTENTE (todos los registros / conjunto de registros)...........4
ANEXAR HACIA UNA BD EXTERNA...................................................................................5
ANEXAR DESDE UNA BD EXTERNA..................................................................................6
CONSULTA DE ACTUALIZACIÓN.............................................................................................6
CONSULTA DE ELIMINACIÓN...................................................................................................7
CONSULTA PARA BÚSQUEDA DE REGISTROS DUPLICADOS............................................8
CONSULTAS PARAMETRIZADAS..................................................................................................9
PROCEDURES..................................................................................................................................10
PARA FINALIZAR ESTE CAPÍTULO.............................................................................................10

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://siliconproject.com.ar/neckkito/
LAS CONSULTAS DE ACCIÓN

TRES Y... ¡ACCIÓN!

Con lo explicado en los capítulos anteriores tenemos un


amplio abanico para seleccionar datos, filtrarlos, re-filtrarlos
y volverlos a filtrar... todo sea por obtener la información tal
y como nosotros queremos.

Sin embargo, hay más cosas que seleccionar datos,


¿verdad?

En este cuarto capítulo vamos a ver cómo ejecutar lo que se denominan consultas de acción.
Veamos un par de cosas comunes que aplicaremos a lo largo de todo este documento.

Las consultas de acción “operan” sobre los datos; es decir, los cambian, los actualizan, los
eliminan, los “crean” (en una nueva tabla, por ejemplo). Y, por ese motivo, son “peligrosas”.
Un error en alguna de nuestras consultas y nuestros datos... pluf.

Por este motivo nuestro amigo Access nos advierte y vuelve a advertir antes de ejecutar una
consulta de estas características. Es decir, que si yo intento ejecutar, por ejemplo, una consulta
para borrar registros (de lo más cool en cuanto a peligrosidad  ) me encontraré con lo
siguiente:

Estas advertencias están muy bien cuando “no sabemos bien lo que hacemos”, pero, ¿y cuándo
lo tenemos más que claro? Pues dichos warnings se convierten en algo de lo más incómodo
(pueden llegar a salirnos tres warnings seguidos, a veces). Click, y click, y click para conseguir
ejecutar la consulta...

Lo anterior podemos evitarlo en nuestro código VB para Access añadiendo dos líneas de
código: una antes de la ejecución de la consulta y otra a continuación de la ejecución de la
consulta. Lo escribiríamos de la siguiente manera:


DoCmd.SetWarnings False
'Ejecución de la consulta
DoCmd.SetWarnings True

Con estas dos líneas se acabaron los avisos de Access... aunque si nos hemos equivocado...
“Houston, tenemos un problema”.

 Importante: es “vital” que volvamos a activar la aparición de warnings a través del


<DoCmd.SetWarnings True>. Ello es así porque la inhabilitación de los avisos (a través del

2
Visítame en http://siliconproject.com.ar/neckkito/
<DoCmd.SetWarnings False>) no se limita a deshabilitar los avisos para ese procedimiento en
concreto que estemos programando, sino que los deshabilita “en general”. Y ello implica que si
se produce algún error en cualquier parte Access no nos avisará, con lo que nuestro código no
funcionará, o no funcionará bien, y no sabremos por qué. Tened cuidado con
“olvidarse” de volver a habilitar los avisos.

Nosotros, en los códigos que veamos de ejemplo,


utilizaremos la desactivación/activación de los warnings.
Pero si queréis hacer la prueba de ejecutar algunas
consultas de acción sin el SetWarnings os invito a ello,
simplemente para que podáis ver qué mensajes de
advertencia nos lanza Access.

Más cosas... Las consultas de acción se ejecutan directamente a través del código. Por ello
veréis que ya no nos hará falta recurrir al código de mdlProcesos que utilizábamos hasta
ahora. Es decir, no nos será necesario crearnos la consulta para poder ver los resultados.

La ejecución de una consulta SQL, utilizando nuestro código VB, se ejecutará a través de

DoCmd.RunSQL <nombreSQL>

 Nota: tened en cuenta que también podríamos utilizar:

CurrentDb.Execute <nombreSQL>

En general (ojo, en general, aunque hay excepciones) no debería darnos problemas utilizar
una sintaxis u otra al operar con consultas de selección o de acción.

Como siempre (pero por si acaso lo repito), los ejemplos de este capítulo se basarán en la
tabla que creamos en el capítulo 1.

Y, ahora sí, vamos a echar una ojeada a las consultas de acción en SQL.

CONSULTA DE DATOS ANEXADOS

ANEXAR EN UNA TABLA EXISTENTE (un único registro)


La instrucción SQL que nos permite realizar este tipo de consultas es INSERT INTO... VALUES

Su estructura sería la siguiente:

INSERT INTO nomTabla(nomCampo1, …, nomCampoN) VALUES (valor1, …, valorN)

Para desarrollar el ejemplo vamos a hacer un copiar-pegar manual de la tabla TClientes (pero
sólo la estructura, no los datos). Para ello copiamos dicha tabla y antes de pegarla con el
nombre de TClientes2 seleccionamos la “opción de pegado → Estructura solamente”.

Supongamos que queremos insertar un nuevo cliente cuyos datos son:


– Nombre (NomCli): Sopas La Morsa
– NIF (NIFCli): B251234546
– Población (PoblCli): Almería

Nuestro código debería quedar, en consecuencia, escrito de la siguiente manera:

3
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdDatosAnexados1_Click()
Dim miSql As String

miSql = "INSERT INTO TClientes2(NomCli,NIFCli,PoblCli)" _


& " VALUES ('Sopas La Morsa','B25123456','Almería')"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Anexión realizada correctamente",


vbInformation, "OK"
End Sub

Incluyo el MsgBox al final porque si no es frustrante no saber si las cosas han ido bien o no, o
dudar de si se ha realizado el proceso o no.

¿Fácil, verdad?

ANEXAR EN UNA TABLA EXISTENTE (todos los registros / conjunto de


registros)
Como probablemente nos ha ido bien con un solo registro vamos a “pelearnos” con un
“grupito” de registros. Lo que haremos será seleccionar todos los registros de una tabla
TClientes a nuestra nueva tabla TClientes2.

Le estructura difiere ligeramente de la anterior, siendo, en general, la siguiente:

INSERT INTO <TablaDestino>


SELECT * FROM <TablaOrigen>

Nuestro código nos debería quedar:


Private Sub cmdDatosAnexadosTodos_Click()
Dim miSql As String

miSql = "INSERT INTO TClientes2 SELECT * FROM TClientes"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Anexión realizada correctamente", vbInformation, "OK"


End Sub

Si lo que queremos no son todos los registros sino un conjunto de registros simplemente
deberíamos añadir un WHERE a nuestra SQL.

Para que quede más claro, por si acaso, por ejemplo anexaremos a esta tabla los registros de
los clientes cuyo CIF empiece por A.

El código sería:

4
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdDatosAnexadosAlgunos_Click()
Dim miSql As String

miSql = "INSERT INTO TClientes2 SELECT * FROM TClientes" _


& " WHERE TClientes.NIFCli LIKE 'A*'"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Anexión realizada correctamente", vbInformation,


"OK"
End Sub

ANEXAR HACIA UNA BD EXTERNA


Para realizar este ejemplo vamos a crearnos una nueva BD en el mismo directorio donde
tenemos nuestra BD de ejemplo, y la guardaremos como TablaExterna.mdb (o .accdb).
Crearemos una tabla, que llamaremos TClientesExt, con la siguiente estructura:

Como podemos intuir, vamos a anexar en esta tabla los datos de nuestra tabla TClientes (sólo
los campos con el nombre y el NIF).

La estructura de este tipo de exportación sería la siguiente:

INSERT INTO <tablaDestino> IN '<rutaBDExterna>'


SELECT nomTabla.nomCampo1,...,nomTabla.nomCampoN FROM nomTabla

Es decir, que nuestro código en la BD de ejemplo debería ser el siguiente:


Private Sub cmdDatosAnexadosHaciaBDExterrna_Click()
Dim rutaBDExt As String, miSql As String

rutaBDExt = Application.CurrentProject.Path & "\TablaExterna.mdb"

miSql = "INSERT INTO TClientesExt IN '" & rutaBDExt & "'" _


& " SELECT TClientes.NomCli, TClientes.NIFCli FROM TClientes"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Anexión realizada correctamente", vbInformation, "OK"


End Sub

5
Visítame en http://siliconproject.com.ar/neckkito/
 Tened en cuenta que los nombres de campos deben ser idénticos. Si no lo fueran el código
nos diría que no se encuentra el campo XXX. Esto es válido también para el apartado
que se explica a continuación.

ANEXAR DESDE UNA BD EXTERNA


Podemos realizar la misma operación, pero al revés. Es
decir, introducir valores de una tabla externa en una tabla
de nuestra BD.

La estructura de la SQL sería la siguiente:

INSERT INTO <nomTabla>


SELECT * FROM <nomTablaExt> IN '<rutaBDExterna>'

Es decir, que nuestro código nos debería quedar así, si queremos anexar los registros de
TClientesExt en TClientes2:


Private Sub cmdDatosAnexadosDesdeBDExterna_Click()
Dim rutaBDExt As String, miSql As String

rutaBDExt = Application.CurrentProject.Path & "\TablaExterna.mdb"

miSql = "INSERT INTO TClientes2" _


& " SELECT * FROM TClientesExt IN '" & rutaBDExt & "'"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Anexión realizada correctamente", vbInformation, "OK"


End Sub

CONSULTA DE ACTUALIZACIÓN
Para realizar las consultas de actualización utilizaremos la instrucción UPDATE. La estructura de
una consulta de actualización es:

UPDATE nomTabla
SET nomCampo1 = <nuevoValor>,..., nomCampoN = <nuevoValor>

Es muy usual utilizar WHERE con este tipo de consultas para indicar qué registros son los que
deben ser utilizados. La cláusula WHERE se sitúa al final. Es decir:

UPDATE nomTabla
SET nomCampo1 = <nuevoValor>,..., nomCampoN = <nuevoValor>
WHERE <condición>

 Es importante acordarse del WHERE, porque si no la consulta de actualización afectará a


TODOS los registros, y la acción NO se podrá deshacer. ¿Recordamos nuestros

6
Visítame en http://siliconproject.com.ar/neckkito/
DoCmd.SetWarnings? ¡Hay que utilizarlos con mucho cuidado!

Supongamos que queremos cambiar las poblaciones porque


nos hemos equivocado. Donde pusimos “Valencia” debería
ser “Castellón”. Nuestra consulta sería:


Private Sub cmdActualizaTClientes_Click()
Dim miSql As String

miSql = "UPDATE TClientes SET PoblCli = 'Castellón'" _


& " WHERE PoblCli='Valencia'"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Actualización realizada correctamente", vbInformation, "OK"


End Sub

Recordad que también podemos realizar cálculos para actualizar datos. Por ejemplo, el día que
metimos los datos en nuestra BD no estábamos muy “inspirados” y al dar de alta las ventas no
nos dimos cuenta que las metíamos “impuestos incluidos”. Lógicamente había que almacenar
los datos con “impuestos excluidos”.

Si, por ejemplo, habíamos aplicado una tasa del 18%, nuestra consulta de actualización
debería ser:


Private Sub cmdActualizaTVentas_Click()
Dim miSql As String

miSql = "UPDATE TVentas SET ImpVta = ImpVta/1.18"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Actualización realizada correctamente", vbInformation, "OK"


End Sub

Efectivamente, en este caso necesitaba que se actualizaran todos los registros, por lo que he
omitido el uso de WHERE

CONSULTA DE ELIMINACIÓN
Las instrucción que nos define una consulta de eliminación es DELETE.

El uso de DELETE, personalmente, me recuerda a la figura de “asesino a sueldo”: realiza el


trabajo de “limpieza” de una manera rápida, eficaz y sin dejar huellas 

Su estructura es:

DELETE FROM nomTabla

7
Visítame en http://siliconproject.com.ar/neckkito/
Y, si queremos ser un poco selectivos (es decir, no realizar una matanza masiva), utilizamos
WHERE

DELETE FROM nomTabla WHERE <condición>

Al igual que lo que comentábamos en las consultas de


actualización, una vez ejecutada la instrucción DELETE no
hay vuelta atrás, así que debemos proceder con mucho
cuidado.

En nuestra BD de ejemplo debemos tener por ahí una tabla llamada TClientes2. Vamos a
eliminar todos los clientes que tengan, en su nombre, alguna relación con la “carne”.

Nuestra SQL sería la siguiente:


Private Sub cmdEliminaDeTClientes2_Click()
Dim miSql As String

miSql = "DELETE FROM TClientes2 WHERE nomCli LIKE 'Carn*'" _


& " OR nomCli LIKE 'Cárn*'"

DoCmd.SetWarnings False
DoCmd.RunSQL miSql
DoCmd.SetWarnings True

MsgBox "Eliminación realizada correctamente", vbInformation, "OK"


End Sub

CONSULTA PARA BÚSQUEDA DE REGISTROS DUPLICADOS


Esta consulta resulta un poco “enrevesada”, y mi recomendación, ya que tenemos Access, es
crear este tipo de consulta utilizando el asistente, básicamente por comodidad (después
podemos situar la consulta en vista SQL y “reciclar” el código ).

Dicho lo anterior vamos a ver cómo podemos confeccionar dicha SQL, pero antes, en nuestra
tabla TClientes2, vamos crear un nuevo registro con el nombre duplicado de un cliente, pero
con distinto NIF.

La estructura de la SQL sería:

SELECT nomTabla.nomCampo1,..., nomTabla.nomCampoN FROM nomTabla


WHERE <nomTabla.campoABuscar> IN
(SELECT <campoABuscar> FROM nomTabla AS <alias>
GROUP BY <campoABuscar> HAVING Count(*)>1)
ORDER BY <nomTabla.campoABuscar>

El código que nos haría lo anterior es:


Private Sub cmdDuplicados_Click()
Dim miSql As String, nomQuery As String
nomQuery = "CDuplicados"

8
Visítame en http://siliconproject.com.ar/neckkito/
Call eliminaConsulta(nomQuery)

miSql = "SELECT TClientes2.NomCli, TClientes2.NIFCli FROM TClientes2" _


&" WHERE TClientes2.NomCli IN (SELECT NomCli FROM TClientes2 AS SubCons" _
&" GROUP BY NomCli HAVING Count(*)>1)" _
&" ORDER BY TClientes2.NomCli"

Call creaConsulta(nomQuery, miSql)


DoCmd.OpenQuery nomQuery
End Sub

CONSULTAS PARAMETRIZADAS
Podemos construir consultas en las cuales se solicite información al usuario para, a
continuación, mostrar los datos deseados. Esta solicitud de información se realiza a través de
lo que denominamos “parámetros”.

Y para ello utilizamos la declaración PARAMETERS, que tiene la siguiente estructura:

PARAMETERS nomParametro1 <tipoDato>, …, nomParametroN <tipoDato>;


<ESTRUCTURA DE LA CONSULTA QUE QUERAMOS EJECUTAR>

Supongamos que queremos saber las ventas que se han producido en nuestra empresa, pero a
veces necesitamos los datos con anterioridad a una fecha y dependiendo de un determinado
volumen de ventas.

Nuestros parámetros, en este caso, serían la fecha y el importe. Más allá la consulta sería una
simple consulta de selección.

Ergo nuestro código podría ser el siguiente:


Private Sub cmdCParametrizada_Click()
Dim miSql As String, nomSql As String
nomSql = "FiltroVentas"
Call eliminaConsulta(nomSql)

miSql = "PARAMETERS FechaMax Date, ImportMin Currency;" _


& " SELECT * FROM TVentas WHERE TVentas.FechVta<=FechaMax" _
& " AND TVentas.ImpVta>=ImportMin"

Call creaConsulta(nomSql, miSql)


DoCmd.OpenQuery nomSql
End Sub

9
Visítame en http://siliconproject.com.ar/neckkito/
Fijaos que separamos los parámetros de la consulta utilizando punto y coma (;). Ojo: no hay
que olvidarse de ese punto y coma porque si no obtendremos error en los parámetros de la
SQL.

PROCEDURES
La cláusula PROCEDURE, según Access, <<define un
nombre y parámetros opcionales para una consulta>>.
Como siempre, la ayuda de Access a veces “se pasa” con la
explicación . A eso debemos añadir que esta cláusula ha
quedado obsoleta, debiéndose utilizar (siempre según
Access) la instrucción PROCEDURE.

No la voy a explicar porque, sinceramente, no tengo mucha idea de cómo hacerlo. En primer
lugar porque no la he utilizado nunca; en segundo lugar porque soy incapaz de encontrar un
ejemplo “práctico” para aplicar a nuestra BD de ejemplo, dado que al combinarla con VB esta
instrucción pierde un poco el sentido (siempre desde mi punto de vista).

Si además os digo que en todos los manuales o tutoriales de SQL que he visto, al hablar de
PROCEDURE, se dice que “no es común”, “es muy poco común”, “es muy poco utilizada”, y
comentarios similares... pues creo que tampoco nos perdemos gran cosa.

En la ayuda de Access hay un ejemplo de cómo programar una SQL utilizando la


cláusula/instrucción PROCEDURE. Si alguien quiere echarle un vistazo invitado está a ello.

En definitiva, que así sabréis que existe “esto que llaman PROCEDURE”, y por eso lo dejo,
como mínimo, apuntado.

PARA FINALIZAR ESTE CAPÍTULO


Este capítulo cierra un primer ciclo con SQL, dado que hasta el momento (es decir, desde el
capítulo 1) hemos estado operando con los datos de las tablas y creando estructuras de
consultas (como consulta-objeto de Access, quiero decir).

Desde mi punto de vista lo que hemos aprendido hasta el momento nos amplia nuestras
posibilidades de operar con Access, dado que SQL, combinado con VBA, nos permite realizar
acciones que con sólo las herramientas de Access quizá se hiciera complicado resolver, o quizá
nos sometiera a la utilización de procedimientos que, sin ser malos, pueden ser optimizados.

En el próximo capítulo comenzaremos un nuevo ciclo: ya no operaremos con datos, sino que
trabajaremos con el alma máter de Access: las tablas y su estructura.

Espero que lo aprendido hasta ahora os haya podido ser útil.

¡Suerte!

10
Visítame en http://siliconproject.com.ar/neckkito/
CURSO DE SQL

CAPÍTULO 51

Índice de contenido
TRABAJAR CON TABLAS................................................................................................................2
DESEO UNA TABLA “A MEDIDA”.............................................................................................2
CREAR UNA TABLA.....................................................................................................................2
LA CLÁUSULA “CONSTRAINT”............................................................................................3
UNA PEQUEÑA ACLARACIÓN (PARA NO TENER QUE “RASCARNOS LA
CABEZA”).............................................................................................................................3
DEFINIR UN CAMPO QUE NO ADMITE DUPLICADOS: UNIQUE..............................3
DEFINIR UN CAMPO COMO CLAVE PRINCIPAL: PRIMARY KEY.............................4
DEFINIR UN CAMPO COMO REQUERIDO: NOT NULL...............................................5
DEFINIR UN CAMPO COMO AUTONUMÉRICO Y DEFINIR SU INTERVALO DE
INCREMENTO......................................................................................................................6
COMBINAR DEFINICIÓN DE PROPIEDADES................................................................8
DEFINIR UNA CLAVE EXTERNA: FOREING KEY.........................................................8
BORRAR UNA TABLA................................................................................................................10
CREAR ÍNDICES (con CREATE INDEX)..................................................................................10
ÍNDICES CON REGISTROS DUPLICADOS.........................................................................11
PROHIBIR VALORES NULOS EN EL ÍNDICE...............................................................12
IGNORAR VALORES NULOS EN EL ÍNDICE................................................................13
ÍNDICE CON REGISTROS NO DUPLICADOS....................................................................14
CREAR UN ÍNDICE CON CLAVE PRIMARIA................................................................15
ELIMINAR ÍNDICES...................................................................................................................15
MODIFICAR UNA TABLA..........................................................................................................15
AÑADIR CAMPOS..................................................................................................................16
BORRAR CAMPOS.................................................................................................................16
CREAR ÍNDICES (con ALTER TABLE)................................................................................17
CREAR UNA CLAVE PRINCIPAL....................................................................................17
CREAR UNA CLAVE EXTERNA......................................................................................17
MODIFICAR EL TIPO DE RELACIÓN........................................................................18
AÑADIR UNA REGLA DE VALIDACIÓN............................................................................20
ELIMINAR ÍNDICES..............................................................................................................20
PARA FINALIZAR EL CAPÍTULO (¡Y EL CURSO!)....................................................................21

1 La BD donde están los ejemplos de este capítulo os la podéis bajar aquí.

1
Visítame en http://siliconproject.com.ar/neckkito/
TRABAJAR CON TABLAS

DESEO UNA TABLA “A MEDIDA”

Con lo explicado en los capítulos anteriores tenemos un


amplio abanico para seleccionar datos, filtrarlos, re-filtrarlos
y volverlos a filtrar... todo sea por obtener la información tal
y como nosotros queremos. Y, por supuesto, ya sabemos
como actualizar datos en registros, anexarlos, eliminarlos...

Como siempre, seguiremos con nuestra “eterna” BD de ejemplo.

Veremos en este capítulo cómo podemos crearnos una tabla con SQL, definiendo sus campos,
cómo podemos definir también algunas de sus propiedades, cómo podemos crear claves
primarias, claves externas... Y también veremos cómo podemos modificar tablas: modificar o
eliminar columnas, eliminar índices...

En definitiva, aprenderemos a crearnos una tabla “a medida”.

CREAR UNA TABLA


La forma más simple de crear una tabla la podemos ver en la siguiente estructura:

CREATE TABLE nomTabla (nomCampo1 tipoCampo (longitudCampo),..., nomCampoN


tipoCampo (longitudCampo))

teniendo en cuenta que utilizaremos <longitudCampo> en aquellos campos en los que tenga
sentido determinar una longitud. Si no indicamos la longitud el campo adoptará el valor por
defecto de Access.

Por ejemplo, vamos a crear la tabla TProductos con tres campos: [CodProd] (de tipo texto y de
longitud 5), [DescrProd] (de tipo texto y de longitud 25) e [ImportProd] (de tipo moneda).

Nuestro código quedaría de la siguiente manera2:


Private Sub cmdCreaTProductos_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (CodProd STRING (5), DescripProd STRING (25)," _
& " ImportProd CURRENCY)"

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"

2 Algunos de los códigos que se utilizarán llevan control de errores. El error 3010 se produce cuando la tabla ya existe. Por eso, si
salta dicho error, el código borra la tabla existente y vuelve a crearla de nuevo en función de lo declarado en la SQL.

2
Visítame en http://siliconproject.com.ar/neckkito/
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " -
" & Err.Description
Resume Salida
End If
End Sub

 Nota: es posible que, tras ejecutar el código, no nos aparezca TProductos en la ventana de
base de datos o en el panel de navegación (según la versión de Access que utilicemos). Si
seleccionamos cualquier tabla que tengamos en la BD y pulsamos 'F5' se producirá una
actualización y nuestra tabla “aparecerá como por arte de magia”

LA CLÁUSULA “CONSTRAINT”
Para establecer limitaciones o definiciones en nuestros campos debemos utilizar la cláusula
CONSTRAINT. Avanzaremos que también la podemos utilizar para establecer relaciones con
otra tabla.

Podemos utilizar CONSTRAINT de dos maneras:

• Para realizar la limitación en un sólo campo


• Para realizar la limitación en un grupo de campos

A continuación veremos algunas restricciones que podemos aplicar en nuestros campos

UNA PEQUEÑA ACLARACIÓN (PARA NO TENER QUE “RASCARNOS LA CABEZA”)


En los siguientes códigos que veremos podréis observar cómo yo estoy utilizando CONSTRAINT
para sentar las propiedades de los campos. Sin embargo, según en qué casos, podemos obviar
la estructura de CONSTRAINT y escribir “directamente” la propiedad que queremos que adopte
el campo creado.

Por eso, en los códigos, os indicaré en color gris, como comentario de código, cómo sería la
SQL con esta utilización directa de las definiciones de las propiedades.

En definitiva, que “mataremos dos pájaros de un tiro” 

Lo importante es que lo veáis escrito de una manera u otra seamos capaces de entender “qué
dice” la SQL

DEFINIR UN CAMPO QUE NO ADMITE DUPLICADOS: UNIQUE


Vamos a borrar nuestra tabla TProductos para volverla a crear, pero esta vez la crearemos
haciendo que [CodProd] no admita duplicados. Para conseguir tal propósito utilizamos la
propiedad UNIQUE.

Nuestro código nos quedaría así:

3
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdUnique_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (CodProd STRING (5) CONSTRAINT miIndice UNIQUE," _
& " DescripProd STRING (25), ImportProd CURRENCY)"

'----------Asignación directa------
' miSql = "CREATE TABLE TProductos (CodProd STRING UNIQUE, DescripProd STRING (25)," _
' & "ImportProd CURRENCY)"
'----------------------------------

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
Resume Salida
End If
End Sub

Fijaos que:

• Defino el campo (nombre-tipo-longitud)


• Restrinjo el campo (CONSTRAINT)
• Indico que es un índice (miIndice) -ojo: podemos emplear la palabra que queramos-
• Indico la propiedad (UNIQUE)

Es decir: CodProd STRING (5) CONSTRAINT miIndice UNIQUE

DEFINIR UN CAMPO COMO CLAVE PRINCIPAL: PRIMARY KEY


Al igual que hemos indicado que queríamos un campo sin duplicados, podemos indicar que
queremos una clave principal.

Por ejemplo, nuestro código de producto será la clave principal.

Nuestro código quedaría así:

4
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdPrimaryKey_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (CodProd STRING (5) CONSTRAINT miClave PRIMARY KEY," _
& " DescripProd STRING (25), ImportProd CURRENCY)"

'----------Asignación directa------
' miSql = "CREATE TABLE TProductos (CodProd STRING (5) PRIMARY KEY," _
' & " DescripProd STRING (25), ImportProd CURRENCY)"
'----------------------------------

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
Resume Salida
End If
End Sub

Si os fijáis, la sistemática es muy similar en la definición de la propiedad “sin duplicados”:

[CodProd] de tipo TEXTO y longitud 5 – Restringido - será mi clave, que será clave principal

CodProd STRING (5) CONSTRAINT miClave PRIMARY KEY

DEFINIR UN CAMPO COMO REQUERIDO: NOT NULL


Para definir un campo como “requerido” debemos utilizar la propiedad NOT NULL.

En nuestra tabla TProductos definiremos una clave principal ([CodProd]) y exigiremos que se
deba rellenar el campo [DescripProd].

El código sería:

5
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdNotNull_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (CodProd STRING (5) CONSTRAINT miClave PRIMARY KEY," _
& " DescripProd STRING (25) CONSTRAINT miNoNulo NOT NULL, ImportProd CURRENCY)"

'----------Asignación directa------
' miSql = "CREATE TABLE TProductos (CodProd STRING (5) PRIMARY KEY," _
' & " DescripProd STRING (25) NOT NULL, ImportProd CURRENCY)"
'----------------------------------

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
Resume Salida
End If
End Sub

No creo que haga ya falta insistir en la estructura del CONSTRAINT...NOT NULL, ¿verdad? 

DEFINIR UN CAMPO COMO AUTONUMÉRICO Y DEFINIR SU INTERVALO DE


INCREMENTO
Vamos a añadir a nuestra tabla TProductos un [IdProd] autonumérico, que será clave principal,
y que, en lugar de tener el incremento predeterminado de una unidad, va a tener un
incremento de tres unidades.

Si recordamos lo visto en el capítulo 1 de este curso sabremos que para definir un


autonumérico debemos definir su tipo como COUNTER / AUTOINCREMENT

Por intuición ya deberíamos saber, pues, cómo podría ser nuestra SQL, aunque tiene un
“pequeño truco”. Sería la que aparece en el siguiente código:

6
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdAutonumerico_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (IdProd AUTOINCREMENT(1,3) CONSTRAINT miClave PRIMARY KEY," _
& " CodProd STRING (5), DescripProd STRING (25), ImportProd CURRENCY)"

'----------Asignación directa------
' miSql = "CREATE TABLE TProductos (IdProd AUTOINCREMENT(1,3) PRIMARY KEY," _
' & " CodProd STRING (5), DescripProd STRING (25), ImportProd CURRENCY)"
'----------------------------------

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
Resume Salida
End If
End Sub

Si nos fijamos hemos definido el tipo de campo [IdProd] como AUTOINCREMENT (si
hubiéramos utilizado COUNTER funcionaría exactamente igual), y, entre paréntesis, le hemos
pasado dos valores. Supongo que alguien se preguntará: “¿Y qué significan?”.

Pues (1,3) significa que:

1 → El primer registro que se introduzca empezará por 1. Si hubiéramos puesto 15 el primer


registro hubiera tenido un [IdProd] igual a 15
3 → Los incrementos se producirán de 3 en 3.

Es decir, que la introducción de registros nos quedaría así:

7
Visítame en http://siliconproject.com.ar/neckkito/
COMBINAR DEFINICIÓN DE PROPIEDADES
Como apunte final a lo explicado hasta ahora debo deciros
que las anteriores propiedades pueden combinarse en una
misma SQL (por si había alguna duda).

Por ejemplo, para crear la tabla TProductos con un


autonumérico que sea clave principal, con un campo
[CodProd] que sea a la vez un campo sin duplicados y de
introducción obligatoria y con [DescripProd] que sea un
campo de introducción obligatoria podríamos utilizar el
siguiente código3:


Private Sub cmdTProductosCombinado_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "CREATE TABLE TProductos (IdProd COUNTER (1,5) CONSTRAINT miClave PRIMARY KEY," _
& " CodProd STRING (5) CONSTRAINT miIndice UNIQUE NOT NULL," _
& " DescripProd STRING (25) CONSTRAINT miIndice2 UNIQUE, ImportProd CURRENCY)"

'----------Asignación directa------
' miSql = "CREATE TABLE TProductos (IdProd COUNTER (1,5) PRIMARY KEY," _
' & " CodProd STRING (5)UNIQUE NOT NULL, DescripProd STRING (25) UNIQUE," _
' & " ImportProd CURRENCY)"
'----------------------------------

Creo_tabla:
CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha creado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3010 Then
DoCmd.DeleteObject acTable, "TProductos"
Resume Creo_tabla
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
Resume Salida
End If
End Sub

Como podéis observar el proceso es tan simple como añadir la definición de las propiedades
una tras otra.

DEFINIR UNA CLAVE EXTERNA: FOREING KEY


Por si alguien anda ligeramente perdido, y de una manera rápida, diremos que una clave
externa es una clave de relación con otra tabla.

Por ejemplo,

3 Recordad que, en SQL, para un campo autonumérico AUTOINCREMENT y COUNTER son sinónimos. En el ejemplo anterior he
utilizado AUTOINCREMENT, y en el ejemplo que sigue he utilizado COUNTER.

8
Visítame en http://siliconproject.com.ar/neckkito/
Si nos fijamos en la tabla TPedidos diríamos que:

• Id es la clave principal
• NumMes es la clave externa, porque nos relaciona la tabla con una tabla externa, que
es TMeses.

Clarificados conceptos vamos a realizar con SQL lo que yo he realizado manualmente en


Access.

La estructura para crear una clave externa es la siguiente:

CONSTRAINT miClaveExt FOREIGN KEY (nomCampoInterno) REFERENCES


nomTablaExterna (nomCampoExterno)

Es decir, que nuestro código4 quedaría así.


Private Sub cmdForeingKey_Click()
Dim miSql As String

miSql = "CREATE TABLE TPedidos (Id LONG CONSTRAINT miClave PRIMARY KEY," _
& " NumMes LONG, Cliente STRING (25), ImpPedido CURRENCY," _
& " CONSTRAINT miClaveExt FOREIGN KEY (NumMes) REFERENCES TMeses)"

CurrentDb.Execute miSql
MsgBox "La tabla 'TPedidos' se ha creado correctamente", vbInformation, "OK"
End Sub

Fijaos en un detalle: en TPedidos el campo se llama [NumMes]; en TMeses el campo también


se llama [NumMes]. Es por ello por lo que he podido obviar el nombre del campo en
<REFERENCES TMeses (NumMes)>, y simplemente he escrito <REFERENCES TMeses>.

Para eliminar toda duda la SQL, con nombres de campo diferentes, nos debería haber quedado
así:


miSql = "CREATE TABLE TPedidos (Id LONG CONSTRAINT miClave PRIMARY KEY," _
& " NumMes2 LONG, Cliente STRING (25), ImpPedido CURRENCY," _
& " CONSTRAINT miClaveExt FOREIGN KEY (NumMes2) REFERENCES TMeses (NumMes))"

4 ¿Por qué el código no lleva control de errores? Porque desde el momento en que establecemos relaciones no podemos borrar la
tabla porque Access no nos dejará: nos dirá que no se puede borrar la tabla porque existen relaciones entre tablas. El proceso
sería, pues, primero eliminar la relación y después borrar la tabla. Como aún no sabemos cómo eliminar relaciones (¡pero lo
veremos en breve!) omito el control de errores, por ahora.

9
Visítame en http://siliconproject.com.ar/neckkito/
BORRAR UNA TABLA
Así como podemos crear una tabla con SQL también la
podemos borrar. Para ello utilizaremos la instrucción DROP.

El procedimiento es sencillo, y corresponde a la siguiente


estructura:

DROP TABLE nomTabla

Supongamos que queremos eliminar la tabla TProductos. El


código que nos realizaría lo anterior sería5:


Private Sub cmdBorraTProductos_Click()
On Error GoTo sol_err
Dim miSql As String

miSql = "DROP TABLE TProductos"

CurrentDb.Execute miSql
MsgBox "La tabla 'TProductos' se ha eliminado correctamente", vbInformation, "OK"
Salida:
Exit Sub
sol_err:
If Err.Number = 3376 Then
MsgBox "La tabla 'TProductos' no se puede eliminar porque no existe", _
vbCritical, "TABLA INEXISTENTE"
Else
MsgBox "Se ha producido el error " & Err.Number & " - " & Err.Description
End If
Resume Salida
End Sub

Como vemos en el código, si la tabla no existiera nos saltaría el error 3376. El código gestiona
dicho error a través del control de errores.

CREAR ÍNDICES (con CREATE INDEX)


Según nuestro amigo Access, un índice <<especifica el orden de los registros a los que se
tiene acceso desde las tablas de la base de datos y si se aceptan o no los registros duplicados,
lo que proporciona un acceso a los datos eficaz>>

Así pues, podemos, con ayuda de SQL, crear un índice (o varios) en una tabla.

Vamos a crearnos la tabla TProductos con el código para crear una BD más simple que hemos
aprendido prácticamente a principio de este capítulo; es decir, TProductos sólo con los campos
(sin ningún tipo de restricciones).

Si seguimos la definición expuesta anteriormente podemos fijarnos en que dice <<... y si se


aceptan o no los registros duplicados...>>.

Vamos a empezar a trabajar suponiendo que sí se aceptan registros duplicados. El objetivo es


crear un índice sobre los campos [CodProd] y [DescripProd] (lo que nos lleva a concluir que
5 Revisad la nota 4

10
Visítame en http://siliconproject.com.ar/neckkito/
podemos crear un índice sobre uno o más campos, ¿verdad? ).

ÍNDICES CON REGISTROS


DUPLICADOS
La estructura más simple para crear un índice de las
características que acabamos de mencionar sería:

CREATE INDEX miInidice ON nomTabla (nomCampo1,


…, nomCampoN)

Ergo nuestro código sería:


Private Sub cmdCreaIndice_Click()
Dim miSql As String

miSql = "CREATE INDEX miIndice ON TProductos (CodProd,DescripProd)"

CurrentDb.Execute miSql
MsgBox "Índices creados satisfactoriamente", vbInformation, "OK"
End Sub

Alguien podría pensar: “¿Y qué hemos conseguido con lo anterior?”. OK. En la tabla TProductos
yo he introducido varios registros, de la manera siguiente:

Si ahora cierro la tabla y la vuelvo a abrir me encuentro con lo siguiente:

Conclusión: que al haber convertido en índices los dos campos se produce, automáticamente,
una ordenación de la siguiente manera:

• Primero, se ordena [CodProd], en este caso alfabéticamente


• Segundo, se ordena [DescripProd], en este caso alfabéticamente.

¿Lo vemos? 

Pues ahora vamos a cambiar la ordenación del índice sobre el campo [DescripProd].

11
Visítame en http://siliconproject.com.ar/neckkito/
Aprovecharemos para ver cómo declaro explícitamente también la ordenación sobre el campo
[CodProd]. Nuestro código quedaría así:


Private Sub cmdCreaIndice2_Click()
Dim miSql As String

miSql = "CREATE INDEX miIndice ON TProductos (CodProd ASC,DescripProd DESC)"

CurrentDb.Execute miSql
MsgBox "Índices creados satisfactoriamente", vbInformation, "OK"
End Sub

Y si abro la tabla TProductos (¡es necesario borrarla y volverla a crear antes de realizar el
proceso!) los resultados son los siguientes:

Como observamos, el campo [CodProd] muestra una ordenación ascendente porque así lo
hemos indicado en la SQL, a través de ASC, mientras que [DescripProd] muestra una
ordenación descendente porque hemos indicado que así fuera a través de DESC.

Es decir, que la estructura así sería:

CREATE INDEX miInidice ON nomTabla (nomCampo1 [ASC|DESC], …, nomCampoN


[ASC|DESC])

PROHIBIR VALORES NULOS EN EL ÍNDICE


Podría interesarnos que no se permitieran valores nulos en el índice. En ese caso la estructura
de la SQL sería:

CREATE INDEX miInidice ON nomTabla (nomCampo1, …, nomCampoN) WITH


DISALLOW NULL

En definitiva, que deberíamos utilizar DISALLOW NULL

Nuestro código sería:

12
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdCreaIndiceDisallowNull_Click()
Dim miSql As String

miSql = "CREATE INDEX miIndice ON TProductos (CodProd ASC,DescripProd DESC)" _


& " WITH DISALLOW NULL"

CurrentDb.Execute miSql
MsgBox "Índices creados satisfactoriamente", vbInformation, "OK"
End Sub

Si ahora escribo, en TProductos, lo siguiente:

dejando en blanco [CodProd], e intento ir a un nuevo registro, obtengo lo siguiente:

y no puedo añadir más registros hasta que no introduzca información en el campo [CodProd].

IGNORAR VALORES NULOS EN EL ÍNDICE


La estructura para esta SQL sería:

CREATE INDEX miInidice ON nomTabla (nomCampo1, …, nomCampoN) WITH


IGNORE NULL

Es decir, que deberíamos utilizar IGNORE NULL

Nuestro código sería el siguiente:


Private Sub cmdCreaIndiceIgnoreNull_Click()
Dim miSql As String

miSql = "CREATE INDEX miIndice ON TProductos (CodProd ASC,DescripProd DESC)" _


& " WITH IGNORE NULL"

13
Visítame en http://siliconproject.com.ar/neckkito/
CurrentDb.Execute miSql
MsgBox "Índices creados satisfactoriamente",
vbInformation, "OK"
End Sub

La ordenación de nuestra tabla se mostraría así:

ÍNDICE CON REGISTROS NO DUPLICADOS


Podemos especificar que nuestro índice no admita valores duplicados. Para ello la estructura de
la SQL sería:

CREATE UNIQUE INDEX miIndice ON nomTabla (nomCampo)

Por ejemplo, en TProductos queremos que el campo [CodProd] sea índice sin duplicados.
Deberíamos programar el siguiente código:


Private Sub cmdCreaIndiceSinDuplicados_Click()
Dim miSql As String

miSql = "CREATE UNIQUE INDEX miIndice ON TProductos (CodProd)"

CurrentDb.Execute miSql
MsgBox "Índice creado satisfactoriamente", vbInformation, "OK"
End Sub

Lógicamente, si nuestro índice no admite duplicados y es importante para nosotros (y nuestra


BD) lo más lógico sería que no dejáramos introducir valores nulos. En consecuencia
deberíamos cambiar ligeramente la SQL, así:


Private Sub cmdCreaIndiceSinDuplicados2_Click()
Dim miSql As String

miSql = "CREATE UNIQUE INDEX miIndice ON TProductos (CodProd)" _


& " WITH DISALLOW NULL"

CurrentDb.Execute miSql
MsgBox "Índice creado satisfactoriamente", vbInformation, "OK"

End Sub

14
Visítame en http://siliconproject.com.ar/neckkito/
CREAR UN ÍNDICE CON CLAVE PRIMARIA
Si queremos crear un índice asignando una clave principal
(ojo, sólo puede existir un único índice que sea clave
principal) seguiremos la estructura que ya conocemos...

CREATE INDEX miIndice ON nomTabla (nomCampo)


WITH PRIMARY

… añadiendo WITH PRIMARY y teniendo en cuenta que si el


campo es clave principal ya no debemos preocuparnos por
los valores nulos o los registros duplicados (la clave
principal no los admite por definición).

Nuestro código quedaría así:


Private Sub cmdCreaIndicePrimary_Click()
Dim miSql As String

miSql = "CREATE INDEX miIndice ON TProductos (CodProd)" _


& " WITH PRIMARY"

CurrentDb.Execute miSql
MsgBox "Índice creado satisfactoriamente", vbInformation, "OK"
End Sub

¿Fácil, no?

ELIMINAR ÍNDICES
Para eliminar un índice debemos utilizar la estructura:

DROP INDEX nombreIndice ON nomTabla

Vamos a suponer que hemos creado el índice miIndice sobre la tabla TProductos utilizando
cualquiera de los métodos explicados anteriormente. Vamos a eliminar ese índice a través del
siguiente código:


Private Sub cmdBorraIndice_Click()
Dim miSql As String

miSql = "DROP INDEX miIndice ON TProductos"

CurrentDb.Execute miSql
MsgBox "Índice eliminado correctamente", vbInformation, "OK"
End Sub

Y listo.

MODIFICAR UNA TABLA


Una vez creada ya la tabla, o teniendo una tabla existente, podemos modificar tanto campos

15
Visítame en http://siliconproject.com.ar/neckkito/
como índices.

Como veremos, las diferentes operaciones tienen una


estructura muy similar. Únicamente hay que saber
pronunciar “las palabras mágicas” para que el sortilegio
surja efecto 

Veamos pues qué podemos hacerle a nuestra tabla.

AÑADIR CAMPOS
La estructura para añadir un campo sería:

ALTER TABLE nomTabla ADD COLUMN nomCampo tipoCampo

Supongamos que en nuestra tabla TMeses queremos añadir el campo [Estacion], de tipo texto
con una longitud máxima de 10 caracteres. Nuestro código debería ser así:


Private Sub cmdModifAñadeCampos_Click()
Dim miSql As String

miSql = "ALTER TABLE TMeses ADD COLUMN Estacion STRING(10)"

CurrentDb.Execute miSql
MsgBox "El campo se ha creado correctamente", vbInformation, "OK"
End Sub

BORRAR CAMPOS
¡Caramba! Nos hemos equivocado y debemos borrar el campo [Estacion] que acabamos de
crear. ¿Cómo lo hacemos?

Pues la estructura de la SQL sería:

ALTER TABLE nomTabla DROP COLUMN nomCampo

Es decir, que escribiríamos:


Private Sub cmdModifBorraCampos_Click()
Dim miSql As String

miSql = "ALTER TABLE TMeses DROP COLUMN Estacion"

CurrentDb.Execute miSql
MsgBox "El campo se ha borrado correctamente", vbInformation, "OK"
End Sub

De nuevo, ¿fácil, no?

16
Visítame en http://siliconproject.com.ar/neckkito/
CREAR ÍNDICES (con ALTER TABLE)
Al igual que hemos hecho con los campos podemos
modificar los índices de las tablas. De hecho, si os fijáis, es
como “otro sistema paralelo” a lo que comentábamos en
apartados anteriores utilizando, en aquel caso, “CREATE
INDEX”

CREAR UNA CLAVE PRINCIPAL

Vamos, manualmente, a eliminar la clave principal de la


tabla TMeses. Ahora la volveremos a crear, pero a través de
SQL.

La estructura para realizar esto sería:

ALTER TABLE nomTabla ADD CONSTRAINT miClave PRIMARY KEY(nomCampo)

Es decir, siguiendo el ejemplo:


Private Sub cmdModifCreaClavePrincipal_Click()
Dim miSql As String

miSql = "ALTER TABLE TMeses ADD CONSTRAINT miClave PRIMARY KEY(NumMes)"

CurrentDb.Execute miSql
MsgBox "Clave principal creada correctamente", vbInformation, "OK"
End Sub

CREAR UNA CLAVE EXTERNA


Bueno... Ya hemos visto cómo crear una clave principal. Veamos cómo podemos crear una
clave externa.

Vamos a asegurarnos que creamos desde cero la tabla TPedidos, simple y llana (si ya la
tuviéramos creada la borramos), eso sí, con una clave principal. Para crearla podemos ejecutar
la siguiente SQL:


miSql = "CREATE TABLE TPedidos (Id LONG CONSTRAINT miClave PRIMARY KEY," _
& " NumMes LONG, Cliente STRING (25), ImpPedido CURRENCY)"

Vamos a crear una clave externa con el campo [NumMes] entre las tablas TMeses y TPedidos.

La estructura de dicha construcción sería:

ALTER TABLE nomTabla ADD CONSTRAINT miClaveExt FOREIGN KEY


nomCampoInterno REFERECES nomTablaExterna(nomCampoExterno)

Es decir:

17
Visítame en http://siliconproject.com.ar/neckkito/

Private Sub cmdModifCreaClaveExterna_Click()
Dim miSql As String

miSql = "ALTER TABLE TPedidos ADD CONSTRAINT miClaveExt FOREIGN KEY (NumMes)" _
& " REFERENCES TMeses(NumMes)"

CurrentDb.Execute miSql
MsgBox "La clave externa se ha creado correctamente", vbInformation, "OK"
End Sub

Os recuerdo que para comprobar la “efectividad” de nuestro código basta con abrir la ventana
relaciones y mostrar estas dos tablas: debería aparecernos la relación.

MODIFICAR EL TIPO DE RELACIÓN


Sabemos (y si no lo sabemos lo aprenderemos aquí) que cuando creamos una relación, al
exigir integridad referencial, podemos realizar dos operaciones:

• Actualizar campos en cascada


• Eliminar campos en cascada

¿Alguien se ha perdido? Espero que esta imagen sirva para refrescar memorias:

También podemos definir estas dos propiedades de la relación en nuestra SQL, realizando una
pequeña modificación en la estructura. Es decir:

ALTER TABLE nomTabla ADD CONSTRAINT miClaveExt FOREIGN KEY


nomCampoInterno REFERECES nomTablaExterna(nomCampoExterno) [ON UPDATE
CASCADE | ON DELETE CASCADE]

18
Visítame en http://siliconproject.com.ar/neckkito/
Sin embargo, si intentamos ejecutar un código utilizando
DoCmd.RunSQL o CurrentDb.Execute no hacemos más que
obtener errores, o bien de la cláusula CONSTRAINT o bien
de la propia instrucción SQL (y eso lo sé por experiencia
propia... je, je...).

¿Qué pasa? Pues que el motor Jet de Access no es capaz de


ejecutar esos dos comandos (UPDATE y DELETE CASCADE).
¿Curioso, verdad? 

La solución pasa por utilizar una combinación de SQL y


conexión ADO para que nuestro código no falle más que
una escopeta de feria. Es decir, que nuestros códigos
deberían quedar de la siguiente manera, para actualizar
campos en cascada:


Private Sub cmdModifCreaClaveExterna_Click()
Dim miSql As String

miSql = "ALTER TABLE TPedidos ADD CONSTRAINT miClaveExt FOREIGN KEY (NumMes)" _
& " REFERENCES TMeses(NumMes) ON UPDATE CASCADE"

CurrentProject.Connection.Execute miSql

MsgBox "La clave externa se ha creado correctamente", vbInformation, "OK"


End Sub

Fijaos que ahora la “orden” de ejecución pasa por CurrentProject.Connection.Execute

Para borrar campos en cascada tendríamos:


Private Sub cmdModifCreaClaveExterna_Click()
Dim miSql As String

miSql = "ALTER TABLE TPedidos ADD CONSTRAINT miClaveExt FOREIGN KEY (NumMes)" _
& " REFERENCES TMeses(NumMes) ON DELETE CASCADE"

CurrentProject.Connection.Execute miSql

MsgBox "La clave externa se ha creado correctamente", vbInformation, "OK"


End Sub

Y, lógicamente, si queremos ambas acciones a la vez la SQL sería:


miSql = "ALTER TABLE TPedidos ADD CONSTRAINT miClaveExt FOREIGN KEY (NumMes)" _
& " REFERENCES TMeses(NumMes) ON UPDATE CASCADE ON DELETE CASCADE"

 Importante: ON UPDATE CASCADE y ON DELETE CASCADE sólo pueden ser aplicadas a la


clave externa. En otro caso obtendremos error.

19
Visítame en http://siliconproject.com.ar/neckkito/
AÑADIR UNA REGLA DE VALIDACIÓN
Podemos modificar el campo de una tabla para validar las
entradas de los usuarios. Para ello utilizaríamos la
estructura:

ALTER TABLE nomTabla ADD CONSTRAINT


nomCampo CHECK (<reglaDeValidacion>)

Supongamos que queremos crear una regla de validación


sobre el campo [ImpVta] en nuestra tabla TVentas, de
manera que el usuario no pueda introducir ventas
negativas. Ergo nuestra condición será que el importe de la
venta sea mayor o igual a cero.

¿Cuál es el “pero”? En realidad ya no hay “pero” para nosotros. La característica de esta


modificación de las propiedades del campo es que su ejecución pasa obligatoriamente por la
combinación de SQL con una conexión ADO, como ya hemos visto en el apartado anterior.

Nuestro código pues quedaría así:


Private Sub cmdCreaReglaValidacion_Click()
Dim miSql As String

miSql = "ALTER TABLE TVentas ADD CONSTRAINT ImpVta CHECK (ImpVta>=0)"

CurrentProject.Connection.Execute miSql
MsgBox "Regla de validación creada correctamente", vbInformation, "OK"
End Sub

 Nota: si, una vez ejecutada la SQL, abrimos TVentas en vista diseño y sacamos las
propiedades del campo [ImpVta] veremos que no hay ninguna regla de validación especificada.
Eso no debe preocuparnos: si intentamos escribir un registro con una venta en negativo nos
saltará un aviso de que ese valor no cumple con la regla de validación.

ELIMINAR ÍNDICES
El proceso de eliminar índices sería muy parecido a lo que ya conocemos con ADD, pero
utilizando, en su lugar, DROP.

Ergo la estructura que deberíamos utilizar sería:

ALTER TABLE nomTabla DROP CONSTRAINT nomIndice

Para practicar un poco eliminaremos, de manera manual, la clave principal de la tabla TMeses.

Una vez eliminada la clave principal de esa tabla la volvemos a crear a través de una SQL,
dándole nombre al índice

Eso ya deberíamos saber cómo hacerlo, pero para “refrescar memorias” os indico aquí cómo
sería la SQL

20
Visítame en http://siliconproject.com.ar/neckkito/

miSql = "ALTER TABLE TMeses ADD CONSTRAINT miSuperClavePpal PRIMARY KEY(NumMes)"

Como podemos ver, mi índice se llama “miSuperClavePpal”,


y este será el que eliminemos.

Ahora sí, ya podemos escribir el código, que sería:


Private Sub cmdModifBorraIndices_Click()
Dim miSql As String

'Esta SQL crea una clave principal en TMeses.NumMes


' miSql = "ALTER TABLE TMeses ADD CONSTRAINT miSuperClavePpal PRIMARY
KEY(NumMes)"
'--------------------------------------------------

miSql = "ALTER TABLE TMeses DROP CONSTRAINT miSuperClavePpal"

CurrentDb.Execute miSql
MsgBox "Clave principal eliminada correctamente", vbInformation, "OK"
End Sub

Si abrimos, tras su ejecución, la tabla TMeses, veremos que nuestra clave principal ha
desaparecido.

PARA FINALIZAR EL CAPÍTULO (¡Y EL CURSO!)


Y, como diría aquel, “hasta aquí hemos llegado”. Hemos hecho un “viaje cultural” al país de
SQL, y hemos visto consultas de selección, consultas de acción, consultas para crear tablas,
modificarlas, trabajar con propiedades, con índices...

Siguiendo el símil, hemos visto “los principales monumentos en nuestro viaje”, pero conocer
SQL a fondo... eso sólo lo conseguiremos si nos “quedamos a vivir” en la ciudad.

Afortunadamente tenemos otros recursos, como la ayuda de Access, la referencia del


programador de MSDN, los foros especializados... Es decir, que con un poco de “cabezonería”
podemos encontrar (y profundizar) mucha información sobre SQL. Vamos, que aburrirnos no
nos vamos a aburrir.

Tampoco debemos perder de vista que este curso está orientado a trabajar en Access lo cual
nos implica, entre otras, dos cosas:

La primera, que hay que tener presente que SQL es utilizado en otros SGBD 6, y operaciones
que funcionan en unos no funcionan en otros. No nos volvamos “excesivamente locos”
probando y probando algo que no nos funciona sin cercionarnos de que, efectivamente, eso sí
funciona en nuestro SGBD.

Segunda, que nuestro entorno de trabajo es Access, y, más en concreto, Access y VBA para

6 SGBD: Sistema Gestor de Bases de Datos

21
Visítame en http://siliconproject.com.ar/neckkito/
Access. Eso nos da la ventaja de que podemos trabajar con muchas herramientas que se
complementan o se sustituyen unas a otras. Para clarificar lo que quiero decir, recordemos que
tenemos las consultas como objeto en Access, fácilmente manipulables a nivel visual.
Si tenemos problemas con la programación de alguna SQL
quizá podríamos plantearnos el utilizar una consulta-objeto
de Access para realizar la misma función, por ejemplo.

Quisiera también remarcar que existe una estrecha


“simbiosis” entre SQL y ADO, en la cual no hemos
profundizado en este curso porque eso excedería de las
pretensiones del manual. Sin embargo, creo que es
interesante dejarlo apuntado para aquellos que quieran
profundizar sobre el tema, porque, dadas las características
de ADO, no hablamos de hacer “un viaje a otra ciudad”,
sino más bien de “realizar un viaje a otro continente” 

Espero que este manual os haya sido de utilidad. Para quejas y demás poneos en contacto con
mi editor (¡Anda, pero si no tengo editor! :P).

Un saludo, y...

¡suerte!

22
Visítame en http://siliconproject.com.ar/neckkito/

También podría gustarte