Está en la página 1de 117

USO DE MAQUINAS VIRTUALES

El concepto de máquina virtual


La existencia de diferentes sistemas operativos acarrea, como una de las principales desventajas en el
ámbito del desarrollo de software, la necesidad de tener que crear para un mismo software diferentes
versiones adecuadas para cada uno de los sistemas operativos.
Sin embargo, esto no ocurre siempre, ya que la creación de una versión del mismo software para
diferentes plataformas supone un esfuerzo económico para las empresas que no siempre es rentable.
Esta situación produce problemas en el momento que se quiere utilizar un determinado software que
solo se ejecuta para un determinado sistema operativo que no coincide con el que dispone el usuario.

Para poder crear y ejecutar una máquina virtual, es necesario un software especial encargado de llevar a
cabo la simulación. Actualmente, las herramientas más utilizadas para la creación y gestión de máquinas
virtuales son Virtual Box y VMware .

La herramienta VMware dispone de una versión gratuita con las características y funciones necesarias
para un usuario particular, tales como la creación de una máquina virtual, y una versión de pago
orientada a empresas que añade funciones y características más avanzadas tales como la posibilidad
de virtualizar sistemas operativos y gestionarlos a través de la red como si se tratase de una nube.

Por otro lado, Virtual Box es una herramienta de código abierto, totalmente gratuita y disponible para
cualquier sistema operativo, propiedad de Oracle. No ofrece las mismas características avanzadas
orientadas hacia las empresas de VMware, pero sí dispone de todas las funciones necesarias para crear y
ejecutar máquinas virtuales. E incluso dispone de algunas funcionalidades propias como la capacidad de
ejecutar varias máquinas virtuales a la vez.
Con respecto a la eficiencia y rendimiento, VMware presenta un rendimiento mejor que Virtual Box en
el uso de la memoria y de la CPU. Sin embargo, con respecto al rendimiento del disco duro ambas
herramientas son similares.

Creación y configuración de una máquina


virtual
Virtual Box es un programa de software libre que permite la instalación de otro sistema operativo,
dentro del sistema existente, y la interacción con el mismo. De esta forma, es posible instalar programas
diseñados para los sistemas operativos clientes. En esta sección se va a describir cómo se instala Virtual
Box, además de cómo se puede crear y configurar de una manera básica una máquina virtual.
Instalación de Virtual Box
Para instalar Virtual Box, en primer lugar, hay que descargar el software desde la siguiente dirección:

Página web de descargas de Virtual Box, https://www.virtualbox.org/wiki/Downloads

Durante esta unidad, se os enseña cómo descargar e instalar la versión 5.1.22. Sin embargo, es
aconsejable que os descarguéis la última versión disponible. En las explicaciones siguientes se utilizará
la versión para Windows —para el resto de sistemas operativos el proceso es similar—. En la página de
descargas, se pulsa sobre el enlace correspondiente al sistema operativo sobre el que se quiere instalar
—en este caso sobre Windows hosts— y comienza la descarga. Como no se que versión de MacOS
tienes, te lo pongo primero para las versiónes hasta el SIERRA, y después para del SIERRA para
adelante.

Una vez que se ha bajado el archivo ejecutable, se pulsa sobre el instalador para que comience la
ejecución del asistente.
Se da doble click cobre el archivo *.dmg

Cuando se termine de montar la imagen, aparecera la siguiente imagen, y le damos en


VirtualBox.pkg:
Nos va preguntar si queremos instlar, y le damos a si (continue):
Y ahora a install:

A meter la clave de administrador:

Y ya se termino la intalacion de VirtualBox.


En las versiones posteriores de SO SIERRA, puede dar un fallo la principio de la instalación, si
es asi, se tendría que dar permisos al fichero, por que ha sido bloqueado por el sistema. Se haría asi:
Creación y configuración de una máquina virtual
Para crear una máquina virtual, lo primero que se necesita es una imagen del sistema operativo que se
desea virtualizar. Para ilustrar la creación de la máquina virtual, se va a utilizar Lubuntu . Se trata de una
distribución de Linux pensada para máquinas que disponen de pocos recursos software.
La descarga de Lubuntu se realizar desde la siguiente dirección: página web de
Lubuntu, http://lubuntu.net/
El proceso esta realizado para un Windows, pero es exactamente igual para un MAC.

Una vez descargado Lubuntu, se va a realizar la creación de la máquina virtual. Para ello, se abre
Virtual Box pulsando sobre el icono que habrá en el escritorio, o bien en la lista de programas, o bien
sobre el archivo VirtualBox.exe que se encuentra en la carpeta de instalación C:\Program
Files\Oracle\VirtualBox. En la interface principal de Virtual Box, se pulsa sobre el icono “Nueva” para
crear una nueva máquina virtual. Como resultado, se abre el asistente.

En el asistente, se inserta un nombre para la máquina virtual “Lubuntu-Virtualizado”, se elige como


tipo “Linux” y como versión “Ubuntu (64-bit)”. Por último, se pulsa sobre el enlace “Next”
En la siguiente ventana, se debe elegir cuánta memoria se va asignar al sistema virtualizado (memoria
que se quita a Windows), y se pulsa sobre “Next”. Se puede seleccionar la recomendación que indica el
asistente.

En la siguiente ventana, se verifica que se ha seleccionado la opción de “Crear disco virtual ahora” y
se pulsa sobre “Crear”.
En la siguiente ventana, se elige el tipo de archivo que se desea utilizar para el disco duro virtual. Se
puede dejar la recomendación que indica el asistente y se pulsa sobre “Next”.

En la siguiente ventana, se selecciona cómo se realizará el almacenamiento en el disco duro. Se


mantiene la recomendación que indica el asistente y se pulsa sobre “Next”.
En la siguiente ventana, se pregunta sobre dónde se debe ubicar el archivo. Por defecto, se guardará
en: C:\Users\nombre_usuario\VirtualBox VMs\Nombre-Máquina Virtual.
Se puede cambiar pulsando sobre el icono en forma de carpeta. Asimismo, en esta ventana se puede
configurar el tamaño máximo del disco duro. Se puede dejar el que indica por defecto el asistente.

Al pulsar sobre “Crear”, aparece la ventana de Virtual Box actualizada con la máquina que se ha
configurado.
La máquina virtual aún no se ha creado, lo que se ha hecho es configurar las características de la
máquina. Antes de crear la máquina es necesario configurar algunas más, lo cual se realiza
seleccionando la máquina y pulsando sobre el enlace “Configuración” que genera la ventana mostrada.

En esta ventana, se pulsa sobre el icono “Almacenamiento” y aparece la pestaña que se muestra
En esta pestaña, se pulsa en el icono “CD” del cuadro “Árbol de almacenamiento” del “Controlador:
IDE” que aparece vacío.

En el cuadro “Atributos”, se pulsa sobre el icono del “CD” y aparece una lista desplegable. Se
selecciona “Seleccione archivo de disco óptico virtual…”
Aparece un navegador de archivos y se selecciona el archivo de “Lubuntu” que se ha descargado
previamente.

Tras realizar la selección, la pantalla aparece asi.

A continuación, se pulsa sobre “OK” para volver a la interface principal de Virtual Box en la que se
puede observar que se ha actualizado el cuadro almacenamiento (figura 1.26.).
Ahora, se va a crear el sistema virtualizado. Desde la pantalla principal, se pulsa sobre el icono de
“Iniciar” para que se cargue el sistema. Aparecerán diferentes mensajes y pantallas de instalación. Son
mensajes similares a los que se mostrarían si se estuviera instalando realmente el sistema operativo.
Estos mensajes solo aparecerán la primera vez que se cargue el sistema virtualizado, no lo harán en las
restantes ocasiones.
Se selecciona “Instalar Lubuntu”.

Se selecciona el idioma.
Se seleccionan paquetes de software adicional para su instalación.

Y el tipo de instalación.
Se pulsa sobre “Continuar” en la pantalla que aparece a continuación.

Se selecciona nuestra localización.


Y la disposición del teclado.

Se configura el usuario .
Se realiza la instalación de Lubuntu.

Se completa la instalación y se pulsa sobre “Reiniciar ahora” (figura 1.37.).


Se carga el sistema operativo.

En la figura 1.39., se puede observar el sistema operativo virtualizado.


Opciones de cierre de la máquina virtual

- Cuando se cierra la máquina virtual, aparecen varias opciones


- Guardar el estado de la máquina: cierra la MV guardando el estado en el que se encuentra en
ese momento, de modo que, al volver a iniciarla, continuará justo en el punto donde se cerró.
No se pierde ningún tipo de información.
- Enviar señal de apagado: es el apagado ACPI.
- Apagar la máquina: equivale a cerrar la máquina sin guardar el estado, por lo que el SO no
se cerrará correctamente y al iniciar la MV posteriormente, el SO hará los chequeos
previstos cuando este no se cierre correctamente. Es posible que se pierdan datos, por lo que
debe usarse como último recurso cuando la MV se ha bloqueado.

La próxima vez que se quiera ejecutar el sistema virtual bastará con seleccionar la máquina virtual y
pulsar sobre “Iniciar” en la pantalla principal de Virtual Box.

Importación de una máquina virtual


Las máquinas virtuales son archivos que tienen el software de un sistema operativo virtualizado y
permiten ejecutarlo en un sistema de virtualización. Físicamente son archivos que pueden tener varias
extensiones, dependerán de la forma en la que se ha exportado la máquina virtual: vmdk, ova, .vdi, etc.
Hay repositorios gratuitos con máquinas virtuales ya preparadas para descargar e instalar:

• OSBoxes, http://www.osboxes.org/
• VirtualBoxes, https://virtualboxes.org/

Para ilustrar cómo cargar una máquina virtual, es posible descargar una de una distribución de
Lubuntu desde el siguiente enlace:
SourceForge, https://sourceforge.net/projects/virtualimages/files/Lubuntu/
Una vez descargada, se importa en Virtual Box. Para ello, se selecciona en el menú la opción:
“Archivos”> “Importar Servicio Virtualizado” y se selecciona la máquina virtual que se ha descargado.
A continuación, se selecciona la máquina virtual que se quiere importar.

A continuación, se pulsa sobre “Next” y aparecerá un resumen de la máquina virtual que se va a


importar. Se debe marcar la opción “Reiniciar la dirección MAC de todas las tarjetas de red” y pulsar
sobre “Importar”.
Se muestra una ventana con el estado del proceso de importación. El tiempo varía dependiendo de las
características de la máquina.

Una vez importada la máquina virtual, debería aparecer en la parte de la izquierda de la pantalla una
nueva máquina virtual de nombre Lubuntu13-4.
Se pulsa sobre Lubuntu 13-4 y se selecciona la opción de “Configuración”.

A continuación, se selecciona en “Sistema” la opción “Placa Base” para fijar la cantidad de memoria
de la máquina virtual. Dependiendo de la memoria RAM que tenga el ordenador que se está usando, se
puede aumentar o disminuir el valor. 512 Mb deberían ser suficientes, pero se recomiendan 1024 Mb
para que esta funcione con fluidez.
Se selecciona la opción “Pantalla” y, en esta pestaña, se ajusta la cantidad de memoria para la tarjeta
gráfica: 32 Mb deberían ser suficientes.

A continuación, se pulsa en el botón “OK”. En la pantalla principal de Virtual Box, se selecciona la


máquina virtual “Lubuntu 13-4” y se pulsa en el botón “Iniciar”.
A continuación, se ejecuta la máquina virtual “Lubuntu 13-4” y ya se podrá trabajar con ella. Se
selecciona la cuenta “Guest Account” y se pulsa sobre “Login”.

A continuación, aparece el escritorio del sistema operativo virtualizado.


Algunas observaciones que se deben tener en cuenta:
1.Para que funcionen las máquinas virtuales es necesario que el PC permita la "Virtualización
Hardware". Esta es una opción que suele encontrarse en la BIOS del equipo. Puede llamarse
"Virtualización Hardware" o similar en función del modelo del PC.
2.Para confirmar que el PC soportará la virtualización, se puede usar la siguiente página:
https://www.grc.com/securable.htm
3.Si la importación da un error estilo ERROR_ZIP_CODE, muy posiblemente el archivo .ova
esté corrupto. Para solucionarlo, se debe descargar de nuevo la máquina virtual.
4.Una vez importada la máquina, si al intentar encenderla no lo hiciera, se debe revisar el punto
1.
5.También es importante que la máquina virtual esté preparada para el sistema operativo
anfitrión, es decir, saber si es de 64 bits o 32 bits.
Se acaba de describir cómo cargar una máquina virtual mediante la opción de “Importar un servicio
virtualizado”, puesto que la máquina se encontraba en un archivo de tipo .ova. Sin embargo, las
máquinas virtuales pueden distribuirse en otros formatos diferentes
La shell de comandos de Linux. Creación de
scripts
La shell de comandos
Linux es un sistema operativo similar a Unix que se distribuye como software libre.

Para ilustrar los contenidos de esta sección, se va utilizar una distribución de Linux denominada
Lubuntu que se caracteriza por exigir pocos recursos hardware para su ejecución. Linux dispone de una
aplicación denominada intérprete de comandos o Shell de comandos que permite interactuar al usuario
con el sistema operativo mediante la ejecución de comandos o sentencias.

Hay diferentes tipos de intérpretes que se diferencian en la sintaxis de los comandos y en la forma de
interaccionar con el usuario. Para acceder al intérprete de comandos, se pulsa sobre Ctrl+Alt+T o bien
se pulsa en un icono que aparece en la parte inferior izquierda, dentro de la barra de herramientas
inferior del escritorio de Lubuntu.
Algunas características generales son:
El terminal muestra en pantalla un indicador de línea de órdenes esperando que el usuario
introduzca una. El indicador finaliza con un carácter $ en el caso de usuarios normales y con #
en el caso del superusuario.
Al comienzo de la línea de órdenes aparece el usuario de la forma usuario@nombre: ~$ (figura
1.61.)
Cuando se escribe un comando para que se ejecute, hay que pulsar la tecla “Enter”.
Los comandos hay que teclearlos exactamente. En este sentido, las letras mayúsculas y
minúsculas se consideran caracteres diferentes.
El terminal siempre está dentro de una carpeta específica y puede navegar hasta otras carpetas
y gestionar los archivos. Siempre se abre desde la carpeta personal del usuario.
Comandos principales
Cambio de directorio

Para cambiar de directorio se usa el comando cd directorio_destino. En particular para ir al


directorio superior se usa cd .. y cd sin argumentos para volver a la carpeta personal.

El comando pwd imprime la ruta del directorio actual .


El comando ls muestra los nombres de los archivos y subdirectorios contenidos en el directorio en el
que se está.

Admite un conjunto de opciones:


ls-R lista recursivamente los subdirectorios encontrados.
ls -a muestra todos los archivos, incluyendo algunos que están ocultos para el usuario —
aquellos que comienzan por un punto—. El archivo punto . indica el directorio actual y el doble
punto .. el directorio padre, que contiene al actual.
ls -c muestra ordenando por día y hora de creación.
ls -t muestra ordenando por día y hora de modificación.
ls -r muestra el directorio y lo ordena en orden inverso.
ls subdir muestra el contenido del subdirectorio subdir.
ls -l filename muestra toda la información sobre el archivo.
ls -color muestra el contenido del directorio coloreado: verde para los ejecutables, azul para las
carpetas, fucsia para las imágenes, rojo para los comprimidos, etc.
ls -l muestra toda la información de cada archivo, incluyendo protecciones, tamaño y fecha de
creación o del último cambio introducido, etc.
Creación de ficheros:
El comando touch actualiza los registros de fecha y hora con la fecha y hora actual de los ficheros
indicados como argumento. Si el fichero no existe, el comando touch lo crea. Su uso más frecuente es
para crear archivos.
La sintaxis del comando touch sigue la forma: touch fichero
Además, touch ejemplo.txt crea el archivo ejemplo.txt en el directorio actual, si este no existiera.
Creación de subdirectorios:
El comando mkdir permite crear un nuevo subdirectorio. La sintaxis es mkdir subdir1 donde subdir es el
nombre del directorio que se va a crear.

El comando borra uno o más directorios del sistema siempre que estos subdirectorios estén vacíos. La
sintaxis es rmdir subdir1 donde subdir es el nombre del directorio que se va a eliminar .
La sintaxis del comando es cp file1 file2 donde file1 y file2 indican el nombre de un archivo o la ruta
al mismo. Su ejecución hace una copia de file1 y lo llama file2. Si file2 no existía, lo crea con los
mismos atributos de file1 y en caso de existir su contenido es sustituido por el de file1. El archivo file2
estará en el mismo directorio que file1.

También se puede usar como cp file1 file2 namedir que hace copias de file1 y file2 en el directorio
namedir.
La sintaxis es mv file1 file2 y realiza la misma función que cp, pero eliminando el archivo original.
Desde la visión del usuario, se cambia el nombre a file1 por file2.

Si los nombres que aparecen son de directorios, entonces el comando mv namedir1 namedir2 cambia
el nombre del subdirectorio namedir1 por namedir2.
mv file1 file2 namedir traslada uno o más archivos (file1, file2, etc.) al directorio namedir
conservando el nombre.
El comando rm elimina uno o más archivos de un directorio en el cual tengamos permiso de escritura.
La sintaxis es rm file1 file2. En el comando, se pueden utilizar los caracteres de sustitución * y ?
Si se usa la opción -i, se pide confirmación para borrar cada archivo de la lista especificada: rm -i
file1 file2.

Un mismo archivo puede estar repetido con más de un nombre. También es posible tener un mismo
archivo con varios nombres distintos y poder acceder a él desde más de un directorio. Esto último se
denomina enlaces múltiples a un archivo y la forma de crearlo es mediante el comando ln: ln file1
file2.
De esta forma el archivo file1 tiene dos nombres: file1 y file2. Además, este comando advierte
previamente si el nombre file2 está ocupado y en este caso no se ejecuta. En el siguiente ejemplo se crea
un enlace al archivo Ejemplo.abw.
Los archivos enlazados a otro se borran como los archivos normales, de manera que si se borra el
archivo original permanece su contenido en los archivos enlazados.
Mediante el comando file se puede obtener información acerca de un archivo: file fich

En Linux, los archivos tienen asociados permisos que indican las operaciones que le está permitido
realizar a un usuario sobre los mismos. Los permisos de cada archivo se pueden ver con el
comando ls -l. Para cambiar los permisos de un archivo se emplea el comando chmod [quien] oper,
permiso, files donde:
Quien:
Indica a quién afecta el permiso que se desea cambiar. Es una combinación de las letras u para el
usuario, g para el grupo del usuario, o para los otros usuarios, y a para todos los anteriores. Si no se
indica nada, se supone a.
Oper:
Indica si el permiso se da usando un + o se quita usando un -.
Permiso:
Indica el permiso que se quiere dar o quitar. Es una combinación de las
letras: r (leer), w (escribir), x (ejecutar). Estos permisos son diferentes cuando se aplican a
directorios: r da la posibilidad de ver el contenido del directorio con el comando ls; el permiso w da la
posibilidad de crear y borrar archivos en ese directorio; el permiso x autoriza a buscar y utilizar un
archivo concreto.
Files:
Nombres de los archivos o directorios cuyos modos de acceso se quieren cambiar.
En el siguiente ejemplo, se cambian los permisos al archivo Ejemplo.abw

El comando chown se emplea para cambiar de propietario a un determinado conjunto de


archivos: chown newowner file1 file2...

Solo lo puede emplear el actual propietario de los mismos. Los nombres de propietario se encuentran
almacenados en el archivo /etc/passwd

El comando chgrp se emplea para cambiar el grupo al que pertenece un archivo: chgrp newgroup
file1 file2... Los grupos de usuarios están almacenados en el archivo /etc/group.

El comando cat filename permite visualizar el contenido de uno o más archivos de forma no
formateada y también permite copiar uno o más archivos como apéndice de otro ya existente:
cat filename: saca por pantalla el contenido del archivo filename.
cat file1 file2…: saca por pantalla, secuencialmente y según el orden especificado, el
contenido de los archivos indicados.
cat file1 file2 >file3: El contenido de los archivos file1 y file2 es almacenado en file3.
cat file1 file2 >>file3: el contenido de file1 y file2 es añadido al final de file3.
cat >file1: Acepta lo que se introduce por el teclado y lo almacena en file1 (se crea file1). Para
terminar, se emplea <ctrl>d
Alternativamente, se puede usar el editor vi. El editor puede encontrarse en dos estados o modos:
En el modo de comandos, vi está esperando alguna orden (por tanto, interpreta lo que se
escribe como órdenes).
En el modo de edición, vi está esperando que se escriba el texto del fichero (por tanto,
interpreta lo escrito como texto).
Cuando se entra en vi, está en modo de comandos. Para pasar al modo de edición, se puede pulsar i
(insertar), a (añadir), o (añadir una línea). Para pasar al modo de comandos, se puede pulsar Escape o
Suprimir.
Algunos comandos de vi son:
i insertar antes del cursor
a añadir detrás del cursor
o añadir una línea en blanco
x borrar un carácter
j borrar el final de línea (une dos líneas)
dd borra la línea completa
u deshacer la última edición
:q salir
:q! salir sin guardar
:w guardar
:wq guardar y salir
:set nu muestra números de línea
:set nonu oculta números de línea
Los comandos more y less permiten visualizar un archivo pantalla a pantalla. El número de líneas por
pantalla es de 23 de texto más una última línea de mensajes, donde aparecerá la palabra more. Cuando
se pulsa la barra espaciadora se visualizará la siguiente pantalla. Para salir de este comando se
pulsa <ctrl>d o q. La sintaxis es more file.

El comando less permite el desplazamiento a lo largo del texto empleando las teclas de cursores
pudiendo ir hacia arriba o abajo de un archivo. La sintaxis es less file.

El comando grep 'conjuntocaracteres' file1 file2 file3 busca una palabra, clave o frase en un
conjunto de directorios, indicando en cuáles de ellos la ha encontrado. Busca archivo por archivo, por
turno, imprimiendo aquellas líneas que contienen el conjunto de caracteres buscado. Si el conjunto de
caracteres está compuesto por dos o más palabras separadas por un espacio, se escribe entre apóstrofes '.

Se pueden utilizar expresiones regulares de la forma: grep [-opcion] expresión_regular


[referencia...]

Las opciones que admite son:


c escribe el número de las líneas que satisface la condición.
i, no se distinguen mayúsculas y minúsculas.
l escribe los nombres de los archivos que contienen líneas buscadas.
n, cada línea es precedida por su número en el archivo.
s, no se vuelcan los mensajes que indican que un archivo no se puede abrir.
v, se muestran solo las líneas que no satisfacen el criterio de selección.
Algunos ejemplos:
grep ‘ˆd’ text recupera las líneas que comienzan por d.
grep ‘ˆ[ˆd]’ text recupera las líneas que no comienzan por d.
grep -v ‘ˆC’ file1 > file2 quita las líneas de file1 que comienzan por C y lo copia en file2

El comando lpr nombre_archivo imprime por defecto el archivo indicado. Si se usa sin argumentos
imprime el texto que se introduzca a continuación en la impresora.

El comando tar -cvf nombre_archivo.tar archivo1 archivo2… agrupa varios archivos en uno solo
archivo tar. Con gzip archivo se comprime archivo (que es borrado) y se crea un archivo con nombre
archivo.gz.
Se pueden realizar las operaciones inversas, de manera que se pueden extraer los archivos mediante tar -
xpvf nombre_archivo.tar archivo1… y descomprimir un archivo mediante gzip -d archivo.gz.
Dado que suele emplearse tar y gzip de forma consecutiva obteniéndose archivos con extensión tar.gz o
tgz, entonces tar incluye la opción z para extraer archivos con esta extensión tar -zxf archivo.tar.gz.
Los comandos tienen una entrada estándar (número 0) y dos salidas estándar (número 1 para la salida
normal y número 2 para la salida con errores). Por defecto, la entrada y la salida estándar de un
comando es la terminal, a no ser que el comando especifique los nombres de archivos que hagan de
entrada y de salida, como por ejemplo el comando cp file1 file2.
Sin embargo, se puede redirigir la salida de un comando usando los operadores:
(>) redirige la salida estándar hacia el archivo indicado y en caso de no existir se crea.
(<) redirige la entrada estándar desde un determinado archivo.
(>>) redirige la salida estándar hacia otro archivo, pero añadiendo dicha salida al final de ese
archivo, sin sobrescribir el contenido original.
Por ejemplo:
date >>archivo el archivo archivo contendrá información sobre todas las veces que se ha
entrado en el sistema.
cat file1 file2 >file3 añade al archivo file2 al final de file1 y al conjunto lo llama file3.
cat file2 >>file1 realiza la misma operación que el anterior, pero al resultado lo llama file1.
Una tubería (|) permite comunicar la salida estándar de un comando con la entrada estándar de otro.
Por ejemplo, ls | mail juan envía a juan una lista de los archivos del sistema. Con el operador de tubería
se pueden empalmar tantos comandos como se desee.

Permite redirigir la salida de un comando a un determinado archivo y que también se bifurque hacia
la terminal. Para ello se usa el operador tee,como por ejemplo ls | tee file, que muestra la salida por el
terminal y además la envía al archivo file. En este ejemplo, si se quiere que la salida se añada al final de
file, se usaría la opción -a: ls | tee -a file.

Los mensajes de error se dirigen a la salida número 2, que normalmente es también la terminal. En
algunos casos puede interesar redirigir estos mensajes a otra salida como, por ejemplo, cuando se
ejecuta un programa en segundo plano.

Véanse a continuación los siguientes ejemplos:


gcc prueba.c 2>errores se redirige la salida 2 hacia el archivo errores.
program <datos.d >resultados.r 2> & 1 se redirige la salida estándar de errores al mismo
archivo que la salida estándar.
Existen varios comandos para gestionar la ejecución de un programa:
El carácter & permite realizar una ejecución en segundo plano recuperando inmediatamente el
control del terminal. Para ello se añade el carácter & al final del comando de ejecución program
<datos.d >resultados.r &. Como resultado aparece en el terminal el número de proceso de la
ejecución de este programa.
Para detener la ejecución de un proceso se puede utilizar el comando kill númerodeproceso.
Cuando se sale del sistema, si hay algún proceso ejecutándose en segundo plano se para, salvo
que se use el comando nohup program. En este caso si no se utilizan redirecciones todas las
salidas del programa se dirigen a un archivo llamado nohup.out.
El comando nice permite realizar ejecuciones con baja prioridad de la forma:
nice program &
nice nohup program &
Para darle al programa la prioridad mínima habría que invocarlo como nice -19 program & donde el
-19 indica la mínima prioridad.
El comando time, precediendo a cualquier otro comando, suministra información acerca del
tiempo total empleado en la ejecución, del tiempo de CPU utilizado por el programa del usuario
y del tiempo de CPU consumido en utilizar recursos del sistema. Por ejemplo, time gcc
prueba.c ofrece información sobre el tiempo de compilación y montaje del programa prueba.c.
El comando top muestra una lista de los procesos que se están ejecutando. Sus principales
opciones son u, que muestra los procesos que pertenecen a un determinado usuario), k, que
elimina un proceso, y h, que muestra la ayuda del programa.

Creación de scripts
Un guión o script es un archivo de texto que contiene comandos de la Shell que se interpretarán cuando
se ejecute el script.

Se caracterizan por:
Pueden tener cualquier nombre.
El archivo debe tener permiso de ejecución.
Para ejecutarlo se debe indicar el nombre del intérprete y el nombre del guión.
Las instrucciones del guión se procesan por orden, una por línea, de manera que los saltos de
línea se interpretan como un “INTRO”.
Las órdenes podrían aparecer en una misma línea separadas por punto y coma.
Los guiones pueden tener una línea inicial que indica el tipo de intérprete que se quiere utilizar para
ejecutar los comandos.
Por ejemplo #!/bin/bash indica que debe usarse el intérprete Bourne-Again Shell.
Asimismo, un guión puede contener comentarios que se marcan con el carácter # al inicio del texto
del comentario.

Los principales elementos de un script son:

Variables y parámetros

Para definir una variable se usa la estructura variable=valor donde variable es el nombre de la misma.
Hay que tener en cuenta lo siguiente:
1.No puede haber un espacio entre el nombre de la variable, el signo = y el valor.
2.Si se desea que el valor contenga espacios, es necesario utilizar comillas.
3.Para recuperar el valor de una variable hay que anteponerle a su nombre el carácter $. Por
ejemplo, para visualizar el valor de una variable echo $variable.
En el siguiente ejemplo, se define la variable Prueba=”ls -l” y a continuación se invoca con $Prueba
Para eliminar una variable, se usa el comando unset. Por ejemplo:

Existen algunas variables definidas en la Shell. En la siguiente tabla se muestran las principales
variables.

Nombre de variable Significado

LOGNAME Nombre del usuario.

HOME Directorio de trabajo del usuario actual.

PATH Caminos usados para ejecutar órdenes o programas.

PWD Directorio activo.

TERM El tipo de la terminal actual.

SHELL Shell actual.

Contiene el valor de salida de la última orden ejecutada. El valor de ‘0’ indica


$?
que no ha habido errores, mientras que otros valores indican errores.
Un guión puede recibir parámetros en la línea de órdenes para considerarlos durante su ejecución. Los
parámetros recibidos se guardan en una serie de variables que el script puede consultar cuando lo
necesite. Los nombres de estas variables son: $1 $2 $3 ... ${10} ${11} ${12} ... de manera que:
La variable $0 contiene el nombre con el que se ha invocado al script, es decir, el nombre del
programa.
$1 contiene el primer parámetro.
$2 contiene el segundo parámetro.
. . .
A continuación, en el siguiente ejemplo de un guión Shell se muestran los valores de los parámetros.

La orden shift mueve todos los parámetros una posición a la izquierda, esto hace que el contenido del
parámetro $1 desaparezca y sea reemplazado por el contenido de $2, $2, que es reemplazado por $3, etc.
Además, existen algunas variables especiales tales como $# que contienen el número de parámetros que
ha recibido el script, y $* o $@ contienen todos los parámetros recibidos.

A continuación, se describen algunas reglas de la evaluación de variables:

$var o ${var}

Representa el valor de la variable o nada si la variable no está definida.


${var-val}
Devuelve el valor de var si está definida, si no val.

${var=val}
Valor de var si está definida, si no val y el valor de var pasa a ser val.

${var?message}
Devuelve $var si está definida y si no, imprime el mensaje en el terminal del Shell. Si el mensaje está
vacío imprime uno estándar.

${val+var}
Devuelve $val, si está var definida, si no, nada.

El siguiente ejemplo muestra cómo podemos usar una variable, asignándole un valor en caso de que
no esté definida.

Arrays

Un array es una colección de elementos del mismo tipo, dotados de un nombre, y que se almacenan
en posiciones contiguas de memoria.

El primer elemento del array está numerado con el 0.

No hay un tamaño límite para un array y la asignación de valores se puede hacer de forma alterna.

La sintaxis para declarar un array es la siguiente:


Crear e inicializar un array: nombre_array=(val1 val2 val3 ...).
Asignar un valor al elemento x del array: nombre_array[x]=valor.
Acceder al elemento x: ${nombre_array[x]}.
Consultar todos los elementos: ${nombre_array[*]} o ${nombre_array[@]}.
Es preciso tener en cuenta lo siguiente:
Si al referenciar a un array no se utiliza subíndice se considera que se está referenciando a su
primer elemento.
Para conocer el tamaño en bytes del array se utiliza #${nombre_array[x]}, donde x puede ser
un subíndice, o bien los caracteres * o @.

Estructuras de control
Indica que determinadas órdenes solo se ejecuten cuando se cumplan unas condiciones concretas.
Para ello, se utilizan las estructuras if o case.

Estructura IF
if [expresión]
then
órdenes a ejecutar si se cumple la condición
elif [expresión]
then
órdenes a ejecutar si se cumple la condición
else
órdenes a ejecutar en caso contrario
fi

Por ejemplo:
if grep -q set prueba.bash
then
echo encontrada la palabra clave set
else
echo no encontrada la palabra clave set
fi
Estructura case
case $var in
v1) … #Acción a realizar si var toma el valor v1
;;
v2|v3) …#Acción a realizar si var toma el valor v2 o v3
;;
*) …# Caso por defecto
;;
Esac

Por ejemplo:

case $var in
1) echo La variable var es un uno
;;
2) echo La variable var es un dos
;;
*) echo La variable var no es ni un uno ni un dos
;;
esac

BUCLES

Permite ejecutar bloques de órdenes de forma iterativa dependiendo de una condición. Para ello se
utilizan las estructuras if o case.

WHILE

while [ expresión ] # Mientras la expresión sea cierta ...


do
...
done
UNTIL

until [ expresión ]
# Mientras la expresión sea falsa ...
do ...
done

FOR

for var in lista#Por cada valor en la estructura lista se ejecuta una iteración.
do
órdenes a ejecutar
done

Por ejemplo:

for i in 10 30 70
do
echo Mi número favorito es $i
done

Break y continue

Sirven para interrumpir la ejecución secuencial del cuerpo del bucle:


break transfiere el control a la orden que sigue a done, haciendo que el bucle termine antes de
tiempo.
continue transfiere el control a done, haciendo que se evalúe de nuevo la condición,
prosiguiendo el bucle.
En ambos casos, las órdenes del cuerpo del bucle siguientes a estas sentencias no se ejecutan. Lo normal
es que formen parte de una sentencia condicional, como if.

Órdenes internas de la Shell

Una orden interna del Shell es una orden que el intérprete implementa y que ejecuta sin llamar a
programas externos.

Echo
Envía una cadena a la salida estándar.

Read
Lee una cadena de la entrada estándar.
La orden read puede leer diferentes variables a la vez. También se puede combinar el uso de read con
echo para mostrar un prompt que indique qué es lo que se está pidiendo.

Hay una serie de caracteres especiales para usar en echo y que permiten posicionar el cursor en un
sitio determinado:
_ nb: retrocede una posición (sin borrar)
_ nf: alimentación de página
_ nn: salto de línea
_ nt: tabulador
Para que echo reconozca estos caracteres es necesario utilizar la opción .-e. y utilizar comillas dobles:
$ echo -e "Hola \t ¿cómo estás?"
hola como estás
True y false
Devuelven 0 y 1 siempre, respectivamente. El valor 0 se corresponde con true y cualquier valor
distinto de 0 con false.

La orden test

Test es una orden que permite evaluar si una expresión es verdadera o falsa. Se usan en la estructura
if/then/else/ para determinar qué parte del script se va a ejecutar.
La sintaxis de test puede ser una de las dos que se muestran a continuación:
test expresión
[ expresión ]
Algunos operadores que pueden aparecer en las condiciones a evaluar:
Operadores para números

-eq Igual a

-ne Distinto de

-lt Menor que

-le Menor o igual que

-gt Mayor que

-ge Mayor o igual que


Operadores para cadenas

-z cadena Verdad si la longitud de cadena es cero

-n cadena
o
Verdad si la longitud de cadena no es cero
cadena

cadena1 == cadena2 Verdad si las cadenas son iguales. Se puede emplear = en vez de ==

cadena1 != cadena2 Verdad si las cadenas no son iguales

Verdad si cadena1 se ordena lexicográficamente antes de cadena2


cadena1 < cadena2
en la localización en curso

Verdad si cadena1 se ordena lexicográficamente después de


cadena1 > cadena2
cadena2 en la localización en curso

Operadores sobre ficheros

-e fichero El fichero existe

-r fichero El fichero existe y tengo permiso de lectura

-w fichero El fichero existe y tengo permiso de escritura

-x fichero El fichero existe y tengo permiso de ejecución

-f fichero El fichero existe y es regular

-s fichero El fichero existe y es de tamaño mayor a cero

-d fichero El fichero existe y es un directorio

Operadores lógicos

-o OR

-a AND

! NOT

Paréntesis
\(
izquierdo

\) Paréntesis derecho
Funciones
Declaración

Para declarar una función:


function nombre:
{ mi_código }
Llamar a la función es como llamar a otro programa, solo hay que escribir su nombre.

Por ejemplo:

# Se define la función hola


function hola {
echo ¡Hola!
}

hola # Se llama a la función hola

Definición con parámetros

Se pueden definir funciones con parámetros:


#!/bin/bash
function e {
echo $1
}

Se podría invocar como:


e Hola
Algunos ejemplos de scripts:
Ejemplo 1. Guión que solicita confirmación si va a sobrescribir un archivo cuando se use la orden cp.
#!/bin/bash
if [ -f $2 ]
then
echo "$2 existe. ¿Quieres sobreescribirlo? (s/n)"
read sn
if [ $sn = "N" -o $sn = "n" ]
then
exit 0
fi
fi
cp $1 $2

Ejemplo 2. Guión que liste los directorios existentes en el directorio actual.


#!/bin/bash
for archivo in *
do
test -d $archivo && ls $archivo
done

Ejemplo 3. Guión que borra con confirmación todos los archivos indicados como argumentos en la
línea de comandos.
#!/bin/bash
while test "$1" != ""
do
rm -i $1
shift
done
Ejemplo 4. Guión que evalúa la extensión de un archivo. Si esta se corresponde con .txt., copia el
archivo al directorio ~/copias. Si es otra la extensión presenta un mensaje.
case $1 in
*.txt)
cp $1 ~/copias/$1
;;
*)
echo "$1 extensión desconocida"
;;
Esac

Ejemplo 5. Guión que pone el atributo de ejecutable a los archivos pasados como argumento.
for fich in $@
do
if test -f $fich
then
chmod u+x $fich
fi
done
Ejemplo 6. Guión que lee dos números del teclado e imprime su suma.
#!/bin/bash
echo "Introduzca un número \n"
read numero1
echo "Introduzca otro número \n"
read numero2
let respuesta=$numero1+$numero2
echo "$numero1 + $numero2 = $respuesta \n"
Caso práctico
Se pide crear un script que muestre el siguiente menú:
Ver directorio actual...........[1]
Copiar archivos.................[2]
Editar archivos.................[3]
Imprimir archivo................[4]
Salir del menú..................[5]"
Donde:
Si elige la primera opción, se mostrarán los archivos del directorio actual.
Si elige la segunda opción, se le pedirá el nombre del archivo que se quiere copiar y el nombre
del archivo donde se quiere copiar.
Si elige la tercera opción, se le pedirá el nombre del archivo que se desea editar y se abrirá el
editor vi para editarlo.
Si elige la cuarta opción, se le pedirá el nombre del archivo que se desea imprimir y se
imprimirá el mismo.
Si elige la quinta opción, se saldrá del script.

SOLUCION

while true
do
clear
echo "
Ver directorio actual...........[1]
Copiar ficheros.................[2]
Editar ficheros.................[3]
Imprimir fichero................[4]
Salir del menú..................[5]"
read i
case $i in
1) ls -l|more; read z
;;
2) echo "Introduzca [desde] [hasta]"
read x y
cp $x $y
read x
;;
3) echo "¿Nombre de fichero a editar?"
read x;
vi $x
;;
4) echo "¿Nombre de fichero a imprimir?"
read x
lpr $x
;;
5) clear; break
;;
esac
done
El lenguaje Python y el entorno Jupyter
Notebook
El lenguaje de programación Python

Python es un lenguaje de programación de alto nivel creado por Guido van Rossum. Se desarrolla como
un proyecto de código libre, de manera que existe una comunidad de desarrolladores que mantienen el
lenguaje, gestionan las versiones del mismo y crean librerías para aumentar su funcionalidad.
Algunas de sus características son:
Es un lenguaje interpretado, por lo que no se debe compilar el código antes de su ejecución.
Es multiparadigma. En este sentido, permite varios estilos de programación: imperativo,
orientado a objetos y funcional.
Es multiplataforma. Python es un lenguaje disponible en los principales Sistemas Operativos
(Windows, Linux y Mac).
Posee un tipado dinámico. El tipo de los datos es inferido en tiempo de ejecución, de manera
que no es necesario declarar el tipo de sus variables y permite conversiones dinámicas de los
tipos de los datos.
En comparación con otros lenguajes de programación, Python es un lenguaje simple, fácil de leer y
escribir y simple de depurar. Por estas razones es fácil de aprender, de manera que la curva de
aprendizaje es corta.
Asimismo, Python cuenta con una gran cantidad de librerías, tipos de datos y funciones incorporadas en
el propio lenguaje, lo que le dota de una gran capacidad de procesamiento. En particular, es
ampliamente utilizado en el ámbito del análisis de datos y, en general, en tareas de procesamiento de
ciencias e ingeniería.
El entorno Jupyter notebook

En cualquier lenguaje de programación, las herramientas de edición constituyen un elemento esencial.


En general, este tipo de herramientas implementan servicios tales como el autocompletado de palabras
del lenguaje programación, ayuda interactiva, coloreado de las estructuras sintácticas del lenguaje,
depuración de errores, la compilación o interpretación y la de ejecución del programa .

Se trata de un entorno de edición y ejecución visual que permite integrar trozos de códigos con
contenidos multimedia o textuales que ayudan a documentar y facilitar la comprensión de los programas
realizados. Los documentos generados se visualizan con un navegador (Explorer, Chrome, Firefox...) y
pueden incluir cualquier elemento accesible a una página web, además de permitir la ejecución de
código escrito en el lenguaje de programación Python.
Jupyter contiene todas las herramientas científicas estándar de Python que permiten realizar tareas
propias en el contexto del análisis de datos: importación y exportación, manipulación y transformación,
visualización, etc.
Para instalarlo, lo mejor es el entorno Anaconda, el cual incluye un conjunto de herramientas de
desarrollo para Python, entre las que se encuentra el Jupyter Notebook. Anaconda se puede descargar
desde la página web de Anaconda.1 En la zona de descargas, aparecen dos versiones. Se debe bajar la
versión 3.x .

Los elementos esenciales de un notebook son:

Título del notebook. Cada notebook tiene un título asociado. Por defecto, aparece con el
nombre Untitled. Para modificarlo, basta pulsar sobre Untitled y aparece una ventana en la que
se puede modificar el nombre.
Menús y Barra de herramientas. En la parte superior de la interface del notebook, aparece un
conjunto de menús con diferentes opciones para gestionar los archivos, para editar, visionar, etc.
Iconos de acceso rápido. Iconos que permiten realizar las acciones más comunes sobre los
elementos del notebook.
Celdas. La unidad de edición de un notebook son las celdas. Cada celda contiene código
Python o la información que documenta dicho código. En este sentido, un notebook es una
secuencia de celdas. Las celdas pueden ser de diferentes tipos según el contenido que almacenan:
Markdown: permite escribir texto formateado con el objetivo de documentar. Se usa el
lenguaje de marcas Markdown.
Raw NBConvert: son celdas que permiten escribir fragmentos de código sin que sean
ejecutados.
Heading: permite embeber código html.
Code: sirven para escribir código Python ejecutable. Están marcadas por la palabra In
[n] y están numeradas. El resultado de la ejecución de cada celda se muestra en el
campo Out[n], también numerado.
Todas las celdas son susceptibles de ser ejecutadas. La ejecución de una celda de código Python
ejecutará el código escrito en la celda y producirá una salida. La ejecución de celdas de tipo Markdown
dará formato al texto. Para ejecutar una celda, hay que colocarse en la celda y, posteriormente, pulsar el
botón cuyo icono es un triángulo mirando a la derecha
.

De igual forma para interrumpir la ejecución, se pulsa sobre el cuadrado. Para crear celdas nuevas, se
pulsa sobre el botón con el icono del signo +.
El flujo normal de edición de una celda consiste en:
Elegir el tipo de celda. Por defecto, son de tipo Code. Dependiendo del tipo de celda elegido,
Jupyter lo interpretará de diferente manera.
Una vez introducido el código o texto en la celda, se debe ejecutar. Para ello, se pulsa sobre el
icono en forma de flecha.
A continuación, se genera una nueva celda para editar.
Por ejemplo, si se quiere crear una celda que contenga la función: def multiplica(a,b): return a*b y
después invocarla con los valores 3 y 4, se haría como se muestra en la figura 2.10.
Elementos básicos de Python
Variables

Una variable es un nombre que referencia un valor.

Por ejemplo:

Una sentencia
de asignación
crea variables nuevas y las asocia a valores.

Una sntencia de asignación crea variables nuevas y las asocia a valores.

Para mostrar el valor de una variable, se puede usar la sentencia print


Las variables son de un tipo que coincide con el tipo del valor que referencian. El método type () indica
el tipo de una variable.

Algunos de los tipos más usados son:

No pueden ser palabras reservadas de Python.int: enteros.


float: números reales.
bool: valores booleanos: cierto y falso.
str: cadenas.
None: corresponde al valor nulo.
Cabe señalar que existen unas reglas de construcción de los nombres de las variables:
Pueden ser arbitrariamente largos.
Pueden contener tanto letras como números.
Deben empezar con letras.
Pueden aparecer subrayados para unir múltiples palabras.

Es necesario llamar la atención sobre el hecho de que, antes de poder actualizar una variable, se debe
inicializar mediante una asignación. A continuación, se puede actualizar la variable aumentándola
(incrementar) o disminuyendo (decrementar).

Palabras reservadas

Python reserva 31 palabras clave para su propio uso:


Operadores

Los operadores son símbolos especiales que representan cálculos, como la suma o la multiplicación. Los
valores a los cuales se aplican esos operadores reciben el nombre de operandos. Los principales
operadores sobre los tipos int y float son:
i+j suma
i-j resta
i*j multiplicación
i/j división de dos números. Si son enteros, el resultado es un entero, y si son reales, el
resultado es un real
i//j cociente de la división entera
i%j resto de la división entera
i**j que representa i elevado a la potencia j
i==j que representa i igual que j
i!=j que representa i distinto que j
i>j que representa i mayor que j, y de forma similar: >=, <, <=
Se pueden usar los operadores con las cadenas.

Pero existen algunas particularidades cuando se usan los operadores sobre las cadenas.
Por ejemplo:

Expresiones

Una expresión es una combinación de valores, variables y operadores.

Un valor, por sí mismo, se considera una expresión y también lo es una variable.
Las expresiones tienen un tipo. Así, por ejemplo, 6 + 7 es una expresión que representa un
entero. Cuando en una expresión aparece más de un operador, el orden de evaluación depende de
las reglas de precedencia. Para los operadores matemáticos, Python sigue las convenciones
matemáticas:
El orden de los operadores es: paréntesis, exponenciales, multiplicación/división,
suma/resta.
Cuando existe la misma precedencia, se evalúa de izquierda a derecha.

Comentarios

En Python comienzan con el símbolo #, de forma que todo lo que va desde # hasta el final de la línea es
ignorado y no afecta al programa.

En el ejemplo anterior, el comentario aparece como una línea completa, pero también pueden ponerse
comentarios al final de una línea.
Entrada de información

Python proporciona una función llamada input que recibe la entrada desde el teclado, de forma que
cuando se llama el programa se detiene y espera a que el usuario escriba algo. Cuando el usuario pulsa
“Intro”, el programa continúa y la función devuelve como una cadena aquello que el usuario escribió.

Antes de recibir
cualquier dato
desde el usuario, es mejor escribir un mensaje explicando qué debe introducir. Se puede pasar una
cadena a input, que será mostrada al usuario antes de que el programa se detenga para recibir su entrada.

La secuencia \n al final del mensaje representa un newline, que es un carácter especial que provoca un
salto de línea. Por eso, la entrada del usuario aparece debajo del mensaje. Si se espera que el usuario
escriba un entero, puedes intentar convertir el valor de retorno a int usando la función int (), pero si el
usuario escribe algo que no sea una cadena de dígitos, obtiene un error.

Expresiones booleanas

Una expresión booleana es aquella que puede ser verdadera (True) o falsa (False). True y False son
valores especiales que pertenecen al tipo bool (booleano).

Los ejemplos siguientes usan el operador ==, que compara dos operandos y devuelve True si son iguales
y False en caso contrario.
Los principales operadores booleanos son:
x == y # x es igual que y.
x != y # x es distinto de y.
x > y # x es mayor que y.
x < y # x es menor que y.
x >= y # x es mayor o igual que y.
x <= y # x es menor o igual que y.
x is y # x es lo mismo que y.
x is not y # x no es lo mismo que y.
not representa la negación.
and cierto si las dos expresiones que relaciona son ciertas y falso en caso contrario.
or falso si las dos expresiones que relaciona son falsas y cierto en caso contrario.
Por ejemplo:
x >0 and x <10 es verdadero solo cuando x es mayor que 0 y menor que 10.
n%2 == 0 or n%3 == 0 es verdadero si el número es divisible por 2 o por 3.
not (x>y) es verdadero si x es menor o igual que y.
Hay que tener en cuenta que cualquier número distinto de cero se interpreta como “verdadero”.

Estructuras de control
Condicionales

Las expresiones condicionales facilitan la codificación de estructuras que bifurcan la ejecución del
código en varias ramas o caminos de ejecución. Existen varias formas.

If simple

Tiene la estructura:

if expresión booleana:
ejecutar código
Ahora bien, es preciso observar que:
La expresión booleana después de la sentencia if recibe el nombre de condición. La sentencia
if se finaliza con un carácter de dos puntos (:) y la/s línea/s que van detrás de la sentencia if van
indentadas. Este código se denomina bloque.
Si la condición lógica es verdadera, la sentencia indentada será ejecutada. Si la condición es
falsa, la sentencia indentada será omitida.
No hay límite en el número de sentencias que pueden aparecer en el cuerpo, pero debe haber al
menos una. A veces puede resultar útil tener un cuerpo sin sentencias, usándose en este caso la
sentencia pass, que no hace nada.
Bucles

Los bucles permiten la repetición de acciones y generalmente se construyen así:

Proceso:
Se inicializan una o más variables antes de que el bucle comience.

Se realiza alguna operación con cada elemento en el cuerpo del bucle, posiblemente cambiando
las variables dentro de ese cuerpo.
Se revisan las variables resultantes cuando el bucle se completa.
Existen varias formas:

WHILE

El primer tipo de bucle es el while. El cuerpo del bucle debe cambiar el valor de una o más variables, de
modo que la condición pueda en algún momento evaluarse como falsa y el bucle termine. La variable
que cambia cada vez que el bucle se ejecuta y controla cuándo termina este, recibe el nombre de
variable de iteración. Si no hay variable de iteración, el bucle se repetirá para siempre, resultando así un
bucle infinito. Cada vez que se ejecuta el cuerpo del bucle se dice que se realiza una iteración. Tiene la
siguiente estructura:

while (expresión booleana):


código

En este ejemplo, el bucle nunca se ejecuta cuando x=0 y nunca terminará si empieza con x<0.
Es preciso observar que, a veces, no se sabe si hay que terminar un bucle hasta que se ha recorrido la
mitad del cuerpo del mismo. En ese caso, se puede crear un bucle infinito a propósito y usar la sentencia
break para salir explícitamente cuando se haya alcanzado la condición de salida.

Algunas veces, estando dentro de un bucle se necesita terminar con la iteración actual y saltar a la
siguiente de forma inmediata. En ese caso se puede utilizar la sentencia continue para pasar a la
siguiente iteración sin terminar la ejecución del cuerpo del bucle para la actual.

FOR

El siguiente tipo de bucle es el for. Se repite a través de un conjunto conocido de elementos, de modo
que ejecuta tantas iteraciones como elementos hay en el conjunto. Es útil utilizar la función range para
crear una secuencia. Range puede tomar uno o dos valores:
Si toma dos valores, genera todos los enteros desde la primera entrada hasta la segunda
entrada-1. Por ejemplo: range (2, 5) = (2, 3, 4).
Y si toma un solo parámetro, entonces range(x) = range(0,x).
Tiene la siguiente estructura:
for variable in secuencia:
código

Téngase en cuenta que los bucles pueden estar anidados

Estructuras de datos
Tuplas

Una tupla es una secuencia de valores de cualquier tipo indexada por enteros. Las tuplas son inmutables
—tienen una longitud fija y no pueden cambiarse sus elementos— y son comparables. Sintácticamente,
una tupla es una lista de valores separados por comas y encerradas entre paréntesis.
Para crear una tupla con un único elemento, es necesario incluir una coma al final.

Otra forma de construir una tupla es usar la función interna tuple que crea una tupla vacía si se invoca
sin argumentos, y si se le proporciona como argumento una secuencia (cadena, lista o tulpa) genera una
tupla con los elementos de la secuencia.

Los principales operadores sobre tuplas son:

El operador corchete indexa un elemento.

El operador slice selecciona un rango de elementos.

Es posible reemplazar dos tuplas.

No se pueden modificar los elementos de una tupla, pero se puede reemplazar una tupla por otra.
Se comienza comparando el primer elemento de cada secuencia. Si es igual en ambas, pasa al siguiente
elemento, y así sucesivamente, hasta que encuentra uno que es diferente. A partir de ese momento, los
elementos siguientes ya no son tenidos en cuenta.

Listas

Una lista es una secuencia de valores de cualquier tipo que reciben el nombre de elementos.

Creación

El método más simple para crear una lista es encerrar los elementos entre corchetes.

Asignación

La asignación de valores a una lista no retorna nada, sin embargo, si se usa el nombre de la lista, es
posible ver el contenido de la variable.

Listas vacías

Una lista que no contiene elementos recibe el nombre de lista vacía —se crea con unos corchetes vacíos
[]—.

Acceso a elementos

Para acceder a los elementos de una lista, se usa el operador corchete que contiene una expresión que
especifica el índice —los índices comienzan por 0—. Los índices de una lista se caracterizan por:
Cualquier expresión entera puede ser utilizada como índice.
Si se intenta leer o escribir un elemento que no existe, se obtiene un IndexError.
Si un índice tiene un valor negativo, se cuenta hacia atrás desde el final de la lista.
Listas mutables

Las listas son mutables, puesto que su estructura puede ser cambiada después de ser creadas.

Tipo de elementos

Los elementos en una lista no tienen por qué ser todos del mismo tipo.

Anidación

Cuando una lista está dentro de otra, se dice que está anidada. En una lista anidada, cada lista interna
solo cuenta como un único elemento.

Indexación con números negativos

Soporta indexación con números negativos que permite seleccionar por el final de la lista.

6.2.1. Operadores y
Operadores y funciones

El operador + concatena listas.


El operador in permite preguntar la pertenencia de un elemento a una lista.

El operador in se puede usar para recorrer los elementos de una lista usando un bucle for, como por
ejemplo:

El operador * repite una lista el número especificado de veces.

El operador (slice) cuya sintaxis es [inicio:final:salto] permite seleccionar secciones de una lista:

El operador del elimina un elemento de la lista referenciado en forma de índice.


La función sum () permite realizar la suma de una lista de números.

Las funciones max () y min () proporcionan el elemento máximo/mínimo de una lista.

La función len () proporciona la longitud de una lista.

La función range () crea una secuencia de valores a partir del dado como parámetro. Es útil para los
bucles de tipo for. Por ejemplo:

Para ver el contenido generado por range (), se debe usar el constructor list.
append añade un nuevo elemento al final de una lista.

extend toma una lista como argumento y añade al final de la actual todos sus elementos.

sort ordena los elementos de una lista de menor a mayor.

El método pop elimina un elemento de la lista referenciado en forma de índice. Devuelve el elemento
que ha sido eliminado. Si no se proporciona un índice, borra y devuelve el último elemento.

El método remove permite eliminar un elemento de la lista referenciándolo por su valor.


Equivalencia en las listas

En Python dos listas son equivalentes si tienen los mismos elementos, pero no son idénticas. Sin
embargo, si dos listas son idénticas, también son equivalentes, es decir, la equivalencia no implica que
sean idénticas. Para comprobar si dos variables son idénticas, se puede usar el operador is.

En este ejemplo a y b son equivalentes pero no idénticas.

En este ejemplo a y b son idénticas.

Si a y b son idénticas significa que la lista tiene dos referencias o nombres diferentes. Así, los cambios
que se hagan usando cualquiera de los nombres afectan a la misma lista.

Hay que tener en cuenta que:


En las operaciones que se realizan sobre las listas existen aquellas que modifican listas y otras
que crean listas nuevas. Por ejemplo, el método append modifica una lista, pero el operador +
crea una lista nueva.

La mayoría de los métodos modifican la lista y devuelven el valor None.


Cadenas

Una cadena es una secuencia de caracteres y una lista es una secuencia de valores, pero una lista de
caracteres no es lo mismo que una cadena. Para convertir desde una cadena a una lista de caracteres, se
puede usar la función list, que divide una cadena en letras individuales.

Si se quiere dividir una cadena en palabras, puedes usar el método split

Una vez usado split, se puede utilizar el operador índice (corchetes) para buscar una palabra concreta en
la lista.

Se puede llamar a split con un argumento opcional denominado delimitador, que especifica qué
caracteres se deben usar como delimitadores de palabras.

Join es la inversa de split y toma una lista de cadenas y concatena sus elementos. Al ser un método de
cadena, debe invocarse sobre el delimitador y pasarle la lista como un parámetro.

Por ejemplo:
Listas por comprensión

Una lista por comprensión es una expresión compacta para definir listas, conjuntos y diccionarios en
Python. Se trata de definir cada uno de los elementos sin tener que nombrar cada uno de ellos. La forma
general es:

[exp for val in <coleccion> if <condicion>]

Diccionarios

Un diccionario es una colección no ordenada de pares clave-valor donde la clave y el valor son
objetos que pueden ser de (casi) cualquier tipo. La función dict () crea un diccionario nuevo sin
elementos.

Las llaves {} representan un diccionario vacío.


Para añadir elementos al diccionario, se pueden usar corchetes y usar acceso indexado a través de la
clave.

Otra forma de crear un diccionario es mediante una secuencia de pares clave-valor separados por comas
y encerrados entre llaves.
El orden de los elementos en un diccionario es impredecible, pero eso no es importante, ya que se usan
las claves para buscar los valores correspondientes. En este sentido, si la clave especificada no está en el
diccionario se obtiene una excepción.
Algunos métodos:
El método len()

La función len devuelve el número de parejas clave-valor

El método in()

El operador in dice si algo aparece como clave en el diccionario.

El método values()

Para ver si algo aparece como valor en un diccionario, se puede usar el método values, que devuelve los
valores como una lista, y después usar el operador in sobre esa lista.

El método get ()

Toma una clave y un valor por defecto. Si la clave aparece en el diccionario get, devuelve el valor
correspondiente. En caso contrario, devuelve el valor por defecto.

El método keys()

Crea una lista con las claves de un diccionario.


El método ítems()

Devuelve una lista de tuplas, cada una de las cuales es una pareja clave-valor sin ningún orden definido.

Téngase en cuenta lo siguiente:


Se puede utilizar un diccionario como una secuencia en una sentencia for, de manera que se
recorren todas las claves del diccionario.

Funciones
Una función es una secuencia de sentencias que realizan una operación y que reciben un nombre. Sus
principales características son:

Cuando se define una función, se especifica el nombre y la secuencia de sentencias.


Una vez que se ha definido una función, se puede llamar a la función por ese nombre y
reutilizarla a lo largo del programa.
El resultado de la función se llama valor de retorno
Creación

Para crear una función se utiliza la palabra reservada def. A continuación, aparece el nombre de la
función, entre paréntesis los parámetros, y finaliza con :. Esta línea se denomina cabecera de la función.
Después de los : aparece el código que se ejecuta cuando se llama a la función. Este trozo de código, se
denomina cuerpo de la función y debe estar indentado. El cuerpo puede contener cualquier número de
sentencias. Para devolver el valor se usa la palabra reservada return.
Definición y llamada

Las reglas para los nombres de las funciones son los mismos que para las variables: se pueden usar
letras, números y algunos signos de puntuación, pero el primer carácter no puede ser un número. No se
puede usar una palabra clave como nombre de una función y se debería evitar también tener una
variable y una función con el mismo nombre. Las funciones con paréntesis vacíos después del nombre
indican que esta función no toma ningún argumento.
La sintaxis para llamar a una función definida consiste en indicar el nombre de la función junto a una
expresión entre paréntesis denominados argumentos de la función. El argumento es un valor o variable
que se pasa a la función como parámetro de entrada.

Algunas características

La definición de una función debe ser ejecutada antes de que la función se llame por primera vez, y no
generan ninguna salida. Sin embargo, las sentencias dentro de cada función son ejecutadas solamente
cuando se llama a esa función.
En las funciones no se especifica el tipo de parámetro ni lo que se retorna.
Las definiciones de funciones no alteran el flujo de la ejecución de un programa debido a que
las sentencias dentro de una función no son ejecutadas. Sin embargo, una llamada a una función
es como un desvío en el flujo de la ejecución. En vez de pasar a la siguiente sentencia, el flujo
salta al cuerpo de la función, ejecuta todas las sentencias que hay allí y después vuelve al punto
donde lo dejó.
Las funciones que disponen de argumentos son asignadas a variables llamadas parámetros. Se
puede usar cualquier tipo de expresión como argumento, la cual será evaluada antes de que la
función sea llamada. El nombre de la variable que se pasa como argumento no tiene nada que
ver con el nombre del parámetro, de manera que dentro de la función recibirá el nombre del
parámetro.
Cuando se definen los argumentos de una función, estos pueden tener valores por defecto.

Una vez que se ha definido una función puede usarse dentro de otra, facilitando de esta manera la
descomposición de un problema, y resolverlo mediante una combinación de llamadas a funciones

Funciones con retorno

Aquellas que producen resultados, con los que se querrá hacer algo, como asignárselo a una variable.

Funciones sin retorno

Aquellas que realizan alguna acción, pero no devuelven un valor y, sin embargo, pueden mostrar algo
por pantalla. Si se asigna el resultado a una variable, se obtiene el valor None.
Funciones internas de Python

Python proporciona un número importante de funciones internas que pueden ser usadas sin necesidad de
tener que definirlas previamente:
Las funciones max y min dan respectivamente el valor mayor y menor de una lista.
La función len devuelve cuantos elementos hay en su argumento. Si el argumento es una
cadena devuelve el número de caracteres que hay en la cadena.
Funciones que permiten convertir valores de un tipo a otro: int(), float(), y str().
En Python el paso de argumentos a una función se hace por referencia, de manera que las
modificaciones que se hagan sobre los argumentos se mantienen después de la llamada y ejecución de la
función.

Importación de módulos
Python dispone de una amplia variedad de módulos o librerías. Los módulos son programas que amplían
las funciones y clases de Python para realizar tareas específicas. Los módulos tienen extensión py.

Para poder utilizarlas, hay que importarlas previamente, lo cual se puede hacer de varias formas:

Importar todo el módulo mediante la palabra reservada import.

De esta manera para utilizar un elemento hay que usar el nombre del módulo seguido de un punto (.) y
el nombre del elemento que se desee obtener.

Para conocer las operaciones disponibles de un módulo, se puede usar el comando dir.

Importar solo algunos elementos del módulo.

Mediante la estructura from nombre_modulo import lista_elementos, los elementos importados se usan
directamente por su nombre.
Importar y definir un alias.

Importar todo el módulo mediante la palabra reservada import y definir un alias mediante la palabra
reservada as, de manera que para usar un elemento hay que utilizar el nombre del módulo seguido de un
punto (.) y el nombre del elemento que se desee obtener.

Gestión de archivos
Apertura

Para abrir un archivo en Python se usará la función open, que recibe el nombre del archivo a abrir. Por
defecto, si no se indica nada, el archivo se abre en modo lectura.
La función open abrirá el archivo con el nombre indicado. Si no tiene éxito, se lanzará una excepción. Si
se ha podido abrir el archivo correctamente, la variable asignada a la apertura permitirá manipularlo.

Lectura

La operación más sencilla que se debe realizar sobre un archivo es leer su contenido. Para procesarlo
línea por línea, es posible hacerlo de la siguiente forma:
Además, usando la función readlines es posible recuperar de una sola vez todo el contenido del archivo
estructurado en forma de líneas.

En este caso, la variable líneas tendrá una lista de cadenas con todas las líneas del archivo. Téngase en
cuenta que es posible eliminar los saltos de línea

Apertura para escritura

Si se quiere abrir un archivo en modo escritura, hay que indicar una w como segundo parámetro de la
función open. En caso de que no exista el archivo se crea y, si existe, se pierde la información que
hubiera.

Cierre

Al terminar de trabajar con un archivo, se debe cerrar, ya que lo que se haya escrito no se guardará
realmente hasta no cerrar el archivo. Para ello, se usa close.

Apertura con posicionamiento

También es posible abrir un archivo en modo escritura posicionándose al final del mismo. Para ello, se
usa la opción a con la función open. En este caso se crea el archivo si no existe, pero en caso de que
exista se posiciona al final, manteniendo el contenido original.
Caso práctico
Considera un sistema de cifrado en el que se sustituye cada letra en el texto original por otra que se
encuentra un número fijo de posiciones más adelante en el alfabeto. Por ejemplo, si el desplazamiento es
3 posiciones y se considera la letra A, entonces sería sustituida por la letra D, que se encuentra situada 3
lugares a la derecha de la A. Se considera que el alfabeto es circular por lo que a continuación de la Z
comienza la letra A. Solo se codifican las letras, el resto de símbolos se mantienen.
Una vez cifrado el texto, si este contiene más de una palabra, se reordenan las palabras cifradas,
moviendo cada palabra m posiciones hacia la derecha. Así, la palabra que ocupa la posición 1 se mueve
a la posición m+1, y así sucesivamente —la palabra que ocupa la posición n se moverá a la posición
m—.
Se pide implementar un programa en Python que solicite al usuario que introduzca por teclado un texto
a codificar, dos números que representan el desplazamiento de letras y el desplazamiento de las palabras
codificadas. Como resultado, el programa mostrará por pantalla el mensaje codificado. Se deben hacer
las comprobaciones necesarias sobre la entrada, es decir, es una cadena y 2 números.
def cifrar(texto,desp,desp2):
muestra = ""
texto = convertir(texto,desp2)
palabras = texto.split(' ')
copia = palabras[:]
indices = []
for i in range(0,len(palabras)):
indices.append((i + int(desp)) % len(palabras))
copia[indices[i]] = palabras[i]
for j in range(0,len(copia)):
muestra += "".join(copia[j])+" "
print ("\nTexto cifrado: "+ muestra)

def convertir(text,desp2):
resul = ""
for car in text:
if car.isalpha():
if car.islower():
resul += chr((ord(car) - 97 + int(desp2)) % 26 + 97)
if car.isupper():
resul += chr((ord(car) - 65 + int(desp2)) % 26 + 65)
else:
resul += car
return resul

if __name__ == "__main__":
texto = input("Texo a cifrar: ")
desp2 = input("Desplazamiento letra: ")
desp = input("Desplazamiento palabra: ")
if desp.isdigit() and desp2.isdigit():
cifrar(texto,desp,desp2)
else:
print ("El desplazamiento ha de ser un digito")
Fundamentos de bases de datos relacionales

Introducción
En las últimas décadas, el mecanismo más utilizado para almacenar la información en los sistemas
informáticos ha sido las bases de datos relacionales. Ofrecen un modelo estable, con variedad de
herramientas de desarrollo y altos niveles de seguridad. Sin embargo, se podría decir que el elemento
clave del éxito de este modelo ha sido que dispone de un lenguaje estándar para poder realizar consultas
( SQL). Actualmente muchas fuentes de información se encuentran almacenadas en bases de datos
relacionales.
Por otro lado, dentro del fenómeno Big Data, han surgido nuevos modelos de persistencia de datos
denominados genéricamente como bases de datos NoSQL que, aunque no siguen el modelo relacional ,
sí guardan algunas similitudes en cuanto a sus lenguajes de consulta y conceptos usados. Es por ello
que el conocimiento de las bases de datos relacionales y de SQL facilita la comprensión y aprendizaje
de estos nuevos modelos.
En esta unidad se estudiará, en primer lugar, el modelo relacional que constituye el fundamento teórico
en el que se asientan las bases de datos relacionales. A continuación, se introducirán los elementos
principales del lenguaje SQL que permiten a un usuario gestionar una base de datos relacional . También
se presentará SQLiteStudio , una aplicación que implementa el lenguaje SQLite —una implementación
de SQL ligera muy utilizada en ámbitos como la telefonía móvil— y se mostrará cómo usarla desde
Python.

El modelo relacional
El modelo relacional es un mecanismo de representación de la información que se basa en el concepto
de relación . Formalmente una relación se constituye por dos elementos:

 Esquema . Representa la estructura de la información, indicando qué tipo de información se va


a gestionar. Un esquema, a su vez, se compone de un nombre de la relación y de un conjunto de
atributos de la relación.
Un conjunto de instancias. Representa la aplicación del esquema en un conjunto de datos o
información concreta.
Dominio , atributo e instancia

Un dominio representa un conjunto de valores de un tipo de datos que son atómicos —no pueden
descomponerse más—; por ejemplo: el dominio de los enteros, los reales…

En algunas ocasiones, pueden existir instancias en las que algunos atributos no tomen un valor concreto
y se representan con un valor especial denominado valor nulo. El valor nulo representa un valor
desconocido. En el ejemplo anterior, si se hubiera considerado el atributo “teléfono”, podría darse el
caso de algún empleado que no tenga teléfono y se consignaría el valor nulo.

Algunas características sobre las relaciones y sus tuplas son:

Atomicidad. Los valores de los atributos deben ser atómicos, es decir, no se pueden descomponer más.
Unicidad de las tuplas. No pueden existir 2 tuplas con los mismos valores, dado que las
instancias son un conjunto. En un conjunto solo hay elementos únicos, no repetidos.
Tuplas sin orden. Las tuplas no están ordenadas debido a su definición como un conjunto. En
un conjunto no existe un orden entre sus elementos.
Atributos sin orden. En los atributos de una relación no hay un orden definido, pues se trata de
un conjunto. En un conjunto no existe un orden entre sus elementos.
Se denomina grado de una relación al número de atributos que pertenecen a su esquema y cardinalidad
al número de tuplas definidas en la relación. En el ejemplo anterior, el grado de la relación es 5 y su
cardinalidad 4. (5 atributos y 4 tuplas).
Un atributo es el nombre que recibe un dominio en el contexto de una relación. Por ejemplo, el atributo
nombre de una persona correspondería a una cadena o conjunto de caracteres.

En algunas ocasiones, pueden existir instancias en las que algunos atributos no tomen un valor concreto
y se representan con un valor especial denominado valor nulo. El valor nulo representa un valor
desconocido. En el ejemplo anterior, si se hubiera considerado el atributo “teléfono”, podría darse el
caso de algún empleado que no tenga teléfono y se consignaría el valor nulo.
Algunas características sobre las relaciones y sus tuplas son:
Atomicidad. Los valores de los atributos deben ser atómicos, es decir, no se pueden descomponer más.
Unicidad de las tuplas. No pueden existir 2 tuplas con los mismos valores, dado que las
instancias son un conjunto. En un conjunto solo hay elementos únicos, no repetidos.
Tuplas sin orden. Las tuplas no están ordenadas debido a su definición como un conjunto. En
un conjunto no existe un orden entre sus elementos.
Atributos sin orden. En los atributos de una relación no hay un orden definido, pues se trata de
un conjunto. En un conjunto no existe un orden entre sus elementos.
Se denomina grado de una relación al número de atributos que pertenecen a su esquema y cardinalidad
al número de tuplas definidas en la relación. En el ejemplo anterior, el grado de la relación es 5 y su
cardinalidad 4. (5 atributos y 4 tuplas).

Finalmente, una instancia de una relación consiste en una tupla de valores concretos del dominio de
datos asociado a los atributos de la relación. Por ejemplo, “Juan” es una cadena y representa un valor
concreto para el atributo “Nombre”.

Por ejemplo, si se considera representar la información acerca de los empleados de una empresa
mediante el modelo relacional, las instancias de la relación son tuplas de valores concretos para los
atributos de la relación:
(“Juan”, “Rodríguez Rojo”, 34, “4559999F”,30000)
(“Pedro”, “Sánchez Sánchez”, 54, “5444545E”,”45000”)
(“Isabel”, “Leyva Azul”, 36, “6667733R”,”35000”)
(“Jaime”, “García Redondo”, 39, “3456344T”,”39000”)

En algunas ocasiones, pueden existir instancias en las que algunos atributos no tomen un valor concreto
y se representan con un valor especial denominado valor nulo. El valor nulo representa un valor
desconocido. En el ejemplo anterior, si se hubiera considerado el atributo “teléfono”, podría darse el
caso de algún empleado que no tenga teléfono y se consignaría el valor nulo.
Algunas características sobre las relaciones y sus tuplas son:
Atomicidad. Los valores de los atributos deben ser atómicos, es decir, no se pueden descomponer más.
Unicidad de las tuplas. No pueden existir 2 tuplas con los mismos valores, dado que las
instancias son un conjunto. En un conjunto solo hay elementos únicos, no repetidos.
Tuplas sin orden. Las tuplas no están ordenadas debido a su definición como un conjunto. En
un conjunto no existe un orden entre sus elementos.
Atributos sin orden. En los atributos de una relación no hay un orden definido, pues se trata de
un conjunto. En un conjunto no existe un orden entre sus elementos.
Se denomina grado de una relación al número de atributos que pertenecen a su esquema y cardinalidad
al número de tuplas definidas en la relación. En el ejemplo anterior, el grado de la relación es 5 y su
cardinalidad 4. (5 atributos y 4 tuplas).

Claves: conceptos y tipos

Una superclave de una relación es un subconjunto de los atributos del esquema tal que no puede haber
dos tuplas de la relación que tengan la misma combinación de valores para los atributos del subconjunto.
Así, una superclave permite identificar las tuplas de una relación.
Toda relación tiene al menos una superclave formada por todos los atributos de su esquema —se debe al
hecho de que una relación no puede tener tuplas repetidas—.
En el ejemplo anterior serían superclaves: {Nombre, Apellidos, Edad, DNI,Sueldo}, {DNI, Nombre,
Apellidos}, {DNI}

Importante:

El conjunto de atributos {PlantaDespacho, NumDespacho} de la relación EMPLEADOS constituye una


clave foránea que se refiere a la clave primaria de la relación DESPACHOS y que indica para cada
empleado el despacho donde trabaja.
 El atributo DNIJefe de la relación EMPLEADOS es una clave foránea que se refiere a la clave
primaria de la misma relación que indica, para cada empleado, quién es su jefe.
 El número de atributos de una clave foránea y de la clave primaria a la que referencia deben ser
iguales.
 Debe ser posible establecer una correspondencia entre los atributos de una clave foránea y los
atributos de la clave primaria a la que referencia.
 Los dominios de los atributos de la clave foránea deben coincidir con los dominios de los
atributos de la clave primaria a la que referencian. En el ejemplo anterior, la clave foránea
{PlantaDespacho, NumDespacho} y la clave primaria {Planta, Número} cumplen estas
propiedades.
 Un atributo de una relación podría formar parte tanto de la clave primaria como de una clave
foránea de la relación.
Se denomina clave candidata de una relación a una superclave de la relación que cumple que ningún
subconjunto propio sea superclave. Es decir, que si se elimina algún atributo de la clave candidata ya no
es superclave. Como toda relación tiene al menos una superclave, entonces tiene al menos una clave
candidata.
En el ejemplo anterior, la superclave {DNI} es clave candidata. Sin embargo, la superclave {DNI,
Nombre, Apellidos} no lo es, pues si se elimina, por ejemplo, el atributo “Apellidos”, el conjunto
{DNI,Nombre} sigue siendo superclave
Importante:
El conjunto de atributos {PlantaDespacho, NumDespacho} de la relación EMPLEADOS constituye una
clave foránea que se refiere a la clave primaria de la relación DESPACHOS y que indica para cada
empleado el despacho donde trabaja.
El atributo DNIJefe de la relación EMPLEADOS es una clave foránea que se refiere a la clave
primaria de la misma relación que indica, para cada empleado, quién es su jefe.
El número de atributos de una clave foránea y de la clave primaria a la que referencia deben
ser iguales.
Debe ser posible establecer una correspondencia entre los atributos de una clave foránea y los
atributos de la clave primaria a la que referencia.
Los dominios de los atributos de la clave foránea deben coincidir con los dominios de los
atributos de la clave primaria a la que referencian. En el ejemplo anterior, la clave foránea
{PlantaDespacho, NumDespacho} y la clave primaria {Planta, Número} cumplen estas
propiedades.
Un atributo de una relación podría formar parte tanto de la clave primaria como de una clave
foránea de la relación.
Entre todas las claves candidatas de una relación, se elige una de ellas como la clave cuyos valores se
utilizarán para identificar las tuplas de una relación. Esta clave recibe el nombre de clave primaria. El
resto de claves candidatas no elegidas se las denomina claves alternativas. Toda relación tiene al menos
una clave primaria, dado que siempre tiene al menos una clave candidata.
Es posible que una clave candidata o una clave primaria conste de más de un atributo.
Por ejemplo, considérese una relación para representar tipos de tornillos cuyos atributos son marca,
ancho y largo, de manera que una misma marca puede tener diferentes tipos de tornillos con el mismo
largo y ancho. En este caso, una clave candidata estaría formada por {marca, ancho, largo}

El conjunto de atributos {PlantaDespacho, NumDespacho} de la relación EMPLEADOS constituye una


clave foránea que se refiere a la clave primaria de la relación DESPACHOS y que indica para cada
empleado el despacho donde trabaja.
 El atributo DNIJefe de la relación EMPLEADOS es una clave foránea que se refiere a la clave
primaria de la misma relación que indica, para cada empleado, quién es su jefe.
 El número de atributos de una clave foránea y de la clave primaria a la que referencia deben ser
iguales.
 Debe ser posible establecer una correspondencia entre los atributos de una clave foránea y los
atributos de la clave primaria a la que referencia.
 Los dominios de los atributos de la clave foránea deben coincidir con los dominios de los
atributos de la clave primaria a la que referencian. En el ejemplo anterior, la clave foránea
{PlantaDespacho, NumDespacho} y la clave primaria {Planta, Número} cumplen estas
propiedades.
 Un atributo de una relación podría formar parte tanto de la clave primaria como de una clave
foránea de la relación.

En general, en un contexto real, es necesario gestionar más de una relación y entre las relaciones
consideradas existen vínculos o conexiones. Para modelizar estos vínculos, el modelo relacional dispone
de las claves foráneas.
Una clave foránea de una relación permite establecer conexiones entre las tuplas de varias relaciones.
En este sentido, una clave foránea está formada por el conjunto de atributos de una relación que
referencia la clave primaria de otra relación —o incluso de la misma relación—. Dado que las claves
foráneas establecen una conexión con la clave primaria que referencian, los valores de una clave foránea
deben estar presentes en la clave primaria correspondiente, o bien deben ser valores nulos.
Por ejemplo, considérense las relaciones EMPLEADOS y DESPACHOS que permiten modelizar la
información acerca de un empleado de una empresa y sobre el despacho en el que se encuentran los
empleados. Para la primera relación se han considerado los atributos: DNI, Nombre, Apellidos, DNIJefe,
PlantaDespacho, NumDespacho y para la segunda relación se consideran los atributos: Planta, Número,
Plazas. En la relación EMPLEADOS, la clave primaria podría ser {DNI} y en la relación DESPACHOS
la clave primaria podría ser {Planta, Numero}.
El conjunto de atributos {PlantaDespacho, NumDespacho} de la relación EMPLEADOS constituye una
clave foránea que se refiere a la clave primaria de la relación DESPACHOS y que indica para cada
empleado el despacho donde trabaja.
El atributo DNIJefe de la relación EMPLEADOS es una clave foránea que se refiere a la clave
primaria de la misma relación que indica, para cada empleado, quién es su jefe.
El número de atributos de una clave foránea y de la clave primaria a la que referencia deben
ser iguales.
Debe ser posible establecer una correspondencia entre los atributos de una clave foránea y los
atributos de la clave primaria a la que referencia.
Los dominios de los atributos de la clave foránea deben coincidir con los dominios de los
atributos de la clave primaria a la que referencian. En el ejemplo anterior, la clave foránea
{PlantaDespacho, NumDespacho} y la clave primaria {Planta, Número} cumplen estas
propiedades.
Un atributo de una relación podría formar parte tanto de la clave primaria como de una clave
foránea de la relación.
Operaciones en el modelo relacional
Las operaciones del modelo relacional deben permitir manipular la información representada en las
relaciones. En este sentido se definen dos tipos de operaciones:
Actualización. Permite modificar la información representada en una relación. Pueden ser de
tres tipos:
Inserción para añadir nuevas tuplas a una relación.
Borrado para eliminar tuplas de una relación.
Modificación para alterar los valores de una tupla de la relación.
Consulta. Permite recuperar la información representada en las relaciones y que se encuentra
almacenada en las tuplas.
Para implementar estas operaciones se han definidos lenguajes relacionales.
Integridad en el modelo relacional
Para mantener la integridad y la consistencia de la información que se representa en una relación se
define un conjunto de reglas de integridad:
Unicidad de la clave primaria
Establece que toda clave primaria de una relación no debe tener valores repetidos. Se debe garantizar el
cumplimiento de esta regla en todas las inserciones y modificaciones que afecten a atributos que
pertenezcan a la clave primaria de la relación.
Entidad de la clave primaria
Establece que los atributos de la clave primaria de una relación no pueden tener valores nulos. Se debe
garantizar el cumplimiento de esta regla en todas las inserciones y modificaciones que afecten a
atributos que pertenezcan a la clave primaria de la relación.
Integridad referencial
Establece que todos los valores que toma una clave foránea deben ser nulos o valores que existen en la
clave primaria que referencia.
Operaciones
Se debe tener en cuenta en las siguientes operaciones:
Inserciones en una relación que tenga una clave foránea.
Modificaciones que afecten a atributos que pertenezcan a la clave foránea de una relación.
Borrados en relaciones referenciadas por otras relaciones.
Modificaciones que afecten a atributos que pertenezcan a la clave primaria de una relación
referenciada por otras relaciones.
Políticas de actuación
Existen dos políticas de actuación para mantener la integridad referencial cuando se realiza alguna de las
operaciones anteriormente señaladas:
Rechazar cualquier operación que provoque el incumplimiento de alguna de las reglas
anteriores.
Llevar a cabo la operación y realizar posteriormente aquellas acciones necesarias para que las
relaciones mantengan la integridad. Es posible aplicarlo:
Cuando se borra una tupla que tiene una clave primaria referenciada.
Cuando se modifican los valores de los atributos de la clave primaria de una tupla que
es referenciada.
Para los casos anteriores existen 3 políticas de actuación:
Restricción. Consiste en no aceptar la operación de actualización siempre que afecte a una
clave primaria referenciada por una clave foránea.
Actualización en cascada. Se permite la operación de actualización y se lleva las mismas
operaciones sobre las tuplas que las referencian en otras relaciones.
Anulación. Se permite la operación de actualización y se ponen valores nulos a los atributos de
la clave foránea de las tuplas que la referencian.
Integridad del dominio
Establece que todos los valores no nulos para un determinado atributo deben ser del dominio declarado
para dicho atributo y que los operadores que se pueden aplicar sobre los valores dependen del dominio
de estos valores..

El lenguaje SQL
Las bases de datos relacionales se basan en el modelo relacional para almacenar y estructurar la
información. SQL es un lenguaje relacional que permite manipular bases de datos relacionales.
SQL es un lenguaje estándar ANSI/ISO de definición, manipulación y control de bases de datos
relacionales. Se caracteriza por:
Es un lenguaje declarativo basado en el álgebra relacional.
Está soportado por la mayoría de los sistemas relacionales comerciales.
Se puede utilizar de manera interactiva o embebido en un programa.
En el modelo relacional se estructura la información en base a los conceptos:
Relación.
Atributos.
Tuplas.
En SQL se consideran conceptos similares, pero con una nomenclatura diferente:
Tablas.
Columnas.
Filas.
A continuación, se van a estudiar los elementos principales de lenguaje SQL.

Creación de tablas en SQL


Para crear una tabla , se utiliza la sentencia CREATE TABLE:

CREATE TABLE nombre_tabla (definición_columna[, definición_columna...]


[, restricciones_tabla]);
La definición de una columna consta del nombre de la columna, un tipo de datos
predefinido, un conjunto de definiciones por defecto y restricciones de columna.
A continuación, se explica cada elemento.
a) Tipos de datos
Los principales tipos de datos predefinidos en SQL que pueden asociarse a una columna
Para trabajar con el tiempo, se usa la siguiente nomenclatura:
YEAR (0001..9999)
MONTH (01..12)
DAY (01..31)
HOUR (00..23)
MINUT (00..59)
SECOND (00..59.precisión)

Una columna fecha_nacimiento podría ser del tipo DATE y tomar el valor ‘1978-12-25’.

 Una columna inicio_pelicula podría ser del tipo TIME y tomar el valor ‘17:15:00.000000’.
 Una columna entrada_clase podría ser de tipo TIMESTAMP y tomar el valor ‘1998-7-8 9:30:05’.

b) Definiciones por defecto

La opción def_defecto permite especificar valores por omisión mediante la sentencia DEFAULT
(literal|función|NULL) donde:
Si se elige la opción NULL, indica que la columna debe admitir valores nulos.
Si se elige la opción literal, señala que la columna tomará el valor indicado por el literal.
Si se elige la opción función, se indicará alguna de las funciones siguientes.
Función Descripción

{USER|CURRENT_USER} Identificador del usuario actual

SESSION_USER Identificador del usuario de esta sesión

Identificador del usuario del sistema


SYSTEM_USER
operativo

CURRENT_DATE Fecha actual

CURRENT_TIME Hora actual

CURRENT_TIMESTAMP Fecha y hora actuales

c) Restricciones por columna


Cuando se define una columna, además de especificar su nombre y tipo, se pueden establecer un
conjunto de restricciones que siempre se tienen que cumplir (figura 3.3.):

Restricciones de columna

Restricción Descripción

NOT NULL La columna no puede tener valores nulos

UNIQUE La columna no puede tener valores repetidos. Es una clave alternativa

PRIMARY KEY La columna no puede tener valores repetidos ni nulos. Es la clave primaria

REFERENCES tabla
La columna es la clave foránea de la columna de la tabla especificada
[(columna)]

CHECK (condiciones) La columna debe cumplir las condiciones especificadas

A las restricciones se les puede poner un nombre de la siguiente manera, por ejemplo:
[CONSTRAINT nombre_restricción] CHECK (condiciones).

d) Restricciones por tabla

Cuando se han definido las columnas de una tabla, a continuación, se pueden especificar restricciones
sobre toda la tabla, que siempre se deberán cumplir:
Restricciones de tabla

Restricción Descripción

El conjunto de las columnas especificadas


UNIQUE
no puede tener valores repetidos. Es una
(columna [, columna…])
clave alternativa

El conjunto de las columnas especificadas


PRIMARY KEY (columna [, columna…]) no puede tener valores repetidos. Es una
clave alternativa

FOREIGN KEY (columna [, columna..]) El conjunto de las columnas especificadas


REFERENCES tabla [(columna2 no puede tener valores repetidos. Es una
[,columna2…])] clave alternativa

La tabla debe cumplir las condiciones


CHECK (condiciones)
especificadas

A las restricciones se les puede poner un nombre de la siguiente manera, por ejemplo:
[CONSTRAINT nombre_restricción] CHECK (condiciones).
e) Ejemplos de creación de tablas
Create table sucursal
(nombre_sucursal VARCHAR2(15) CONSTRAINT suc_PK PRIMARY KEY,
ciudad CHAR(20) NOT NULL CONSTRAINT cl_UK UNIQUE,
activos NUMBER(12,2) default 0);

Create table cliente


(dni VARCHAR2(9) NOT NULL,
nombre_cliente CHAR(35) NOT NULL,
domicilio CHAR(50) NOT NULL,
CONSTRAINT cl_PK PRIMARY KEY (dni));

Create table cuenta


(numero_cuenta CHAR (20) PRIMARY KEY,
nombre_sucursal char(15) REFERENCES sucursal,
saldo NUMBER(12,2) default 100,
CONSTRAINT imp_minimo CHECK(saldo >=100))
Create table impositor
(dni CHAR(9) CONSTRAINT imp_dni_FK REFERENCES cliente,
numero_cuenta CHAR(20) NOT NULL,
CONSTRAINT imp_PK PRIMARY KEY (dni, numero_cuenta),
CONSTRAINT imp_ct_FK FOREIGN KEY (numero_cuenta) REFERENCES cuenta)

f) Claves foráneas
Cuando se define una clave foránea, se pueden especificar las políticas de borrado y modificación de
filas que tiene una clave primaria referenciada por claves foráneas de la siguiente forma:

FOREIGN KEY clave_secundaria REFERENCES tabla [(clave_primaria)]


[ON DELETE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]
[ON UPDATE {NO ACTION | CASCADE | SET DEFAULT | SET NULL}]

Donde:
NO ACTION indica no realizar ninguna acción: un intento de borrar o actualizar un valor de
clave primaria no será permitido si en la tabla referenciada hay un valor de clave foránea
relacionado.
CASCADE representa la actualización en cascada. Borra o actualiza el registro en la tabla
referenciada y automáticamente borra o actualiza los registros coincidentes en la tabla actual.
SET NULL borra o actualiza el registro en la tabla referenciada y establece en NULL la/s
columna/s de clave foránea en la tabla actual.
SET DEFAULT indica que se ponga el valor especificado por defecto.

Por ejemplo:
Create table cuenta
(numero_cuenta CHAR (20) PRIMARY KEY,
nombre_sucursal char(15)
CONSTRAINT ct_FK REFERENCES sucursal on delete set null,
saldo NUMBER(12,2) default 100,
CONSTRAINT imp_minimo CHECK(saldo >=100))

Create table impositor


(dni CHAR(9) CONSTRAINT imp_dni_FK REFERENCES cliente on delete cascade,
numero_cuenta CHAR(20),
CONSTRAINT imp_PK PRIMARY KEY (dni, numero_cuenta),
CONSTRAINT imp_ct_FK FOREIGN KEY (numero_cuenta) REFERENCES cuenta on delete cascade)
Modificación de tablas
Para modificar una tabla, se utiliza la sentencia ALTER TABLE:
ALTER TABLE nombre_tabla {acción_modificar_columna |acción_modif_restricción_tabla};

Donde:
acción_modificar_columna puede ser:
{ADD [COLUMN] columna def_columna |
ALTER [COLUMN] columna {SET def_defecto|DROP DEFAULT}|
DROP [COLUMN ] columna {RESTRICT|CASCADE}}
acción_modif_restricción_tabla puede ser:
{ADD restricción|DROP CONSTRAINT restricción {RESTRICT|CASCADE}}

Así pues, las acciones de modificación que pueden realizarse sobre una tabla son:

Añadir atributos a una tabla.


alter table R add Atributo Dominio [propiedades]
Eliminar atributos de una tabla.
alter table R drop COLUMN Atributo
No se puede eliminar la única columna de una tabla.
Si la columna interviene en una constraint dará error:
alter table R drop Atributo CASCADE CONSTRAINTS
Modificar atributos en una tabla.
alter table R modify (Atributo Dominio [propiedades])

Renombrar atributos de una tabla.


alter table R rename column Atributo1 to Atributo2

Añadir restricciones a una tabla.


alter table R add CONSTRAINT nombre Tipo (columnas)

Eliminar restricciones de una tabla.


alter table R drop {PRIMARY KEY|UNIQUE(campos)|
CONSTRAINT nombre [CASCADE]}
La opción CASCADE hace que se eliminen las restricciones de integridad que dependen de la eliminada.

Desactivar restricciones.
alter table R disable CONSTRAINT nombre [CASCADE]

Activar restricciones
alter table R enable CONSTRAINT nombre

ALTER TABLE cuenta ADD comision NUMBER(4,2);


ALTER TABLE cuenta ADD fecha_apertura DATE;
ALTER TABLE cuenta DROP COLUMN nombre_sucursal;
ALTER TABLE cuenta MODIFY comision DEFAULT 1.5;
ALTER TABLE cliente MODIFY nombre_cliente NULL;
ALTER TABLE sucursal ADD CONSTRAINT cd_UK UNIQUE(ciudad);

Otras operaciones sobre tablas


Borrar una tabla
Para borrar una tabla, se utiliza la sentencia DROP TABLE:
DROP TABLE nombre_tabla{RESTRICT|CASCADE};
Donde:
La opción RESTRICT indica que la tabla no se borrará si está referenciada.
La opción CASCADE indica que todo lo que referencie a la tabla se borrará con esta.
Descripción de una tabla
describe R
Renombrar una tabla
rename R to S
Borrar contenidos
truncate table R

Índices

Los índices permiten que las bases de datos aceleren las operaciones de consulta y ordenación sobre los
campos a los que el índice hace referencia.
La mayoría de los índices se crean de manera implícita, como consecuencia de las restricciones
PRIMARY KEY y UNIQUE.
Se pueden crear explícitamente para aquellos campos sobre los cuales se realizarán búsquedas e
instrucciones de ordenación frecuente., se muestra un ejemplo.
CREATE [unique] INDEX NombreIndice ON NombreTabla(col1,...,colk);
Consultas y recuperación de información en SQL
Consultas sobre una sola tabla
Para hacer consultas sobre una tabla, se utiliza la sentencia SELECT:
SELECT nombre_columna_a_seleccionar [[AS] col_renombrada]
[,nombre_columna_a_seleccionar [[AS] col_renombrada]...] FROM tabla_a_consultar [[AS]
tabla_renombrada];
Obsérvese que la palabra clave AS permite renombrar las columnas que se quieren seleccionar o las
tablas que se quieren consultar. Esta palabra es opcional y muchas veces se sustituye por un espacio en
blanco.
Por ejemplo, si se quieren seleccionar las columnas código, nombre, dirección y ciudad de la tabla
clientes, se usaría la sentencia:
SELECT codigo_cli, nombre_cli, direccion, ciudad FROM clientes;
Sin embargo, si se quieren recuperar todas las columnas de la tabla se usa el símbolo *, en vez de listar
todas las columnas:
SELECT * FROM clientes;
Si se quieren seleccionar qué filas han de ser recuperadas, hay que utilizar en la consulta SELECT la
palabra reservada WHERE:
SELECT [DISTINCT|ALL] nombre_columnas_a_seleccionar FROM tabla_a_consultar
[WHERE condiciones];
Téngase en cuenta que:
La cláusula WHERE permite recuperar solo aquellas filas que cumplen la condición
especificada.
La cláusula DISTINCT permite ordenar que nos muestre las filas resultantes sin repeticiones.
La opción por defecto es ALL, que indica que muestre todas las filas.
Para construir las condiciones de la cláusula WHERE, es necesario usar operadores de
comparación o lógicos: < (menor), > (mayor), = (igual), <= (menor o igual), >= (mayor o igual),
<> (distinto), AND (conjunción de condiciones), OR (disyunción de condiciones), NOT
(negación).
Por ejemplo, si se quieren recuperar los diferentes sueldos de la tabla empleados:
SELECT DISTINCT sueldo FROM empleados;
Y si se quieren recuperar los empleados de la tabla empleados, cuyo sueldo es mayor de 1000 euros:
SELECT * FROM empleados WHERE sueldo> 1000;
Una subconsulta es una consulta incluida dentro de otra consulta, que aparecerá como parte de una
cláusula WHERE o HAVING —se verá más adelante—.
Por ejemplo, se quiere obtener los proyectos de la tabla proyectos que se corresponden con un cliente
que tiene como NIF el número “444555-E”:
SELECT * FROM proyectos WHERE codigo_cliente = (SELECT código_cli FROM clientes WHERE
nif=“444555-E”)
En la condición que aparece en la clausula WHERE, se puede utilizar un conjunto de predicados
predefinidos para construir las condiciones:
BETWEEN

Expresa que se quiere encontrar un valor entre unos límites concretos:


SELECT nombre_columnas_a_seleccionar FROM tabla_a_consultar WHERE columna BE
TWEEN límite1 AND límite2;
Por ejemplo, se quieren recuperar todos los empleados cuyos sueldos están entre 1000 y 2000 euros:
SELECT codigo_empl FROM empleados WHERE sueldo BETWEEN 1000 and 2000;
IN

Comprueba si un valor coincide con los elementos de una lista (IN) o no coincide (NOT IN):
SELECT nombre_columnas_a_seleccionar FROM tabla_a_consultar WHERE columna [NO
T] IN (valor1, ..., valorN);
Por ejemplo, se quieren recuperar todos los clientes que viven en Madrid y Zaragoza:
SELECT * FROM clientes WHERE ciudad IN (‘Madrid’, ‘Zaragoza’);
LIKE

Comprueba si una columna de tipo carácter cumple una condición determinada.


SELECT nombre_columnas_a_seleccionar FROM tabla_a_consultar WHERE columna LIK
E condición;

Consultas sobre más de una tabla


En la cláusula FROM, es posible especificar más de una tabla cuando se quieren consultar columnas de
tablas diferentes. Existen varios casos:
Combinacion:
Se crea una sola tabla a partir de las tablas especificadas, haciendo coincidir los valores de las columnas
relacionadas de las tablas.
SELECT nombre_columnas_a_seleccionar FROM tabla1 JOIN tabla2
{ON condiciones|USING (columna [, columna...])} [WHERE condiciones];
La opción ON permite expresar condiciones con cualquiera de los operadores de comparación sobre las
columnas especificadas.
Es posible utilizar una misma tabla dos veces usando alias diferentes para diferenciarlas.
Puede ocurrir que las tablas consideradas tengan columnas con los mismos nombres. En este
caso, es obligatorio diferenciarlas especificando en cada columna a qué tabla pertenecen.
Por ejemplo, se quiere obtener el NIF del cliente y el precio de los proyectos desarrollados para el
cliente con código 30.
SELECT p.precio, c.nif FROM clientes c JOIN proyectos p ON c.codigo_cli = p.codigo_cliente
WHERE c.codigo_cli = 30;
Alternativamente se podría obtener con la siguiente consulta:
SELECT p.precio, c.nif FROM clientes c, proyectos p WHERE c.codigo_cli = p.codigo_cliente
AND c.codigo_cli = 20;
Por ejemplo, si se quieren los códigos de los proyectos que son más caros que el proyecto con código 30:
SELECT p1.codigo_proyec FROM proyectos p1 JOIN proyectos p2 ON p1.precio > p2.precio WHERE
p2.codigo_proyec = 30;
Combinacion natural
Consiste en una combinación en la que se eliminan las columnas repetidas.
SELECT nombre_columnas_a_seleccionar FROM tabla1 NATURAL JOIN tabla2
[WHERE condiciones];
Por ejemplo, si se quiere obtener los empleados cuyo departamento se encuentra situado en Madrid:
SELECT * FROM empleados NATURAL JOIN departamentos WHERE ciudad = ‘Madrid';
De forma equivalente se podría consultar:
SELECT * FROM empleados JOIN departamentos USING (nombre_dep, ciudad_dep)
WHERE ciudad = ‘Madrid';
Combinacion interna vs. Externa
Interna(inner join). Solo se consideran las filas que tienen valores idénticos en las columnas de las
tablas que compara.
SELECT nombre_columnas_a_seleccionar FROM t1 [NATURAL] [INNER] JOIN t2
{ON condiciones||USING (columna [,columna...])} [WHERE condiciones];
Externa(outer join). Se consideran los valores de la tabla derecha, de la izquierda o de ambas
tablas.
SELECT nombre_columnas_a_seleccionar FROM t1 [NATURAL] [LEFT|RIGHT|FULL]
[OUTER] JOIN t2 {ON condiciones| [USING (columna [,columna...])} [WHERE condiciones];
Combinacion de mas de dos tablas
Para combinar más de 2 tablas, basta con añadirlas en el FROM de la consulta y establecer las
relaciones necesarias en el WHERE, o bien combinar las tablas por pares de manera que la resultante
será el primer componente del siguiente par.
Por ejemplo, si se quieren combinar las tablas empleados, proyectos y clientes:
SELECT * FROM empleados, proyectos, clientes WHERE num_proyec = codigo_proyec AND
codigo_cliente = codigo_cli;
O bien:
SELECT * FROM (empleados JOIN proyectos ON num_proyec = codigo_proyec)JOIN clientes ON
codigo_cliente = codigo_cli;
Entre 2 tablas se pueden definir las siguientes operaciones:
Unión

Permite unir los resultados de 2 o más consultas.


SELECT columnas FROM tabla [WHERE condiciones] UNION [ALL] SELECT columnas
FROM tabla[WHERE condiciones];
Obsérvese que la cláusula ALL permite indicar si se quieren obtener todas las filas de la unión (incluidas
las repetidas).
Por ejemplo, si se quiere obtener todas las ciudades que aparecen en las tablas de la base de datos:
SELECT ciudad FROM clientes UNION SELECT ciudad_dep FROM departamentos;
Intersección

Permite hacer la intersección entre los resultados de 2 o más consultas.


SELECT columnas FROM tabla
[WHERE condiciones] INTERSECT [ALL] SELECT columnas FROM tabla
[WHERE condiciones];
Téngase en cuenta que la cláusula ALL permite indicar si se quieren obtener todas las filas de la
intersección (incluidas las repetidas).
Se puede simular usando:
- IN
SELECT columnas FROM tabla WHERE columna IN (SELECT columna FROM tabla
[WHERE condiciones]);
- EXISTS
SELECT columnas FROM tabla WHERE EXISTS (SELECT * FROM tabla WHERE condiciones);
Por ejemplo, si se quiere saber las ciudades de los clientes en las que hay departamentos:
Usando la intersección.
SELECT ciudad FROM clientes INTERSECT SELECT ciudad_dep FROM departamentos;
Usando IN
SELECT c.ciudad FROM clientes c WHERE c.ciudad IN (SELECT d.ciudad_dep FROM departamentos
d);
Usando EXISTS
SELECT c.ciudad FROM clientes c WHERE EXISTS (SELECT * FROM departamentos d WHERE
c.ciudad = d.ciudad_dep;
Diferencia

Permite diferenciar los resultados de 2 o más consultas.


SELECT columnas FROM tabla [WHERE condiciones] EXCEPT
[ALL] SELECT columnas FROM tabla [WHERE condiciones];
Obsérvese que la cláusula ALL permite indicar si se quieren obtener todas las filas de la intersección
(incluidas las repetidas).
Se puede simular usando:
- NOT IN
SELECT columnas FROM tabla WHERE columna NOT IN(SELECT columna FROM tabla
[WHERE condiciones]);
- NOT EXISTS
SELECT columnas FROM tabla WHERE NOT
EXISTS (SELECT *FROM tabla WHERE condiciones);
Por ejemplo, si se quiere saber las ciudades de los clientes en las que no hay departamentos:
Usando la intersección.
SELECT ciudad FROM clientes EXCEPT SELECT ciudad_dep FROM departamentos;
Usando IN
SELECT c.ciudad FROM clientes c WHERE c.ciudad NOT IN (SELECT d.ciudad_dep FROM
departamentos d);
Usando EXISTS
SELECT c.ciudad FROM clientes c WHERE NOT EXISTS (SELECT * FROM departamentos d WHERE
c.ciudad = d.ciudad_dep;

Modificación de tablas en SQL


Operaciones de actualización
Inserccion
Para poder consultar los datos de una base de datos, hay que introducirlos con la sentencia INSERT
INTO VALUES:
INSERT INTO nombre_tabla [(columnas)] {VALUES ({v1|DEFAULT|NULL}, ...,
{vn/DEFAULT/NULL})|<consulta>};
Téngase en cuenta que:
 Los valores v1, v2... vn se deben corresponder con las columnas de la tabla especificada y
deben estar en el mismo orden, a menos que las volvamos a colocar a continuación del
nombre de la tabla. En este último caso, los valores se deben disponer de forma coherente
con el nuevo orden.
 Si se quiere especificar un valor por omisión se usa la palabra reservada DEFAULT y si se
trata de un valor nulo se usa la palabra reservada NULL.
 Obsérvese que, para insertar más de una fila con una sola sentencia, se deben obtener los
datos mediante una consulta a otras tablas.

×
Por ejemplo, si se quiere insertar en una tabla clientes que tiene las columnas :nif, nombre_cli,
codigo_cli, telefono, direccion, ciudad, se podría hacer de dos formas:
INSERT INTO clientes VALUES (10, ‘Mercadona’, ‘122233444-C’, ‘Gran vida 8’, ‘Madrid’,
DEFAULT);
O bien:
INSERT INTO clientes(nif, nombre_cli, codigo_cli, telefono, direccion, ciudad) VALUES (‘122233444-
C’, ‘Mercadona’, 10, DEFAULT, ‘Gran vida 8’, ‘Madrid’);
Insertar un préstamo en la relación Préstamo:
INSERT INTO Prestamo VALUES (‘Navacerrada’, ‘Pepe Pérez’, 125.000)
También es posible obtener los datos mediante una consulta SELECT que actúe como proveedor de
datos:
INSERT INTO Prestamo SELECT * FROM Nuevos_Prestamos
Borrados
Para borrar valores de algunas filas de una tabla, se usa la sentencia DELETE:
DELETE FROM nombre_tabla [WHERE condiciones];
Hay que tener en cuenta que si no se utiliza la cláusula WHERE se borrarán todas las filas de la tabla, en
cambio, si se utiliza WHERE, solo se borrarán aquellas filas que cumplan las condiciones especificadas.
Por ejemplo, si se quieren borrar todas las filas de la tabla proyectos se usaría la sentencia:
DELETE FROM proyectos;
Sin embargo, si solo se quieren borrar las filas de la tabla en las que el valor de la columna cliente vale
12, entonces se usaría la sentencia:
DELETE FROM proyectos WHERE codigo_cliente = 12;
Por ejemplo, para borrar todos los clientes que tengan un préstamo no registrado en la relación Préstamo:
DELETE FROM Clientes WHERE Clientes.NumPrestamo NOT IN(SELECT NumPrestamo FROM
Prestamo)
Modificacion
Para modificar los valores de algunas filas de una tabla se usa la sentencia UPDATE:
UPDATE nombre_tabla SET columna = {expresión|DEFAULT|NULL} [, columna =
{expr|DEFAULT|NULL} ...] WHERE condiciones;
La cláusula SET indica qué columna modificar y los valores que puede recibir, y la cláusula WHERE
especifica qué filas deben actualizarse.
Por ejemplo, si se quiere inicializar el sueldo de todos los empleados del proyecto 2 en 500 euros:
UPDATE empleados SET sueldo = 500 WHERE num_proyec = 2;
La parte WHERE es opcional y, si no se especifica, se actualizarán todas las tuplas de la tabla.
UPDATE Prestamo SET importe=200.000 WHERE NumPrestamo=‘P-170’
La cláusula WHERE admite consultas anidadas. Por ejemplo “Modificar todos los prestamos cuya
sucursal ha sido cerrada a la sucursal ‘Centro’”
UPDATE Prestamo SET sucursal= ‘Centro’ WHERE sucursal IN (SELECT sucursal FROM
Sucursales_Cerradas)
Operaciones sobre tablas
Las funciones de agregación son funciones que permiten realizar operaciones sobre los datos de una
columna. Algunas funciones se muestran en la figura 3.7.

En general, las funciones de agregación se aplican a una columna, excepto COUNT, que se aplica a
todas las columnas de las tablas seleccionadas. Se indica como COUNT (*).
Sin embargo, si se especifica COUNT (distinct columna), solo contará los valores no nulos ni repetidos,
y si se especifica COUNT (columna), solo contaría los valores no nulos.
Por ejemplo, si se quiere contar el número de clientes de la tabla clientes cuya ciudad es Madrid:
SELECT COUNT(*) AS numero_clie FROM clientesWHERE ciudad = ‘Madrid’;
Al realizar una consulta, las filas se pueden agrupar de la siguiente manera:
SELECT nombre_columnas_a seleccionar FROM tabla_a_consultar [WHERE condiciones] GROUP
BY columnas_según_las_cuales_se_quiere_agrupar
[HAVING condiciones_por_grupos] [ORDER BY columna_ordenación [DESC] [, columna [DESC]...]];
Donde:
La cláusula GROUP BY permite agrupar las filas según las columnas indicadas, excepto
aquellas afectadas por funciones de agregación.
La cláusula HAVING especifica las condiciones para recuperar grupos de filas.
Por ejemplo, si se quiere conocer el importe total de los proyectos agrupados por clientes:
SELECT código_cliente, SUM(precio) AS importe FROM clientes GROUP BY codigo_cliente;
Y si solo queremos aquellos clientes con un importe facturado mayor de 10000 euros:
SELECT código_cliente FROM clientes GROUP BY codigo_cliente HAVING SUM(precio)>10000
Una vista es una tabla ficticia —no existen como un conjunto de valores almacenados en la base de
datos— que se construye a partir de una consulta a una tabla real. Para definir una vista se usa la
siguiente sintaxis:
CREATE VIEW nombre_vista [(lista_columnas)] AS (consulta) [WITH CHECK OPTION];

A continuación de donde se indica el nombre de la vista, se pueden especificar los nombres de las
columnas de la vista, se define la consulta que construirá la vista, tras lo que se puede añadir la cláusula
“with check option” para evitar inserciones o actualizaciones excepto en los registros en que la cláusula
WHERE de la consulta se evalúe como true.
Para borrar una vista se utiliza la sentencia DROP VIEW:

DROP VIEW nombre_vista (RESTRICT|CASCADE);


Donde:
La opción RESTRICT indica que la vista no se borrará si está referenciada.
La opción CASCADE indica que todo lo que referencie a la vista se borrará con esta.
Para ilustrar las vistas, se van a considerar las siguientes tablas:

Si se quiere crear una vista que indique para cada cliente el número de pedidos que tiene encargado, se definiría
la vista:

CREATE VIEW pedidos_por_cliente (codigo_cli, num_pedidos) AS (SELECT c.codigo_cli, COUNT(*)


FROM pedidos p, clientes c WHERE p.codigo_cliente = c.codigo_cli GROUP BY c.codigo_cli);
Y se obtendría la vista

SQLitestudio
Para poner en práctica SQL, se va a utilizar SQLiteStudio: un sistema de gestión de bases de datos
relacionales que puede descargarse en:
Descarga:

https://sqlitestudio.pl/index.rvt?act=download
La descarga del enlace anterior es un fichero ejecutable . Da igual si es para un SO Linux, Mac o windows,
no necesita instalacion y es independiente del mismo.

Para ilustrar algunas de las funcionalidades de SQLiteStudio, se va a considerar una base de datos para
gestionar una empresa de arquitectura que tiene sedes en Madrid, Zaragoza y Granada. La información
que se desea almacenar es:
Información a mostrar
Sobre los empleados: código de empleado, nombre y apellido, sueldo, nombre de la ciudad y de su departamento
y el número de proyecto al que están asignados.

Sobre cada departamento: nombre, ciudad donde se encuentran y teléfono. Un departamento con el
mismo nombre puede estar en ciudades diferentes y, en una misma ciudad, puede haber departamentos
con nombres diferentes.
Sobre los proyectos: código, nombre, precio, fecha de inicio, fecha prevista de finalización, fecha real
de finalización y el código de cliente.
Sobre los clientes: código de cliente, el nombre, el NIF, la dirección, la ciudad y el teléfono.
En primer lugar, hay que crear la base de datos que contendrá las tablas que almacenarán la información:

Se pulsa sobre “Database”.


En el desplegable que aparece se selecciona “Add database”:

Tipo de base de datos


Aparece un formulario en el que se debe indicar el tipo de base de datos (sección “Database type”),
donde se ubicará la base de datos (sección “File”) y el nombre de la base de datos (“sección name”). Se
elige el tipo “SQLite 3”. Se pulsa sobre el símbolo + para crear el archivo de la base de datos, aparece
un navegador para ir a la carpeta donde se quiere guardar el archivo, y se crea un archivo denominado
“Empresa.sqlite3”.
Base de datos en la interface principal
Cuando se pulsa sobre “OK”, la nueva base de datos aparece en el marco izquierdo de la interface
principal.

Conectar a la base de datos


Para poder usar la base de datos, se selecciona con el ratón y con botón derecho aparece un desplegable
en el que se elige “Connect to the database”

Ver las tablas asociadas


Una vez que se ha conectado con la base de datos, en el marco izquierdo aparecen listadas las tablas y
vistas asociadas a la base de datos. En el ejemplo aparecen vacías

Abrir el editor SQL


Para interactuar con la base de datos, se debe abrir el editor de SQL, pulsando sobre el icono en forma
de lápiz
Posición del editor
El editor de SQL se muestra en la parte derecha de la interface principal

Creación de tablas
Se van a crear las diferentes tablas. Para crear una tabla se puede usar un formulario gráfico o bien el
editor de SQL. Para ilustrar el uso de SQL, se va a utilizar el editor donde habrá que introducir las
sentencias en la sintaxis de SQL.
Se crea una tabla clientes para almacenar la información sobre los mismos. Para ello se introduce la
siguiente sentencia en el editor

CREATE TABLE clientes (codigo_cli INTEGER,


nombre_cli CHAR (30) NOT NULL ,nif CHAR (12), direccion CHAR (30),ciudad CHAR (20),
telefono CHAR (12),PRIMARY KEY (codigo_cli),UNIQUE(nif) );
Ejecutar sentencias
A continuación, se pulsa sobre el icono de ejecutar sentencia SQL (icono ) para crear la tabla. La nueva
tabla aparece listada en la base de datos
Navegación
Si se pulsa sobre la tabla, aparece en la parte derecha de la interface la definición de la tabla creada

Creación del resto de tablas


A continuación, se pueden crear el resto de tablas de la base de datos de ejemplo de la misma forma:

Tabla departamentos
CREATE TABLE departamentos
(nombre_dep CHAR(20),
ciudad_dep CHAR(20),
telefono INTEGER DEFAULT NULL,
PRIMARY KEY (nombre_dep, ciudad_dep));

Tabla proyectos
CREATE TABLE proyectos
(codigo_proyec INTEGER,nombre_proyec CHAR(20),
precio REAL, fecha_inicio DATE, fecha_prev_fin DATE,
fecha_fin DATE DEFAULT NULL,codigo_cliente INTEGER,
PRIMARY KEY (codigo_proyec),
CHECK (fecha_inicio < fecha_prev_fin),
CHECK (fecha_inicio < fecha_fin),
FOREIGN KEY ( codigo_cliente ) REFERENCES clientes ( codigo_cli));
Tabla empleados
CREATE TABLE empleados
(codigo_empl INTEGER,nombre_empl CHAR (20),
apellido_empl CHAR(20),sueldo REAL CHECK (sueldo > 7000),
nombre_dep CHAR(20),ciudad_dep CHAR(20),
num_proyec INTEGER,PRIMARY KEY (codigo_empl),
FOREIGN KEY (nombre_dep, ciudad_dep) REFERENCES
departamentos (nombre_dep, ciudad_dep),
FOREIGN KEY (num_proyec) REFERENCES proyectos (codigo_proyec) );

El resultado final debería ser similar al siguiente:

Hay que tener en cuenta que, al crear una tabla, las restricciones se pueden definir a nivel de columna o
bien a nivel de tabla. Así, en el caso de que la restricción haga referencia a una sola columna, se puede
optar por una opción u otra. Sin embargo, si hace referencia a más de una columna, es necesario definir
la restricción a nivel de tabla.
Creación de una vista
A continuación, se va a crear una vista utilizando las tablas proyectos y clientes (figura 3.24.). Para ello,
se usa el editor de SQL de la misma forma que en el caso de la creación de tablas.
CREATE VIEW proyectos_por_cliente AS
SELECT c.codigo_cli, COUNT(*)
FROM proyectos p, clientes c
WHERE p.codigo_cliente = c.codigo_cli
GROUP BY c.codigo_cli;
Otras pruebas
Por último, se puede probar lo siguiente:
- Eliminar la vista proyectos_por_cliente usando la sentencia: DROP VIEW proyectos_por_cliente.
- Eliminar la tabla clientes usando la sentencia: DROP TABLE clientes.
- Añadir una nueva columna denominada “país” de tipo CHAR(20) a la tabla clientes: ALTER TABLE
clientes ADD COLUMN paises CHAR(20)-

Caso práctico

Considérese una base de datos para gestionar las solicitudes de acceso de estudiantes a los institutos. La
información que se desea almacenar es:

Sobre los institutos: nombre, área de la ciudad donde se encuentra y número máximo de plazas.
Sobre los estudiantes: identificador de estudiante, nombre, puntos que tiene para acceder y un
valor de corrección.
Sobre las solicitudes: identificador de estudiante, nombre del instituto, vía solicitada y decisión
sobre la solicitud.

Tablas

Se van a crear 3 tablas: institutos, estudiantes y solicitudes.

CREATE TABLE Institutos(Nombre_Inst CHAR(35), Area CHAR(35), Plazas INTEGER,


PRIMARY KEY ( Nombre_Inst ),
UNIQUE ( Nombre_Inst, Comunidad ));
CREATE TABLE Estudiantes(ID INTEGER, Nombre_Est CHAR(35), Puntos REAL, Valor INTEGER,
PRIMARY KEY ( ID ),
UNIQUE ( ID ) );
CREATE TABLE Solicitudes(ID INTEGER, Nombre_Inst CHAR(35), Via CHAR(35), Decision
CHAR(35),
PRIMARY KEY ( ID, Nombre_Inst, Via ),
FOREIGN KEY ( ID ) REFERENCES Estudiantes ( ID ),
FOREIGN KEY ( Nombre_Inst ) REFERENCES
Institutos ( Nombre_Inst ),
UNIQUE ( ID, Nombre_Inst, Via ) );
Datos en tablas

Tabla Institutos

Tabla Estudiantes

Tabla Solicitudes
Deben hacerse las siguientes consultas:

1.Obtener los nombres y notas de los estudiantes, así como el resultado de su solicitud, de
manera que tengan un valor de corrección menor que 1000 y hayan solicitado la vía de
“Tecnología” en el “Instituto Ramiro de Maeztu”.
2.Obtener la información sobre todas las solicitudes: ID y nombre del estudiante, nombre del
instituto, puntos y plazas, ordenadas de forma decreciente por los puntos y en orden creciente de
plazas.
3.Obtener todas las solicitudes a vías denominadas como “Ciencias” o “Ciencias Sociales”.
4.Obtener los estudiantes cuya puntuación ponderada cambia en más de un punto respecto a la
puntuación original.
5.Borrar a todos los estudiantes que solicitaron más de 2 vías diferentes.
6.Obtener las vías en las que la puntuación máxima de las solicitudes está por debajo de la media.
7.Obtener los nombres de los estudiantes y las vías que han solicitado.
8.Obtener el nombre de los estudiantes y la puntuación con valor de ponderación menor de 1000
que hayan solicitado la vía de “Tecnología” en el “Instituto San Isidro”.

SOLUCIONES

La inserción de datos se haría de la siguiente manera:


Inserción
Tabla Institutos
insert into Institutos values ('Instituto San Isidro', 'Centro', 150);

insert into Institutos values ('Instituto Ramiro de Maeztu', 'Salamanca', 360);


insert into Institutos values ('Instituto Arturo Soria', 'Hortaleza', 100);
insert into Institutos values ('Instituto Torres Quevedo', 'Moncloa', 210);
Tabla Estudiantes
insert into Estudiantes values (123, 'Antonio', 8.9, 1000);

insert into Estudiantes values (234, 'Juan', 8.6, 1500);


insert into Estudiantes values (345, 'Isabel', 8.5, 500);
insert into Estudiantes values (456, 'Doris', 7.9, 1000);
insert into Estudiantes values (567, 'Eduardo', 6.9, 2000);
insert into Estudiantes values (678, 'Carmen', 5.8, 200);
insert into Estudiantes values (789, 'Isidro', 8.4, 800);
insert into Estudiantes values (987, 'Elena', 6.7, 800);
insert into Estudiantes values (876, 'Irene', 6.9, 400);
insert into Estudiantes values (765, 'Javier', 7.9, 1500);
insert into Estudiantes values (654, 'Alfonso', 7.9, 1000);
insert into Estudiantes values (543, 'Pedro', 5.4, 2000);
Tabla Solicitudes
insert into Solicitudes values (123, 'Instituto Ramiro de Maeztu', 'Tecnologia', 'Si');

insert into Solicitudes values (123, 'Instituto Ramiro de Maeztu', 'Ciencias Sociales', 'No');
insert into Solicitudes values (123, 'Instituto San Isidro', 'Tecnologia', 'Si');
insert into Solicitudes values (123, 'Instituto Torres Quevedo', 'Ciencias Sociales', 'Si');
insert into Solicitudes values (234, 'Instituto San Isidro', 'Ciencias', 'No');
insert into Solicitudes values (345, 'Instituto Arturo Soria', 'Tecnologia', 'Si');
insert into Solicitudes values (345, 'Instituto Torres Quevedo', 'Tecnologia', 'No');
insert into Solicitudes values (345, 'Instituto Torres Quevedo', 'Ciencias', 'Si');
insert into Solicitudes values (345, 'Instituto Torres Quevedo', 'Ciencias Sociales', 'No');
insert into Solicitudes values (678, 'Instituto Ramiro de Maeztu', 'Ciencias Sociales', 'Si');
insert into Solicitudes values (987, 'Instituto Ramiro de Maeztu', 'Tecnologia', 'Si');
insert into Solicitudes values (987, 'Instituto San Isidro', 'Tecnologia', 'Si');
insert into Solicitudes values (876, 'Instituto Ramiro de Maeztu', 'Tecnologia', 'No');
insert into Solicitudes values (876, 'Instituto Arturo Soria', 'Ciencias', 'Si');
insert into Solicitudes values (876, 'Instituto Arturo Soria', 'Ciencias Sociales', 'No');
insert into Solicitudes values (765, 'Instituto Ramiro de Maeztu', 'Ciencias Sociales', 'Si');
insert into Solicitudes values (765, 'Instituto Torres Quevedo', 'Ciencias Sociales', 'No');
insert into Solicitudes values (765, 'Instituto Torres Quevedo', 'Ciencias', 'Si');
insert into Solicitudes values (543, 'Instituto Arturo Soria', 'Tecnologia', 'No');
Consultas
La resolución de las consultas se haría de la siguiente forma:

1.SELECT Nombre_Est, Puntos, Decision FROM Estudiantes,Solicitudes WHERE


Estudiantes.ID = Solicitudes.ID
AND Valor < 1000 AND Via = 'Tecnologia' AND Nombre_Inst = 'Instituto Ramiro de Maeztu';

2.SELECT Estudiantes.ID, Nombre_Est, Nota, Solicitudes.Nombre_Inst, Plazas FROM


Estudiantes, Institutos, Solicitudes WHERE Solicitudes.ID = Estudiantes.ID AND
Solicitudes.Nombre_Inst = Institutos.Nombre_Inst ORDER BY Puntos DESC, Plazas;

3.SELECT * FROM Solicitudes WHERE Via like '%Ciencias%';

4.SELECT ID, Nombre_Est, Puntos, Puntos*Valor/1000.0 as Ponderada FROM Estudiantes


WHERE ABS(Puntos*(Valor/1000.0) - Puntos) > 1.0;

5.DELETE FROM Solicitudes WHERE ID IN (SELECT ID FROM Solicitudes GROUP BY ID


HAVING COUNT (DISTINCT Via) >2);

6.SELECT Via, Puntos FROM Estudiantes, Solicitudes WHERE Estudiantes.ID= Solicitudes.ID


GROUP BY Via HAVING MAX(Puntos) < (Select AVG(Puntos) FROM Estudiantes);

7.SELECT DISTINCT Nombre_Est, Via FROM Estudiantes JOIN Solicitudes ON


Estudiantes.ID = Solicitudes.ID;
8.SELECT Nombre_Est, Puntos FROM Estudiantes JOIN Solicitudes ON Estudiantes.ID =
Solicitudes.ID AND Valor < 1000 AND Via='Tecnologia' AND Nombre_Inst= 'Instituto San
Isidro';

También podría gustarte