Modelo Físico: SQL - Instrucciones DDL y DML¶
Propuesta didáctica¶
Una vez conocido el modelo relacional, en esta unidad vamos a comenzar a trabajar el RA2 "Crea bases de datos definiendo su estructura y las características de sus elementos según el modelo relacional", además de comenzar con el RA4 "Modifica la información almacenada en la base de datos utilizando asistentes, herramientas gráficas y el lenguaje de manipulación de datos".
Criterios de evaluación¶
Respecto al RA2:
- CE2a: Se ha analizado el formato de almacenamiento de la información.
- CE2b: Se han creado las tablas y las relaciones entre ellas.
- CE2c: Se han seleccionado los tipos de datos adecuados.
- CE2d: Se han definido los campos clave en las tablas.
- CE2e: Se han implantado las restricciones reflejadas en el diseño lógico.
Respecto al RA4:
- CE4a: Se han identificado las herramientas y sentencias para modificar el contenido de la base de datos.
- CE4b: Se han insertado, borrado y actualizado datos en las tablas.
Contenidos¶
Bases de datos relacionales:
- Lenguaje de descripción de datos (DDL).
Tratamiento de datos:
- Inserción, borrado y modificación de registros.
- Integridad referencial.
Cuestionario inicial
- ¿Cuál es el propósito de las sentencias DDL?
- ¿Y DML?
- ¿Cómo averiguamos tanto las tablas de una base de datos como su estructura?
- ¿Para qué sirve el comando
CREATE TABLE?
- ¿Qué diferencia
CREATE TYPE
deCREATE DOMAIN
? - A la hora de crear una tabla, ¿Dónde definiremos la clave primaria? ¿Y las restricciones de valor no nulo?
- ¿Que restricciones SQL podemos aplicar sobre las columnas?
- ¿Por qué es mejor definir las restricciones al final de cada tabla mediante la keyword
CONSTRAINT
? - ¿Cómo puedo ejecutar un script SQL, que tengo almacenado en un archivo, de una sola vez?
- ¿Puedo eliminar las tablas en cualquier orden mediante
DROP TABLE
? - ¿Es obligatorio que una clave ajena siempre referencie a una clave primaria?
- ¿Para qué sirve el comando
INSERT INTO
? - ¿Qué sucede si omitimos el
WHERE
en unDELETE FROM
?
Programación de Aula (11h)¶
Esta unidad es la quinta, con lo que se imparte en la primera evaluación, a finales del mes de noviembre, con una duración estimada de 11 sesiones lectivas:
Sesión | Contenidos | Actividades | Criterios trabajados |
---|---|---|---|
1 | Modelo físico. Puesta en marcha | AC501 | CE2a |
2 | DDL Bases de datos y tipos de datos | ||
3 | DDL Tablas | AC503 | CE2b, CE2c, CE2d |
4 | Restricciones | AC504 | CE2e |
5 | Supuesto DDL | AC506 | CE2b, CE2c, CE2d, CE2e |
6 | Cargando y exportando datos | ||
7 | DML. Inserciones | AC509 | CE4a, CE4b |
8 | DML. Actualizaciones y Borrados | AC510 | CE4b |
9 | Supuesto DDL-DML I | AC513 | CE2b, CE2c, CE2d, CE2e |
10 | Supuesto DDL-DML II | AC514 | CE4b |
11 | Reto - Diseño físico | PY515 | CE2a, CE2b, CE2c, CE2d, CE2e, CE4a, CE4b |
Al finalizar esta unidad, comenzaremos un nuevo reto, centrado en el modelo físico, creando y cargando datos.
Modelo físico¶
El modelo físico es la traducción del modelo lógico a un modelo interno, dando lugar a un esquema físico interpretable por un SGBD concreto, paso final del diseño de las bases de datos.
Para ello, transformaremos el modelo relacional al modelo físico utilizando un lenguaje específico de cada SGBD que nos permite definir los elementos, el cual se conoce como DDL (Data Definition Language).
SQL¶
SQL (sequel en inglés) es la lingua franca para interactuar con las bases de datos relacionales. Es un lenguaje declarativo, donde lo importante es definir qué se quiere hacer, en lugar de cómo se va a hacer.
Su origen se remonta a 1970 cuando Edgar Frank Codd publica el documento “Un modelo relacional de datos para grandes bancos de datos compartidos”, donde se definieron las bases del modelo relacional.
Versiones
El lenguaje SQL ha ido evolucionando conforme lo han hecho los diferentes SGBD y tecnologías, desde el primer estándar en el archivo 1986 hasta la última versión de 2023:
- SQL-86: funcionalidad mínima para que un lenguaje se considere SQL, introduciendo las sentencia
SELECT
,CREATE TABLE
,INSERT
, ... yJOIN
. - SQL-89: añade instrucciones para gestionar las claves ajenas (reglas de integridad referencial).
- SQL-92: standard base. Introduce los tipos de datos para trabajar con fechas, como
DATE
,TIME
yTIMESTAMP
, la agregación de consultas conGROUP BY
y sus operadoresCOUNT
ySUM
, así como la validación de los campos conCHECK
. - SQL:1999 (SQL3): se añaden soporte para la programación orientada objetos y el uso de triggers y procedimientos almacenados mediante PL/SQL.
- SQL:2003: añade características de XML y las funciones ventana, así como el uso de
SEQUENCE
para generar valores únicos. - SQL:2008: introduce soporte para datos temporales, la instrucción
MERGE
para combinar datos y mejoras en las expresiones de tabla comunes (conWITH
). - SQL:2016: introduce soporte nativo para JSON y funciones para manejar datos geoespaciales.
SQL se divide a su vez en cuatro "sublenguajes" de datos respecto a la funcionalidad que ofrecen:
- DDL (Data Definition Language): permite crear y manipular la estructura de una base de datos.
- DML (Data Manipulation Language): permite recuperar, almacenar, modificar, y eliminar datos de una BD. Dentro de DML, algunos autores definen DQL (Data Query Language) como el sublenguaje para realizar las consultas sobre las tablas para recuperar los datos.
- DCL (Data Control Language): permite crear roles, permisos y usuarios, controlando el acceso a los elementos de nuestras bases de datos.
- TCL (Transaction Control Language): administra las modificaciones creadas con el DML.
En esta sesión nos vamos a centrar en los lenguajes DDL y DML, definiendo la estructura de una base de datos y rellenándola con datos. En las tres unidades siguientes aprenderemos la parte de DQL para realizar consultas. Y para terminar el bloque, en la unidad 9 estudiaremos en profundidad tanto el lenguaje DCL como TCL.
Sintaxis¶
La sintaxis de SQL se define mediante instrucciones que tiene diferentes parámetros, unos opcionales, otros con diferentes valores, etc.. La nomenclatura que vamos a emplear al explicar la sintaxis de cada instrucción será la siguiente:
Símbolo | Descripción |
---|---|
MAYÚSCULAS | Palabras reservadas (keywords) de SQL |
minúsculas | variable que hay que sustituir por un elemento concreto |
[] |
opcional |
| |
separa opciones alternativas |
[ | ] |
se elige uno de los valores |
{} |
obliga a elegir uno de los valores |
... |
número variable de datos |
Así pues, si por ejemplo, la sintaxis de una instrucción es la siguiente:
SELECT [ALL | DISTINCT] columna1 [,columna2, columna3,.......] | *
FROM tabla1 [tabla2, tabla3, ….]
[WHERE condición ]
[ORDER BY expr1 [DESC | ASC] [, expr2 [DESC | ASC] ....]
Case sensitive
En entornos Windows, a la hora de citar a las tablas o sus campos, no se distingue entre mayúsculas y minúsculas, pero en entornos Linux sí. Así pues, es recomendable respetar el uso de mayúsculas y minúsculas. Respecto a las keywords, son case insensitive, pero al menos es recomendable hacer un uso homogéneo a lo largo de nuestras aplicaciones.
Dicho esto, las siguientes sentencias son semejantes:
SELECT nombre FROM cliente;
select nombre from cliente;
Select nombre From cliente;
SelecT nombre FroM cliente;
Destacar que las instrucciones finalizan con el signo de punto y coma (;
) y que se emplea el doble guion (--
) para introducir comentarios. Además, cualquier instrucción SQL puede ser partida por espacios o saltos de línea antes de finalizar la instrucción, facilitando su lectura. Así pues, posibles sentencias que cumplen la sintaxis serían:
-- Ejemplos de instrucciones SQL
SELECT nombre, dni FROM cliente WHERE salario > 100;
SELECT * FROM cliente WHERE salario > 100 ORDER BY salario;
SELECT DISTINCT nombre
FROM cliente
ORDER BY salario DESC;
DDL¶
Al empezar esta unidad habíamos comentado que DDL es el lenguaje encargado de definir las estructuras de las bases de datos, esto es, permite la creación, modificación y eliminación de los objetos de la base de datos (metadatos), como, por ejemplo, las bases de datos, tablas, índices o vistas.
TRUNCATE
Mientras que con DROP
podemos borrar una tabla, con TRUNCATE
vaciaremos las tablas de contenido. Es por ello, que no se considera una operación DDL, sino más bien DML.
Para ello, utilizaremos principalmente tres tipos de sentencias:
CREATE
: permite crear nuevas tablas, vistas e índices.ALTER
: permite modificar la estructura de un elemento, como por ejemplo en las tablas agregando o eliminando campos, cambiando la definición de tipos, etc...DROP
: empleado para eliminar las tablas y los índices.
Puesta en marcha¶
Para trabajar con un SGBD tenemos varias posibilidades. Una de ellas es mediante una solución ya desplegada en la nube, como veremos en la siguiente unidad. La segunda sería que lo instalásemos en nuestro sistema operativo mediante las instrucciones que ofrecen en sus páginas web, o bien utilizar uno de los paquetes "todo incluido" que ofrecen plataformas como Bitnami (ya sea XAMPP u otro paquete). Nosotros nos hemos decantado por la tercera posibilidad, el uso de contenedores Docker para aislar los servicios que utilicemos con lo que tengamos instalado en nuestros ordenadores. Por ello, en Entornos tiene toda la información necesaria tanto para poner en marcha MariaDB como PostgreSQL.
Una vez instalados los SGBD y conectados a los mismos, ya sean mediante la consola o la interfaz gráfica, debemos saber que aunque el 90% de la sintaxis de SQL es común, cada SGBD tiene sus particularidades. Es por ello, que en este curso vamos a trabajar con MariaDB y PostgreSQL de manera indistinta.
Recuerda que todas las instrucciones terminan en ;
y que vamos a dejar en MAYÚSCULAS las keywords para facilitar la lectura, aunque no es obligatorio escribirlas así.
Autoevaluación
En este momento deberías tener tanto MariaDB como PostgreSQL instalado en local y ser capaz de conectarte desde la línea de comandos.
Una vez dentro, ejecuta los siguientes comandos y comprueba si entiendes qué realiza cada una de las siguientes sentencias:
SHOW DATABASES;
USE mibd;
SHOW TABLES;
DESCRIBE mitabla;
select version();
SELECT datname FROM pg_database;
\l
\dt
Comencemos con un caso muy sencillo, donde vamos a crear una tabla con un campo, insertar un registro y finalmente consultar la información recién almacenada:
CREATE TABLE ejemplo8a (
id INT PRIMARY KEY,
nombre VARCHAR(64) NOT NULL);
INSERT INTO ejemplo8a VALUES (1, "Aitor Medrano");
SELECT * FROM ejemplo8a;
Una vez conectados a nuestros SGBD y comprobado que podemos crear estructuras, insertar datos y consultarlos, vamos a profundizar en la definición de los esquemas relacionales.
Bases de datos¶
El principal elemento en un SGBD es la base de datos.
Cuando nos conectamos indicaremos siempre a qué base de datos lo hacemos. De todos modos, siempre podemos consultar las bases de datos existentes:
En el caso de MariaDB usaremos el comando SHOW DATABASES
:
MariaDB [pruebas]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| pruebas |
+--------------------+
2 rows in set (0.001 sec)
Si solo queremos el nombre de las bases de datos, podemos hacer una consulta a la tabla pg_database
(la cual hace la función de diccionario de datos):
pruebas=# SELECT datname FROM pg_database;
datname
-----------
postgres
pruebas
template1
template0
(4 rows)
Si queremos toda la información, podemos hacer uso del comando \l
:
pruebas=# \l
List of databases
Name | Owner | Encoding | Locale Provider | Collate | Ctype | ICU Locale | ICU Rules | Access privileges
-----------+-------+----------+-----------------+------------+------------+------------+-----------+-------------------
postgres | s8a | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
pruebas | s8a | UTF8 | libc | en_US.utf8 | en_US.utf8 | | |
template0 | s8a | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/s8a +
| | | | | | | | s8a=CTc/s8a
template1 | s8a | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | =c/s8a +
| | | | | | | | s8a=CTc/s8a
(4 rows)
Para crear una base de datos, usaremos la sentencia CREATE DATABASE
:
CREATE [OR REPLACE] DATABASE [IF NOT EXISTS] nombreBD;
Por ejemplo:
CREATE DATABASE midb;
CREATE DATABASE IF NOT EXISTS severo;
Más información en la documentación oficial
CREATE DATABASE nombreBD;
Por ejemplo:
CREATE DATABASE midb;
Más información en la documentación oficial
Si necesitamos borrarla, porque ya no la necesitamos, usaremos la sentencia DROP DATABASE
, tanto en MariaDB como en PostgreSQL:
DROP DATABASE [IF EXISTS] nombreBD;
Por ejemplo:
DROP DATABASE prueba;
DROP DATABASE IF EXISTS severo;
Almacenamiento¶
A la hora de almacenar la información de una base de datos, tanto a nivel lógico como físico, necesitamos de diferentes elementos que conviene conocer.
Motores de almacenamiento¶
En los SGBD, y en concreto en MariaDB y PostgreSQL, los motores de almacenamiento (storage engines) son fundamentales para definir el formato de almacenamiento, es decir, cómo se almacenan y gestionan los datos en las bases de datos. Tanto MariaDB como PostgreSQL ofrecen diferentes motores de almacenamiento que proporcionan distintas capacidades, los cuales influyen en cómo gestionan y organizan los datos en disco, afectando el rendimiento, la concurrencia y la eficiencia del espacio.
-
MariaDB
Los motores más conocidos son:
-
InnoDB: motor de almacenamiento predeterminado en MariaDB, desde la versión 10.2. Proporciona soporte completo para transacciones ACID. Maneja bloqueo a nivel de fila y cuenta con soporte para claves ajenas. Finalmente, es adecuado para aplicaciones donde la integridad y las transacciones sean críticas.
-
MyISAM: Históricamente, fue el motor de almacenamiento por defecto en MySQL antes de InnoDB. Su uso ha caído al no ser transaccional y no soportar claves ajenas. Se utiliza principalmente en aplicaciones que priorizan el rendimiento de las lecturas sobre la integridad de los datos.
Existen otros motores más específicos como TokuDB (para grandes volúmenes de datos con alta concurrencia y compresión), Aria (utilizado para recuperación rápida y consultas de solo lectura), ColumnStore (en aplicaciones de análisis de datos) o Memory (para tablas temporales y cachés rápidas)
-
-
PostgreSQL
En cambio, PostgreSQL no utiliza el concepto de múltiples motores de almacenamiento como MariaDB, sino que emplea un único motor nativo altamente configurable que ofrece muchas características avanzadas:
- Proporciona soporte completo para transacciones ACID, integridad referencial (claves foráneas) y MVCC para la gestión de concurrencia.
- Bloqueo a nivel de fila.
- Muy eficiente para grandes volúmenes de datos y con soporte completo para tipos de datos avanzados como JSON, XML, y más.
Además, es posible ajustar su comportamiento, por ejemplo, permitiendo acceder a datos externos a través de FDW (Foreign Data Wrappers), y optimizaciones internas mediante tablas particionadas y configuraciones adicionales.
Bloqueo a nivel de fila (row-level locking)
Técnica de control de concurrencia que permite a las bases de datos bloquear únicamente las filas que están siendo accedidas o modificadas por una transacción, en lugar de bloquear toda la tabla o bloques más grandes de datos.
Permite que múltiples transacciones trabajen en diferentes partes de una tabla sin interferir entre sí.
MVCC - Multiversion Concurrency Control
Cuando hay una modificación de datos, en lugar de sobrescribir una fila en actualizaciones, se almacena una nueva versión de la fila mientras se mantiene la versión anterior hasta que ya no sea necesaria (las transacciones que la usan han finalizado).
Las versiones anteriores de una fila se almacenan en el espacio de "deshacer" (undo space) para permitir transacciones y lecturas consistentes.
Jerarquía lógica de una BD¶
A nivel lógico, los elementos que organizan la información de una base de datos se definen mediante la siguiente jerarquía:
- Tablespace o espacio de tablas: estructura física (normalmente una carpeta/directorio) para agrupar varias bases de datos o esquemas.
- Base de datos: elemento lógico de nivel superior
- Esquema: agrupación lógica de una o más tablas (en MySQL/MariaDB un esquema es una base de datos).
- Tabla: representación de una tabla relacional, que contiene datos organizados en filas y columnas. Cada tabla pertenece a un base de datos específica, y sus datos se almacenan en el tablespace asociado.
- Fila: cada uno de los registros de la tabla
En cuanto a los tablespaces, podemos crearlos mediante la instrucción CREATE TABLESPACE
en PostgreSQL (en MariaDB no está soportado), y a partir de él, crear bases de datos y tablas:
CREATE TABLESPACE mi_tablespace LOCATION '/ruta/al/directorio';
CREATE DATABASE mi_db TABLESPACE mi_tablespace;
CREATE TABLE mi_tabla (
id SERIAL PRIMARY KEY,
nombre VARCHAR(100)
) TABLESPACE mi_tablespace;
En cambio, a nivel físico, el formato de almacenamiento depende principalmente del motor de almacenamiento que se esté utilizando, ya que cada motor tiene su propia manera de organizar y almacenar los datos.
-
MariaDB - InnoDB
-
Un bloque almacena los datos de tablas e índices mediante páginas (normalmente de 16 KB)
-
Los bloques se almacenan en un archivo de tablespace (generalmente
ibdata1
para bases de datos que comparten un tablespace, o en un archivo por tabla). -
Respecto a las filas, se almacenan utilizando un formato compacto o redondeado, dependiendo de la configuración. Cada fila contiene una clave primaria (o un índice que actúa como clave primaria) y los datos correspondientes.
-
-
PostgreSQL
-
De manera predeterminada, todas las bases de datos utilizan el tablespace
pg_default
, pero se pueden crear otros tablespaces personalizados. -
Cada base de datos se representa por un directorio con un nombre numérico (OID) dentro del tablespace. Dentro de esta carpeta se encuentran archivos que representan tablas, índices, secuencias y otros objetos de la base de datos.
-
Cada tabla se almacena en un archivo separado, y estos archivos están divididos en páginas de 8 KB. Si una tabla crece más allá de un tamaño específico (1 GB), se fragmenta en varios archivos con un sufijo numérico.
-
Las filas se organizan dentro de páginas de 8 KB, donde cada página contiene múltiples tuplas (filas). Cada fila o tupla en una tabla tiene un ctid (tuple identifier) que identifica su ubicación física en el archivo de datos.
-
Juegos de caracteres¶
Entendemos como juego de caracteres (character sets) a la codificación que utiliza el SGBD para representar los datos, definiendo qué caracteres pueden almacenarse en la base de datos y cómo esos caracteres son codificados internamente.
Tal como ya habéis estudiado en el módulo profesional de Sistemas Informáticos, los juegos de caracteres más conocidos son ASCII
(caracteres básicos en inglés), iso-8859-1 latin
(expande ASCII para incluir los caracteres acentuados y símbolos de los lenguajes occidentales) y utf-8
, el cual parte de la familia de estándares Unicode, que puede representar caracteres de casi todos los lenguajes del mundo, incluidos caracteres multibyte.
Además, podemos configurar la colación (collation), la cual define cómo se comparan y ordenan las cadenas de texto para un juego de caracteres específico (por ejemplo, la ñ
después de la n
y no conforme a la tabla de códigos ASCII), teniendo en cuenta cosas como el uso de mayúsculas y minúsculas o el tratamiento de acentos (por ejemplo, si Á
es igual que á
).
En MariaDB y PostgreSQL, los juegos de caracteres y las colaciones juegan un papel importante en cómo se almacenan y comparan las cadenas de texto. Cada sistema maneja los juegos de caracteres de manera diferente, y a continuación vamos a estudiar cómo funciona en cada uno:
-
MariaDB
Admite múltiples juegos de caracteres y colaciones, que pueden establecerse a nivel de base de datos, tabla, columna o incluso a nivel de sesión.
Así pues, ofrece una gran flexibilidad con varios juegos de caracteres (como
utf8
,utf8mb4
,latin1
, etc.) y permite especificar colaciones para determinar cómo se comparan y ordenan los textos. -
PostgreSQL
Está optimizado principalmente para el uso de
utf8
, con menos enfoque en otros juegos de caracteres.Las colaciones son más dependientes de las configuraciones del sistema operativo y se aplican al nivel de la base de datos o columna, pero la mayoría de los sistemas de producción usan
utf8
por su versatilidad.
Del mismo modo, la configuración del juego de caracteres y la colación dependen del SGBD empleado:
La codificación y la colación se especifican al crear una base de datos mediante CREATE DATABASE
con las opciones CHARACTER SET
y COLLATE
.
Además de utf8
, latin1
y ascii
ya comentados previamente, una codificación muy empleado es utf8mb4
, la cual es una extensión de utf8
que permite almacenar todos los caracteres de Unicode, incluidos emojis y otros símbolos que no se pueden representar en utf8
.
Además, luego para cada juego de caracteres, tenemos sus colaciones. Por ejemplo, el juego de caracteres utf8
tiene varias colaciones como:
utf8_general_ci
: Colación insensible a mayúsculas/minúsculas (case-insensitive).utf8_bin
: Colación binaria, sensible a mayúsculas/minúsculas y que compara los caracteres basándose en sus valores binarios.
CREATE DATABASE mi_base_de_datos
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
La codificación y la colación se especifican al crear una base de datos mediante CREATE DATABASE
con las opciones ENCODING
y LC_COLLATE
.
Las colaciones más comunes son:
en_US.UTF-8
: Colación en inglés, sensible a mayúsculas/minúsculas.es_ES.UTF-8
: Colación en español.
CREATE DATABASE mi_base_de_datos
WITH ENCODING 'UTF8'
LC_COLLATE 'es_ES.UTF-8'
LC_CTYPE 'es_ES.UTF-8';
Tipos de datos¶
Antes de entrar a ver cómo podemos crear las tablas, necesitamos conocer los tipos de datos que van a poder tener las columnas que definamos. Grosso modo, los tipos más empleados en MariaDB y PostgreSQL son:
Tipo de dato | MariaDB | PostgreSQL | Descripción |
---|---|---|---|
Numéricos | |||
TINYINT |
Sí | No | Entero pequeño, de 1 byte (MariaDB) |
SMALLINT |
Sí | Sí, alias int2 |
Entero pequeño, de 2 bytes |
MEDIUMINT |
Sí | No | Entero de 3 bytes (MariaDB) |
INT , INTEGER |
Sí | Sí, alias int4 |
Entero estándar de 4 bytes |
BIGINT |
Sí | Sí, alias int8 |
Entero grande de 8 bytes |
DECIMAL , NUMERIC |
Sí | Sí | Número con precisión fija (exacta) |
FLOAT |
Sí | Sí | Número de coma flotante de precisión simple |
DOUBLE |
Sí | Sí, alias float8 |
Número de coma flotante de precisión doble |
SERIAL |
No | Sí, alias serial4 |
Entero autoincremental (PostgreSQL) 1 |
Cadenas | |||
CHAR(n) |
Sí | Sí | Cadena de longitud fija |
VARCHAR(n) |
Sí | Sí | Cadena de longitud variable |
TEXT |
Sí | Sí | Cadena de longitud ilimitada |
Fechas y horas | |||
DATE |
Sí | Sí | Fecha (año, mes, día) |
TIME |
Sí | Sí | Hora (sin zona horaria) |
DATETIME |
Sí | No | Fecha y hora (MariaDB) |
TIMESTAMP |
Sí | Sí | Marca de tiempo (con o sin zona horaria) |
Booleanos | |||
BOOLEAN |
Sí (alias de TINYINT ) |
Sí, alias bool |
Tipo booleano (en MariaDB es alias de TINYINT ) |
Algunos ejemplos de tipos serían:
BIGINT
CHAR(8)
VARCHAR(255)
DECIMAL(5,2) -- 5 dígitos en total, con 2 decimales, por ejemplo 123,45
Creando tipos y dominios¶
Además de todos los tipos de datos vistos (y algunos más que se salen del alcance del curso), en algunos SGBD (por ejemplo, PostgreSQL sí que lo permite, pero MariaDB no), podemos crear nuestros propios tipos de datos o dominios.
Mediante CREATE TYPE
podemos definir tipos de datos complejos, como tipos de registros, arrays o tipos compuestos de varios atributos. Por ejemplo, podemos crear un tipo de dato para almacenar coordenadas:
CREATE TYPE coordenada AS (
x FLOAT,
y FLOAT
);
Por otro lado, mediante CREATE DOMAIN
podemos definir un tipo de dato basado en un tipo existente, pero con restricciones adicionales (como validaciones, reglas de longitud, formatos, etc.). Por ejemplo, podemos crear un dominio para almacenar números naturales (enteros positivos):
CREATE DOMAIN nnatural AS integer CHECK (VALUE > 0);
Enumeraciones¶
Además de los tipos vistos, podemos crear enumeraciones como un conjunto de valores restringidos que puede tomar un determinado campo, haciendo uso de la keyword ENUM
tanto en MariaDB como en PostgreSQL:
curso ENUM ('0', '1', '2'),
horario ENUM ('mañana', 'tarde', 'noche')
Primero debemos definir un tipo y luego asociar ese tipo al campo:
CREATE TYPE animo AS ENUM ('feliz', 'triste', 'normal');
estado animo;
Tablas¶
Dentro de la definición de datos, la creación de tablas es la parte más importante ya que nos permite trasladar nuestro modelo relacional al modelo físico.
Para crear una tabla emplearemos la sentencia CREATE TABLE
. La sintaxis básica compartida por todos los SGBD es la siguiente:
CREATE TABLE [OR REPLACE] [basededatos.]nombreDeTabla (
columna1 tipoDato1 [propiedadesColumna],
...
columnaN tipoDatoN [propiedadesColumna],
[restriccionesTabla]
);
Conviene revisar la sintaxis de MariaDB y PostgreSQL para comprobar los detalles de cada SGBD.
Por ejemplo, para crear una tabla con un único atributo podríamos hacer:
CREATE TABLE USUARIO (
nombre VARCHAR(25)
);
Algunas restricciones que hemos de tener en cuenta son:
- No puede haber nombres de tablas repetidas.
- El nombre de la tabla debe comenzar por un carácter alfabético y su longitud máxima es de 30 caracteres. Sólo se permiten letras del alfabeto inglés, dígitos o el signo de guión bajo.
- No podemos crear tablas cuyo nombre coincida con las palabras reservadas de SQL (por ejemplo, no podemos llamar a una tabla
WHERE
). - Como convención, vamos a nombrar las TABLAS en MAYÚSCULAS y las columnas en minúsculas.
- En principio, respecto a los nombre de las tablas y las columnas, los SGBD no son case sensitive (no distinguen entre mayúsculas y minúsculas), aunque puede depender de la configuración del sistema operativo (sobre todo en sistemas Linux/Unix) o el uso de comillas dobles (no recomendado). En cambio, en los datos, depende de la configuración de la colación.
A continuación tenemos algunos ejemplos de creaciones de tablas con diferentes tipos de datos:
CREATE TABLE PROVEEDOR(nombre VARCHAR(32));
CREATE TABLE CLIENTE (
nombre VARCHAR(32),
localidad VARCHAR(30) DEFAULT 'Elche');
CREATE TABLE PRESTAMO (
idPrestamo DECIMAL(8),
fechaPrestamo DATE DEFAULT (CURRENT_DATE));
CREATE TABLE CIUDAD (
nombre CHAR(20) NOT NULL,
poblacion INT NULL,
codigoPostal DECIMAL(6) NOT NULL DEFAULT 03203);
CREATE TABLE PROVINCIA (
nombre CHAR(20) NOT NULL,
poblacion INT DEFAULT 5000);
Con estos ejemplos, hemos visto que algunas de las propiedades de columna que podemos indicar son los valores no nulos (mediante NOT NULL
) o valores por defecto (mediante DEFAULT
). En el apartado Restricciones las estudiaremos en profundidad.
En todo momento, podemos obtener la estructura de una tabla mediante el comando DESCRIBE nombreTabla
en MariaDB como mediante \d nombreTabla
en PostgreSQL:
DESCRIBE CIUDAD
-- +--------------+--------------+------+-----+---------+-------+
-- | Field | Type | Null | Key | Default | Extra |
-- +--------------+--------------+------+-----+---------+-------+
-- | nombre | char(20) | NO | | NULL | |
-- | poblacion | int(11) | YES | | NULL | |
-- | codigoPostal | decimal(6,0) | NO | | 3203 | |
-- +--------------+--------------+------+-----+---------+-------+
-- 3 rows in set (0.000 sec)
\d CIUDAD
Clave primaria¶
Una vez visto como crear las tablas y sus atributos, nos centramos en la creación de la clave primaria. Toda tabla debe tener una clave primaria (ya sea simple o compuesta). Para ello, si tenemos un atributo simple, podemos añadir la propiedad PRIMARY KEY
al atributo, aunque por convenciones de código, es mejor definir la clave primaria después de todos los atributos, a modo de restricciones.
CREATE TABLE CIUDAD (
nombre CHAR(20) PRIMARY KEY, -- clave primaria como propiedad
poblacion INT DEFAULT 5000);
CREATE TABLE PROVINCIA (
nombre CHAR(20),
poblacion INT DEFAULT 5000,
PRIMARY KEY(nombre)); -- clave primaria como restricción
CREATE TABLE EMPLEADO (
codigo VARCHAR(9),
departamento VARCHAR(15),
nombre VARCHAR(40),
PRIMARY KEY(codigo, departamento)); -- clave primaria compuesta
Una buena práctica es definir un atributo a modo de codigo
(o id
) asociado a un número entero autoincrementable. Para ello, tras la definición del atributo como entero (sin signo), añadiremos la propiedad AUTO_INCREMENT
en la definición del atributo.
CREATE TABLE PERSONA (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
nombre VARCHAR(40),
fecha DATE);
En PostgreSQL existe el tipo serial
para crear una secuencia entera autoincrementable.
CREATE TABLE PERSONAP (
id SERIAL PRIMARY KEY,
nombre VARCHAR(40),
fecha DATE);
En Oracle (y en PostgreSQL también podemos hacerlo), se utilizan las secuencias en vez de los tipos autoincrementables. Tras cada inserción, la secuencia toma un nuevo valor.
CREATE SEQUENCE pid_seq AS integer;
CREATE TABLE PERSONAPS (
id integer NOT NULL DEFAULT nextval('pid_seq')
);
Creando tablas con consultas
Un caso particular de la creación de tablas es hacerlo a partir del resultado de una consulta, de manera que contiene la estructura (tipos de datos) y los datos obtenidos. Para ello, en vez de indicar los campos y restricciones, realizamos la consulta mediante SELECT
:
CREATE TABLE nombreTabla AS SELECT…
Por ejemplo, para crear una tabla GERENTE
con los datos de empleados catalogados como Gerentes, haríamos:
CREATE TABLE GERENTE AS
SELECT e.nombre, e.apellido1, e.apellido2, e.email
FROM EMPLEADO e
WHERE e.puesto LIKE "%Gerente%";
Volveremos a esta posibilidad cuando aprendamos a realizar consultas mediante SQL:
Modificando tablas¶
Una vez creada una tabla, podemos modificar su estructura mediante las sentencias ALTER TABLE
, tanto en MariaDB como en PostgreSQL.
Para añadir columnas, usaremos la opción ADD
:
ALTER TABLE nombreTabla ADD (
nombreColumna tipoDatos [propiedades]
[,columnaSiguiente tipoDatos [propiedades]…)
De este modo, si queremos añadir un campo para almacenar el teléfono de los empleados (sus campos estarán inicializados a NULL
cada registro de la tabla EMPLEADO
), haríamos:
ALTER TABLE EMPLEADO ADD (telefono CHAR(12));
En cambio, si queremos eliminar una columna, usaremos la opción DROP
:
ALTER TABLE nombreTabla DROP (columna [,columnaSiguiente,...]);
Así pues, si queremos eliminar el teléfono recién creado:
ALTER TABLE EMPLEADO DROP (telefono);
Finalmente, si queremos modificar el tipo de una columna, usaremos la opción MODIFY
:
ALTER TABLE nombreTabla MODIFY(
columna tipo [propiedades]
[,columnaSiguiente tipo [propiedades]]
Un caso particular de las operaciones de modificación es renombrar una tabla. Para ello, usaremos la opción RENAME TO
:
ALTER TABLE nombreTablaViejo RENAME TO nombreNuevo;
Finalmente, si queremos cambiar el juego de caracteres (y la colación) de una tabla concreta, haremos:
ALTER TABLE nombreTabla CONVERT TO CHARACTER SET nuevoJuego [COLLATE nuevaColacion];
Autoevaluación
Si tenemos las siguientes tablas:
CREATE TABLE CLIENTE (
dni VARCHAR(9) PRIMARY KEY,
cnombre VARCHAR(50),
direccion VARCHAR(60));
CREATE TABLE MASCOTA (
codigo INTEGER PRIMARY KEY,
nombre VARCHAR(50),
raza VARCHAR(50));
¿Sabes qué realizan las siguientes operaciones?
ALTER TABLE MASCOTA ADD especie VARCHAR(10) AFTER raza;
ALTER TABLE MASCOTA ADD cliente VARCHAR(9) AFTER nombre;
ALTER TABLE MASCOTA ADD CONSTRAINT fk_duenyo FOREIGN KEY (cliente) REFERENCES CLIENTE(dni);
ALTER TABLE MASCOTA MODIFY codigo INT(3) AUTO_INCREMENT;
Las siguientes instrucciones realizan la misma operación pero en diferente SGBD. ¿Sabes averiguar de quién es cada sentencia?
ALTER TABLE clientes CHANGE nombre nomMascota VARCHAR(50);
ALTER TABLE clientes RENAME nombre TO nomMascota VARCHAR(50);
Eliminando tablas¶
Para eliminar tablas se emplea la instrucción DROP TABLE nombreTabla
, tanto en MariaDB como en PostgreSQL. Hemos de tener en cuenta que cuando tenemos un base de datos con tablas relacionadas mediante claves ajenas, la integridad referencial restringe el borrado de una clave primaria referenciada por una clave ajena. Es por ello que el orden importa, y tenemos que ir eliminando primero las tablas que contienen las claves ajenas y no son referenciadas por otras tablas.
DROP TABLE CLIENTE;
Restricciones¶
Una vez hemos visto los casos más sencillos, hemos de saber que las tablas van a contener restricciones que aportan mayor integridad a los datos, en forma de claves ajenas, validaciones de campos, campos no nulos, únicos, etc... representando las restricciones estudiadas en el modelo relacional.
Así pues, si volvemos a la creación de tablas, y nos centramos en MariaDB podemos indicar las restricciones mediante diferentes parámetros:
CREATE [TEMPORARY] TABLE [db.]tabla (
campo1 tipo [(tamaño)]
[NOT NULL | NULL]
[DEFAULT valor] -- valor por defecto
[UNIQUE [KEY] | PRIMARY KEY] -- clave única o primaria
[REFERENCES tablaexterna [(campoexterno1, campoexterno2)] -- atributo clave ajena
[ON DELETE {CASCADE | SET NULL | NO ACTION}] -- propagación de borrados
[ON UPDATE {CASCADE | SET NULL | NO ACTION}], -- propagación de actualizaciones
....,
[CONSTRAINT de múltiples campos]) -- definición de restricciones
ENGINE = Innodb; -- motor de ejecución
Las restricciones las podemos definir a nivel de opciones dentro de cada campo, o tras la definición de todos los campos, mediante la keyword CONSTRAINT
tanto en MariaDB como en PostgreSQL.
Claves únicas¶
Cuando marcamos en el modelo relacional una propiedad como UK
, es decir, clave única o alternativa, estamos indicando que dicho atributo no permite repetidos. Para ello, en el modelo físico y mediante SQL, ahora usaremos la propiedad UNIQUE
, que provocará la creación de un índice (los índices los estudiaremos en la unidad 9).
Recuerda que las claves alternativas, normalmente, son una de las claves candidatas que no hemos elegido como clave primaria:
CREATE TABLE EMPLEADO(
dni VARCHAR(9) PRIMARY KEY,
nSegSocial VARCHAR(15) UNIQUE,
nombre VARCHAR(40));
La principal diferencia entre una clave primaria y una clave única es que la clave única permite valores nulos.
Nombrando las restricciones
Aunque hemos visto que podemos añadir las restricciones asociadas a cada campo, es mucho mejor definirlas tras todos los campos, haciendo uso de la keyword CONSTRAINT
poniéndole un nombre a la restricción. Así pues, como norma general, nombraremos las restricciones con PK
, UK
, FK
, etc... y utilizando la barra de subrayado como separador, seguido del nombre de la tabla y/o los campos implicados.
Al asignarles un nombre, más adelante podremos acceder a las restricciones para modificarlas o eliminarlas.
De este modo, la tabla anterior queda mucho mejor si la definimos mediante:
CREATE TABLE EMPLEADO(
dni VARCHAR(9),
nSegSocial VARCHAR(15),
nombre VARCHAR(40),
CONSTRAINT PK_EMPLEADO PRIMARY KEY (dni),
CONSTRAINT UK_EMPLEADO_SEGSOC UNIQUE KEY (nSegSocial)
);
Claves ajenas¶
Para indicar las claves ajenas, aunque podemos hacerlo a nivel de campo, es mucho mejor acostumbrarse a definirlas siempre como restricciones. Para ello, usaremos la siguiente estructura, donde col
es el campo que apunta a la clave primaria clave
de la tabla tabla
.
CONSTRAINT nombre FOREIGN KEY col REFERENCES tabla (clave)
Así pues, un ejemplo de tabla con clave primaria y claves ajenas definidas mediante CONSTRAINT
sería:
CREATE TABLE ALQUILER (
dni VARCHAR(9),
codPelicula INT UNSIGNED,
CONSTRAINT PK_ALQUILER PRIMARY KEY (dni, codPelicula),
CONSTRAINT FK_ALQ_CLI FOREIGN KEY (dni) REFERENCES CLIENTE(dni),
CONSTRAINT FK_ALQ_PEL FOREIGN KEY (codPelicula) REFERENCES PELICULA(cod)
);
Cuando tenemos una clave ajena compuesta, sólo hemos de indicar los atributos separándolos con comas:
CREATE TABLE EXISTIR (
tipo CHAR(9),
modelo INT,
numAlmacen INT,
cantidad DECIMAL(7),
CONSTRAINT PK_EXISTIR PRIMARY KEY(tipo, modelo, numAlmacen),
CONSTRAINT FK_EXI_PIE FOREIGN KEY (tipo, modelo) REFERENCES PIEZA,
CONSTRAINT FK_EXI_ALM FOREIGN KEY (numAlmacen) REFERENCES ALMACEN
);
Propagación¶
Cuando se elimina (ON DELETE
) o actualiza (ON UPDATE
) un campo que está referenciado mediante una clave ajena, debemos indicar qué comportamiento de propagación debe realizar el SGBD con los datos en las tablas origen.
Vamos a retomar el ejemplo que vimos en la unidad 3, sobre un estudiante y las asignaturas que cursa, el cual se podría traducir en el siguiente DDL:
CREATE TABLE ESTUDIANTE (
nif varchar(9),
codigo int,
nombre varchar(32),
fMatricula date,
direccion varchar(64),
CONSTRAINT PK_ESTUDIANTE PRIMARY KEY(nif)
);
CREATE TABLE CURSAR (
nifEstudiante varchar(9),
asignatura int,
anyo int,
repetidor boolean,
CONSTRAINT PK_CURSAR PRIMARY KEY(nifEstudiante, asignatura, anyo),
CONSTRAINT FK_CUR_EST FOREIGN KEY (nifEstudiante) REFERENCES ESTUDIANTE(nif)
);
Y un ejemplo de las tablas con datos sería:
-
ESTUDIANTE
nif codigo nombre fMatricula direccion 12345678A 1 Pedro Casas 1/9/24 Avenida de la libertad, 23 48123456B 2 Mireia Vidal 1/9/24 Porta de la Morera, 6 -
CURSAR
nifEstudiante* asignatura anyo repetidor 12345678A 1 2024 true 48123456B 1 2024 false 12345678A 2 2023 false
¿Cómo repercute el sistema si eliminamos el estudiante 12345678A
de la tabla ESTUDIANTE
? ¿O le cambiamos el nif
(algo inusual, pero que puede pasar si nos hemos equivocado al teclearlo)? ¿Qué opciones tenemos para respetar la integridad referencial?
Las posibilidades son:
NO ACTION
: se impide la operación, rechazando el borrado o la actualización. Este es el comportamiento por defecto si no indicamos nada.CASCADE
: la operación se propaga de la tabla origen a la de destino. Esto es, si borramos el estudiante, borrará los registros deCURSAR
. Si modificamos sunif
, también modificará losnifEstudiante
deCURSAR
.SET NULL
: la clave ajena se pone aNULL
, de manera que se pierde la relación existente, provocando probablemente inconsistencia de datos.
Si volvemos a definir la tabla CURSAR indicando las restricciones de propagación, podríamos hacer:
CREATE TABLE CURSAR (
nifEstudiante varchar(9),
asignatura int,
anyo int,
repetidor boolean,
CONSTRAINT PK_CURSAR PRIMARY KEY(nifEstudiante, asignatura, anyo),
CONSTRAINT FK_CUR_EST FOREIGN KEY (nifEstudiante) REFERENCES ESTUDIANTE(nif)
ON DELETE CASCADE
ON UPDATE NO ACTION
);
Autoevaluación
¿Por qué en este caso concreto no podemos indicar la acción SET NULL
?
Validaciones¶
Para poder incluir restricciones de validación y que los datos introducidos cumplan ciertas condiciones, emplearemos el atributo CHECK
, tanto tras la definición de un atributo como una restricción con CONSTRAINT
:
CREATE TABLE INGRESO1 (
cod DECIMAL(5) PRIMARY KEY,
concepto VARCHAR(40) NOT NULL,
importe DECIMAL(11,2) CHECK (importe>0 AND importe<8000));
CREATE TABLE INGRESO2 (
cod DECIMAL(5) PRIMARY KEY,
concepto VARCHAR(40) NOT NULL,
importe DECIMAL(11,2),
CONSTRAINT CK_INGRESO CHECK (importe>0 AND importe<8000));
También podemos crear validaciones que comparen el valor de dos atributos de la tabla:
CREATE TABLE INGRESO3(
cod DECIMAL(5),
concepto VARCHAR(40) NOT NULL,
importeMax DECIMAL(11,2),
importe DECIMAL(11,2),
CONSTRAINT PK_INGRESO (cod),
CONSTRAINT CK_INGRESO CHECK (importe<importeMax));
Gestionando¶
Además de indicar las restricciones mientras creamos una tabla, podemos hacerlo mediante sentencia ALTER TABLE
.
Así pues, para añadir una nueva restricción usaremos la opción ADD
:
ALTER TABLE tabla ADD [CONSTRAINT nombre] tipoDeRestricción (columnas);
Para eliminar, la opción DROP
:
ALTER TABLE tabla DROP {PRIMARY KEY | CONSTRAINT nombreRestricción} [CASCADE]
Debes tener en cuenta que para eliminar una clave primaria, no debe haber ninguna clave ajena apuntando a ella. Además, si la clave primaria ya es clave ajena, antes de poder borrar la clave, deberemos borrar las claves ajenas que formen parte de la clave primaria y luego borrar sus índices. Por ejemplo:
ALTER TABLE relacionNM DROP FOREIGN KEY PK_MUCHOS_N;
ALTER TABLE relacionNM DROP FOREIGN KEY PK_MUCHOS_M;
ALTER TABLE relacionNM DROP INDEX FK_MUCHO_N;
ALTER TABLE relacionNM DROP INDEX FK_MUCHO_M;
ALTER TABLE relacionNM DROP PRIMARY KEY;
Respecto a las claves, si queremos deshabilitarlas para hacer algún tipo de modificación, haremos uso de las opciones ENABLE
o DISABLE
según convenga:
ALTER TABLE tabla ENABLE KEYS;
ALTER TABLE tabla DISABLE KEYS;
Finalmente, si queremos comprobar todas las restricciones que tiene una tabla, podemos ejecutar la sentencia SHOW CREATE TABLE
.
SHOW CREATE TABLE CURSAR;
-- +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- | Table | Create Table |
-- +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- | CURSAR | CREATE TABLE `CURSAR` (
-- `nifEstudiante` varchar(9) NOT NULL,
-- `asignatura` int(11) NOT NULL,
-- `anyo` int(11) NOT NULL,
-- `repetidor` tinyint(1) DEFAULT NULL,
-- PRIMARY KEY (`nifEstudiante`,`asignatura`,`anyo`),
-- CONSTRAINT `FK_CUR_EST` FOREIGN KEY (`nifEstudiante`) REFERENCES `ESTUDIANTE` (`nif`)
-- ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_uca1400_ai_ci |
-- +--------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
-- 1 row in set (0.001 sec)
Organizando las restricciones
Para tener una mejor organización de las restricciones, definiremos:
- a nivel de columnas:
DEFAULT
,NOT NULL
yAUTO_INCREMENT
. - mediante
CONSTRAINT
tras la definición de las columnas:PRIMARY KEY
,UNIQUE
,FOREIGN KEY
yCHECK
.
Otros elementos
Existen otras estructuras que forman parte de DDL pero que veremos más adelante son:
- Vistas: Permiten crear una representación externa (tabla virtual) a partir de una consulta, mediante
CREATE VIEW
. Las estudiaremos en la unidad 8. - Índices: Se utilizan para acelerar las consultas, mediante
CREATE INDEX
. Los estudiaremos en la unidad 9. - Disparadores (triggers): Se asocian a operaciones que se producen en el sistema (evento) para realizar otra acción. Los estudiaremos en la unidad 11.
Cargando datos¶
A la hora de cargar datos en una base de datos, además de las operaciones de inserción individual o múltiple de registros, todos los SGBD incorporan herramientas para la importación tanto de datos como recuperación de copias de seguridad que nos sirven para tener un entorno listo para trabajar.
Ejecutando un script¶
Si ya tenemos un script, por ejemplo, con las sentencias CREATE TABLE
definidas o diferentes instrucciones que insertan o modifican datos, mediante la línea de comandos podemos ejecutarlas a través de la entrada estándar (<
) o haciendo uso de parámetros:
mariadb -u usuario -p nombre_de_base_de_datos < archivo.sql
psql -U usuario -d nombre_de_base_de_datos < archivo.sql
psql -U usuario -d nombre_de_base_de_datos -f archivo.sql
Si en cambio lo queremos hacer dentro de cada cliente (una vez nos hemos autenticado con el usuario y contraseña):
Cargando datos desde archivos¶
Si en vez de un script tenemos los datos a cargar y ya tenemos nuestra estructura de datos creada (y probablemente con datos ya existentes), tenemos otras posibilidades:
En MariaDB usaremos la herramienta mariadb-import que permite cargar datos en formato texto, csv, etc...:
mariadb-import --local -u usuario -p nombre_de_base_de_datos /ruta/al/archivo.csv
Otra forma es hacer uso de datos almacenados en ficheros de texto y cargarlos directamente en una tabla es utilizar el comando LOAD DATA INFILE
:
LOAD DATA INFILE '/ruta/al/archivo.csv'
INTO TABLE empleados
FIELDS TERMINATED BY ',' -- Separador de campos (coma)
LINES TERMINATED BY '\n' -- Fin de línea
IGNORE 1 LINES -- Ignora la primera línea (encabezados)
(nombre, puesto, salario); -- Especifica las columnas
Esta opción es mucho más eficiente que ejecutar múltiples sentencias INSERT
.
En cambio, para exportar los datos, podemos crear un volcado de la base de datos mediante mariadb-dump:
mariadb-dump -u usuario -p nombre_de_base_de_datos > dump.sql
mariadb-dump -u usuario -p --all-databases > dump.sql
Usaremos las herramientas pg_dump y pg_restore para, respectivamente, extraer a un archivo y restaurar los datos de una base de datos.
pg_dump -Fc mydb > db.dump
Si queremos exportar todas las bases de datos, usaremos el comando pg-dumpall:
pg_dumpall > db.out
Para restaurarlas todas haríamos:
psql -f db.out postgres
Importando datos en binario
Para un administrador de bases de datos, es muy común cargar datos a partir de una copia de seguridad. Para ello, dependiendo del SGBD, utilizaremos una herramienta u otra.
Usaremos la herramienta mariabackup, la cual es una herramienta de MariaDB para crear copias de seguridad físicas (binarias) de las bases de datos.
Para restaurar una copia de seguridad haremos:
mariabackup --copy-back --target-dir=/ruta/a/la/copia_de_seguridad
Y para realizar la copia de seguridad:
mariabackup --prepare --target-dir=/ruta/a/la/copia_de_seguridad
La herramienta pg_basebackup permite crear copias de seguridad en PostgreSQL
Para crear la copia de seguridad haremos:
pg_basebackup -D /ruta/destino -Fp -Xs -P -U usuario
Para restaurar los datos, hemos de copiar los archivos del backup a la ruta del directorio de datos y reiniciar el servicio (tareas normalmente realizadas por perfiles más cercanos a la administración de sistemas que a un desarrollador).
DML¶
Una vez que ya sabemos cómo crear las estructuras de datos para guardar la información, vamos a aprender a gestionarla.
CRUD
Las operaciones DML se asocian al acrónimo CRUD:
- Create: insertar
- Read: consultar
- Update: modificar
- Delete: eliminar
Sobre una tabla, las operaciones que vamos a poder realizar son:
- insertar datos en una tabla
- modificar datos de una tabla
- eliminar datos de una tabla
- consultar datos de una o más tablas.
Cuando hablamos de "datos de una tabla", podemos referirnos a registros completos, campos concretos o incluso elementos relacionados de varias tablas.
Para realizar estas operaciones, se utiliza el subconjunto DML (Data Manipulation Language) de SQL. En esta unidad nos vamos a centrar en la inserción, modificación y eliminación de datos, dejando las consultas para trabajarlas en profundidad en las siguientes unidades.
Para los siguientes ejemplos vamos a trabajar con la siguiente tabla:
CREATE TABLE dml8a (
id INT PRIMARY KEY,
nombre VARCHAR(64) NOT NULL DEFAULT "Chuck Norris",
email VARCHAR(64));
Inserciones¶
La sentencia para insertar datos es INSERT INTO
, tanto en MariaDB como en PostgreSQL:
INSERT INTO tabla VALUES (valor1, valor2, ...);
Veamos unos ejemplos. Podemos insertar en la tabla dml8a
utilizando:
INSERT INTO dml8a VALUES (1, "Aitor Medrano", "a.medrano@edu.gva.es");
INSERT INTO dml8a(id, nombre) VALUES (2, "Pedro Casas");
Si no ponemos los nombres de los campos detrás del nombre de la tabla, en los valores deberemos introducir todos los valores siguiendo el orden de los campos según fueron creados (siempre puedes consultar la estructura de una tabla mediante DESCRIBE nombre_tabla
). Además, los tipos de los datos deben concordar, incluso en el tamaño o precisión, debiendo haber una correspondencia posicional uno a uno entre las columnas y los valores introducidos.
Si queremos insertar varios registros con una única instrucción, los indicaremos separados por comas:
INSERT INTO dml8a VALUES (3, "María Sánchez", "maria@gmail.com"),
(4, "Eva Amaral", "eva@gmail.com");
-- Query OK, 2 rows affected (0.002 sec)
-- Records: 2 Duplicates: 0 Warnings: 0
Si id
fuera un campo auto incrementable podríamos omitir su valor, y automáticamente le asignaría el siguiente valor de la secuencia:
INSERT INTO dml8a(nombre) VALUES ("José Manuel Pérez");
Si queremos insertar los valores por defectos asociados a una columna o un valor nulo (por ejemplo, a una clave ajena) usaremos DEFAULT
y NULL
respectivamente. Por ejemplo:
INSERT INTO dml8a VALUES (66, DEFAULT, NULL);
En el caso de PostgreSQL, si queremos recuperar la información sobre la fila recién insertada, podemos emplear la cláusula RETURNING
:
INSERT INTO dml8a (nombre, email)
VALUES ("David Suárez", "david@gmail.com")
RETURNING id;
Gestión de errores
Si insertamos datos con tipos diferentes, dejamos campos no nulos en blanco, o insertamos datos en un campo que es clave ajena y no cumplimos la integridad referencial, el SGBD nos devolverá diferentes mensajes:
INSERT INTO dml8a VALUES ("veinte", "Juan", 33);
-- ERROR 1366 (22007): Incorrect integer value: 'veinte' for column `pruebas`.`dml8a`.`id` at row 1
En cambio, si ejecutamos INSERT INTO dml8a VALUES (20, "Juan", 33);
no dará error porque convertirá el número 33 a un string antes de insertarlo.
También podemos insertar datos con el resultado de una consulta mediante la instrucción INSERT SELECT
, pero esta operación la estudiaremos en profundidad más adelante.
Modificaciones¶
La sentencia para actualizar datos es UPDATE ... SET
, tanto en MariaDB como en PostgreSQL:
UPDATE tabla SET campo=valor1 [,campo2=valor2];
Si queremos decidir qué elementos modificar, añadimos la expresión WHERE
, ya que, si no, actualizaremos todos los registros de la tabla:
UPDATE tabla SET campo=valor WHERE condicion;
Veamos unos ejemplos. Podemos modificar el email de un usuario concreto:
UPDATE dml8a SET email="pedro@gmail.com" WHERE id=2
O modificar varios campos de una sola vez:
UPDATE dml8a SET nombre="Pedro Casas García",
email="pedro.casas@gmail.com"
WHERE id=2
También podemos realizar cálculos al modificar un campo:
-- Incrementamos la población de Elche en un 10%
UPDATE CIUDAD SET poblacion = poblacion * 1.10 WHERE nombre = "Elche";
El filtrado de los datos mediante la instrucción WHERE
ofrece múltiples posibilidades, tanto con operadores relacionales, aritméticos, funciones de tratamiento de textos, fechas, etc... como comparación con el resultado de una consulta u operaciones de conjuntos. A partir de la próxima unidad profundizaremos en todas las alternativas del atributo WHERE
.
Borrados¶
No te olvides...
Si no conoces este meme, hazte el favor y mira el siguiente vídeo.
La sentencia para borrar datos es DELETE FROM ...
, tanto en MariaDB como en PostgreSQL:
DELETE FROM tabla;
Si queremos decidir qué elementos eliminar, añadimos la expresión WHERE
, ya que si no, eliminaremos todos los registros de la tabla, lo cual es muy peligroso:
DELETE FROM tabla WHERE condicion;
Integridad referencial
Recuerda que, si eliminamos un registro referenciado por una clave ajena, se ejecutará la regla descrita en la tabla de origen, de manera, que es posible que no podamos eliminar ciertos datos al estar referenciado desde otra tabla (si estaban configuradas con NO ACTION
o RESTRICT
).
En cambio, si la clave ajena se configuró con CASCADE
, al borrar el registro, se borrarán todos los registros de las tablas origen que referenciaban a éste.
Finalmente, si se configuró con NULL
, la tabla origen modificará el valor de su clave ajena se pondrá a nulo.
Conviene destacar que, si queremos eliminar todos los datos de una tabla, es mucho más eficiente utilizar el comando TRUNCATE TABLE
, tanto en MariaDB como en PostgreSQL
TRUNCATE TABLE dml8a
Por debajo, cuando truncamos una tabla, realmente está eliminando la tabla y volviéndola a crear, eliminando todos los ficheros de datos y reiniciando los campos autonuméricos.
Reto II - Creamos¶
En el reto anterior diseñamos una base de datos. En este reto nos vamos a centrar en la creación del modelo físico, creando y cargando datos, para posteriormente definir una serie de informes y KPIs que generaremos mediante consultas utilizando SQL.
KPI
Un KPI (key performance indicator) es una métrica cuantitativa que muestra un valor importante para el negocio, permitiendo comparar el desempeño de alguna característica.
Ejemplos de KPIs podrían ser la cantidad de pedido realizados durante el último més, beneficios obtenido en el último año, comparación Year-over-year (YoY) de las ventas de este mes respecto a las ventas del mismo mes pero en el año pasado, etc...
Así pues, inicialmente crearemos la estructura de datos necesaria, la cargaremos con datos ficticios, y deberemos pensar qué información queremos extraer de ella.
Referencias¶
-
Sintaxis SQL oficial de PostgreSQL y MariaDB.
-
Materiales sobre el módulo de BD:
- DDL y DML - Institut Obert de Catalunya
- Creación de bases de datos en MySQL de José Juan Sánchez
- Introducción a SQL, DDL y DML de Jorge Sánchez
- Tratamiento de datos de Javier Gutiérrez
- Diseño físico de bases de datos - DDL y Modificación de bases de datos - DML de gestionbasesdatos.readthedocs.io
- Introducción a SQL, por Luis Valencia y David Orellana, de la Universidad de Sevilla.
Actividades¶
-
AC501. (RABD.2 // CE2a // 3p) En MariaDB, descarga la base de datos world.sql.zip y tras descomprimirla, cárgala mediante PhpMyAdmin. A continuación, responde a las siguientes cuestiones y adjunta una captura de pantalla para cada una de ellas:
- ¿Cuántas tablas tiene?
- ¿Cuántos y de qué tipos son los campos de la tabla
city
? - ¿Cuál es el formato de almacenamiento de la base de datos? ¿Qué motor de almacenamiento emplea?
A continuación, mediante DBeaver, conéctate a la URL que tienes en Aules y averigua:
- ¿Qué bases de datos existen?
- ¿Cuantas tablas tiene cada base de datos?
- ¿Qué tabla tiene más datos (indica el tamaño que ocupan y cuantos registros hay exactamente)?
-
AP502. (RABD.2 // CE2a // 3p) En PostgreSQL, mediante SQL, crea una base de datos con un tablespace definido previamente y el juego de caracteres que permita el uso de emojis y que no sea case sensitive.
En dicha tabla, crea una tabla cuya clave sea un valor autonumérico y tenga un atributo que permita introducir cadenas de texto.
A continuación, mediante PgAdmin, obtén la información tanto de la base de datos como de la tabla, e inserta una frase que contenga algún emoji. Comprueba que la información se ha almacenado correctamente.
Debes entregar en Aules:
- Comandos SQL empleados.
- Capturas de pantalla de PgAdmin con la información solicitada.
-
AC503. (RABD.2 // CE2b, CE2c, CE2d // 3p) Crea el siguiente modelo relacional en MariaDB (y opcionalmente en PostgreSQL), y adjunta tanto las instrucciones DDL como capturas de pantalla con la estructura de las tablas una vez creadas (utiliza el comando para describirlas):
-
Tabla
FABRICANTE
- Campos:
codFabricante
: entero autoincrementablenombre
: cadena 32pais
: cadena 32
- Restricciones:
- La clave primaria es
codFabricante
- El
nombre
no puede ser nulo
- La clave primaria es
- Campos:
-
Tabla
ARTICULO
- Campos:
codigo
: cadena 32codFabricante
: enteropeso
: numérico con dos decimalescategoria
: cadena 16precioVenta
: numérico con dos decimalesprecioCompra
: numérico con dos decimalesexistencias
: entero sin signo
- Restricciones:
- La clave primaria es:
codigo
,codFabricante
,peso
,categoría
. - La
categoria
ha de serprimera
,segunda
otercera
.
- La clave primaria es:
- Campos:
- Modifica el atributo
pais
de la tablaFABRICANTE
para que el valor por defecto seaEspaña
. - Añade una columna antes de
pais
en la tablaFABRICANTE
para guardar laprovincia
. - Renombra la tabla
ARTICULO
aPRODUCTO
.
-
-
AC504. (RABD.2 // CE2e // 3p) A partir del ejercicio AC503, vuelve a indicar la instrucción
CREATE TABLE
de la tablaPRODUCTO
, pero ahora llámalaPIEZA
, de manera que:codFabricante
sea una clave ajena que apunta aFABRICANTE
.- al eliminar un fabricante concreto, si hay artículos de dicho fabricante, se deben prohibir la operación. En cambio, si modificamos el código de la tabla
FABRICANTE
, la clave ajena también cambiará su valor. - los atributos
precioVenta
yprecioCompra
deben de ser mayores de 0.
A continuación, mediante operaciones
ALTER TABLE
añade las siguientes restricciones:- La columna
PRODUCTO.categoria
no puede admitir valores nulos. - El
peso
debe ser superior o igual a 1.00.
-
AR505. (RABD.2 // CE2e // 3p) Mediante MariaDB (y opcionalmente en PostgreSQL), crea las tablas necesarias para representar que un empleado tiene diferentes números de teléfono que queremos almacenar, teniendo en cuenta que:
- Los teléfonos se presentan mediante un prefijo de tres caracteres y número de 9 dígitos.
- Si eliminamos a un empleado, también eliminaremos todos sus teléfonos.
Para ello, haciendo uso de la herramienta gráfica apropiada, inserta datos (varios empleados y varios teléfonos para cada empleado) y posteriormente prueba a eliminar un empleado.
Adjunta tanto los scripts DDL como capturas de las tablas antes y después de realizar la operación de borrado.
-
AC506. (RABD.2 // CE2b, CE2c, CE2d // 3p) Crea el siguiente modelo relacional en MariaDB (y opcionalmente en PostgreSQL):
DEPARTAMENTO (codD, nombre, direcc)
· PK: (codD)
· VNN: (nombre)EMPLEADO (dni, nombrec, salario, direcc, departamento
*
)
· PK: (dni)
· UK: (nombrec)
· VNN: (nombrec)
· VNN: (salario)
· VNN: (departamento)
· FK: (departamento) → DEPARTAMENTOAdemás, el salario de cada empleado debe ser igual o superior a 900, y su valor por defecto es de 1000.
A continuación, haciendo uso de
ALTER TABLE
:- Elimina la clave ajena de la tabla
EMPLEADO
. - Vuelve a añadir la clave ajena, de manera que, al modificar un departamento, el cambio se propague en cascada, pero que al eliminarlo, no realice ningún acción.
Finalmente, elimina las tablas creadas.
- Elimina la clave ajena de la tabla
- AP507. (RABD.2 // CE2b, CE2c, CE2d, CE2e // 3p) A partir de la actividad AC408 (que a su vez se basa en la AC203), crea las instrucciones DDL necesarias para crear el modelo físico.
- AP508. (RABD.4 // CE4a // 3p) Haciendo uso de MariaDB y del cliente
mariadb
, a partir del siguiente script con las instrucciones para crear una base de datos, carga los datos que tienes disponibles en ap508.zip haciendo uso de las instruccionesLOAD DATA
. Finalmente, mediantemysqldump
realiza una exportación de toda la base de datos (con los datos cargados), y adjunta tanto los comandos empleados como el resultado de la exportación.
-
AC509. (RABD.4 // CE4a, CE4b // 3p) A partir de la actividad AC504, se pide:
- Modifica el charset de la tabla
FABRICANTE
autf8mb4
. -
Inserta los siguientes datos utilizando las herramientas gráficas en MariaDB (y opcionalmente en PostgreSQL):
-
FABRICANTE
codFabricante nombre provincia pais 1 Sony Kantō Japón 2 Microsoft Nuevo México USA 3 Nintendo Kantō Japón -
PIEZA
codigo codFabricante* peso categoria precioVenta precioCompra existencias PS5 1 4.5 primera 500.00 550.00 123 PS4 1 2.8 primera 300.00 400.00 234 XSX 2 4.4 primera 500.00 550.00 345 NSW 3 0.3 segunda 225.00 330.00 456
-
-
¿Has podido insertar la pieza
NSW
? ¿Por qué? - Indica las sentencias SQL para vaciar las tablas.
- Anota las sentencias SQL para introducir de nuevo los datos.
- Modifica el charset de la tabla
-
AC510. (RABD.4 // CE4b // 3p) Carga el siguiente script de datos sobre las tablas de la AC506, y realiza las siguientes modificaciones:
- Añade un nuevo departamento llamado
Formación
. - Renombra el departamento
IT
porInformática
. - Añade el campo
email
al final de la tablaDEPARTAMENTO
, y rellénalos todos conemail@ac510.com
. - Incrementa el salario de todos los empleados del departamento de informática en 300€.
- Inserta un nuevo empleado con tus datos en el departamento
Formación
. - Cambia al empleado
12345678A
al departamento deFormación
. - Modifica la clave del departamento
Formación
a 666. - Elimina el departamento
Legal
. ¿Has podido? ¿Qué ha pasado con sus empleados?
- Añade un nuevo departamento llamado
-
AP511. (RABD.4 // CE4b // 3p) Sobre la actividad AC510, añade una nueva tabla
DIETAS
donde podamos anotar los gastos ocasionados por las dietas de los empleados, como son la fecha, el coste y una descripción de la misma. A continuación, inserta 10 dietas de diferentes empleados.Debes tener en cuenta que si eliminamos un empleado, no debe permitir realizar la operación ni eliminar las dietas, ya que, si no, luego no nos cuadraría la contabilidad. ¿Cómo solucionamos dar de baja a un empleado pero no eliminarlo del sistema?
-
AR512. (RABD.4 // CE4b // 3p) Carga el script
ar512.sql
con el DDL sobre una base de datos de libros y préstamos, y realiza las siguientes operaciones:-
Inserta los siguientes valores en las tablas:
-
LIBRO
codigo titulo autor stock genero 1 Historia de España J. Pérez 5 HIS 2 Reina Roja J. Gómez-Jurado 33 NOV -
PRESTAMO
codLibro codSocio fentrega fdevolucion 1 1 15/10/24 2 1 25/10/24
-
-
Realiza las siguientes operaciones:
- Cambia la clave primaria de libro al título. Realiza los cambios necesarios para que la tabla libros y prestamos estén relacionadas.
- Eliminar aquellos libros que sean del género de historia. ¿Se elimina el registro de la tabla
LIBRO
? Razona la respuesta.- Si no lo has conseguido, cambia la restricción de la clave ajena para que la operación se propague.
- Vuelve a realizar la operación de borrado de libros.
- ¿Cuántos registros tiene ahora la tabla
PRESTAMO
?
- Inserta los siguientes libros (Loba Negra, MariaDB Essentials), buscando en Internet la información que necesites.
- Reduce el stock de
Reina Roja
en 3 unidades. - Haz que el libro prestado tenga como fecha de devolución el 31/12/24.
- Cambia el nombre del autor de
Reina Roja
porJuan Gómez Jurado
.
-
-
AC513. (RABD.2 // CE2a, CE2b, CE2c, CE2d, CE2e // 3p) A partir del siguiente modelo relacional, genera el script DDL (
ac513.sql
) para la creación de las tablas mediante MariaDB en una base de datos denominadaac513
(el script debe poder ejecutarse una y otra vez, de manera que si ya existe la base de datos debe borrarla, y para crear cada tabla, debe comprobar que no exista previamente).El diccionario de datos es el siguiente:
TABLA CAMPO TIPO RESTRICCIONES ALUMNOS
id
Entero sin signo autoincrementable PK dni
Carácter (9) UK nombre
Texto (64) obligatorio apellidos
Texto (64) obligatorio sexo
{H, M} obligatorio dirección
Texto (128) telefono
Carácter (12) fnac
Fecha ASIGNATURAS
id Entero sin signo autoincrementable PK nombre
Texto (64) obligatorio aula
entero Entre 1 y 10, por defecto se asigna el aula 1 duracion
Entero Entre 1 y 12, por defecto su duración es 3 NOTAS
idAlumno
Entero sin signo PK, FK → ALUMNOS
idAsignatura
Entero sin signo PK, FK → ASIGNATURAS
fecha
Fecha obligatorio calificación
Numérico con 1 decimal Entre 0.0 y 10.0, obligatorio A continuación, modifica la estructura de las tablas mediante
ALTER TABLE
:- Cambia el nombre de las tablas y ponlas en singular.
- Añade una columna
recuperacion
aNOTA
para almacenar la posible calificación de un examen de recuperación. Dicha calificación debe estar comprendida entre 0 y 10. - Modifica la columna
sexo
deALUMNO
para añadir un tercer valor (indeterminado - I). - Modifica la clave primaria de
NOTA
para incluir también la fecha. - Modifica la clave ajena de
NOTA
haciaALUMNO
para que al realizar un borrado de un alumno, se eliminen todas sus notas.
- AC514. (RABD.4 // CE4b // 3p) Continua la actividad anterior, y crea el script
ac514.sql
donde insertes los siguientes datos para el alumnado y los cursos mediante instruccionesINSERT
:
* `ALUMNO`
| **DNI** | **Nombre** | **Apellidos** | **Sexo** | **Dirección** | **Teléfono** | **Fecha de nacimiento** |
| --- | --- | --- | --- | --- | --- | --- |
| 12345678A | Johnny | Mentero | H | Debajo del puente, -1 | | 25/12/80 |
| 23456789B | María | Gracia | M | | 636112233 | |
| 34567890C | Armando | Casas | I | Villarriba, 33 | 636223344 | 31/12/90 |
| 45678901D | Mario | Neta | H | Circo del Sol, 22 | 636334455 | |
| 56789012E | Susana | Oria | M | Calle La Granja | | 3/1/00 |
* `ASIGNATURA`
| **id** | **Nombre** | **Aula** | **Duración** |
| --- | --- | --- | --- |
| | SQL | 3 | 9 |
| 666 | Humor | | 12 |
| | Cocina | 6 | |
A continuación, inserta los siguientes datos de calificaciones mediante `LOAD DATA`, creando previamente el archivo `ac514.csv` con la siguiente información:
| **Alumno** | **Asignatura** | **Fecha** | **Nota** | **Recuperación** |
| --- | --- | --- | --- | --- |
| 1 | 1 | 20/12/20 | 7,25 | |
| 1 | 666 | 20/12/20 | 9,99 | |
| 2 | 2 | 10/1/21 | 5 | |
| 2 | 666 | 10/1/21 | 7 | |
| 3 | 1 | 10/1/21 | 3 | 6 |
| 3 | 666 | 10/1/21 | 3 | 6 |
Finalmente, realiza las siguientes operaciones de modificación y borrado de datos:
1. Modificar:
1. El teléfono del alumno `1` es `666111222`
1. La dirección de todos los alumnos hombres es `Donde me lleve la vida`.
1. El apellido de todos alumnos que se llamen `Armando` ahora es `Bullas`.
1. Todos los cursos tendrán una unidad más de duración
1. Los alumnos que no tengan dirección se les asignará `Internet`.
1. Eliminar:
1. Borra las calificaciones inferiores a 4. ¿has podido? ¿cuantas calificaciones quedan?
1. Borra la información de `Johnny Mentero`. ¿has podido? ¿cuantos calificaciones quedan?
1. Elimina el curso de cocina ¿has podido? ¿cuantas asignaturas quedan?
1. Elimina los alumnos que no tengan teléfono ¿has podido? ¿cuantos alumnos quedan? ¿cuantas calificaciones quedan?
1. Elimina todos los alumnos ¿has podido? ¿cuantos alumnos quedan? ¿cuantas calificaciones quedan?
Recuerda que para comprobar los datos que contiene una tabla puedes ejecutar en el cliente `SELECT * FROM tabla`;
-
PY515. (RABD.2, RABD.4 // CE2a, CE2b, CE2c, CE2d, CE2e, CE4a, CE4b // 10p + 10p) A partir del modelo diseñado en la actividad PY415 asociada al primer reto de diseño de la base de datos, ahora nos vamos a centrar en crear el modelo físico y cargar datos para trabajar con él. Para ello, como equipo, deberéis decantaros por un SGBD en concreto.
Para ello, se pide:
- Informe con el modelo ER, el modelo MR y justificación de las decisiones tomadas respecto a las restricciones incluidas en el modelo físico.
- Script SQL con las sentencias DDL.
- Script SQL con las sentencias DML para cargar datos.
- Script SQL para vaciar todas las tablas.
Se utilizará una rúbrica para su evaluación en base a la siguiente lista de cotejo:
- Limpieza y calidad de los scripts.
- Documentación de los scripts.
- El informe entregado no contiene faltas de ortografía.
- El informe entregado tiene un formato adecuado (portada, apartados, autores, etc...).
- El informe debe indicar cómo se han repartido las tareas y qué ha realizado cada alumno/a.
- Cada tabla contiene un mínimo de 10 registros, salvo justificación.
- En el caso de existir relaciones entre tablas, cada registro debe relacionarse con un mínimo de tres registros más.
- AR516. (RABD.2, RABD.4 // CE2a, CE2b, CE2c, CE2d, CE2e, CE4a, CE4b // 3p) Una vez finalizada la unidad, responde todas las preguntas del cuestionario inicial, con al menos un par de líneas para cada una de las cuestiones.
-
en MariaDB se hace con la cláusula
AUTO_INCREMENT
↩