Optimización de Bases de Datos: Normalización Relacional y Consultas MongoDB Avanzadas
Enviado por Chuletator online y clasificado en Informática y Telecomunicaciones
Escrito el en español con un tamaño de 9,08 KB
Modelado de Datos y Normalización en Bases de Datos Relacionales
Este documento aborda conceptos fundamentales de modelado de datos relacionales y su normalización, así como la manipulación de datos en MongoDB.
Esquemas de Bases de Datos Relacionales y Formas Normales
A continuación, se presentan esquemas de tablas relacionales y se identifican sus propiedades de normalización, específicamente en relación con la Segunda y Tercera Forma Normal (2FN y 3FN).
Tabla: Politico
Esquema: Politico(DNI, cargo1, cargo2, cargo3, NomPartido, votosObtenidos, siglasPartido, Matricula)
- Clave Primaria (PK):
DNI
- Observación de Normalización:
- Existe una dependencia funcional entre
NomPartido
ysiglasPartido
(y viceversa). Cada cambio en uno obliga a actualizar el otro en todas las filas. - Violación de la Tercera Forma Normal (3FN): Si
DNI
determinaNomPartido
yNomPartido
determinasiglasPartido
, entoncessiglasPartido
depende transitivamente de la clave primariaDNI
a través deNomPartido
(un atributo no clave). La tabla se encuentra en 2FN, pero no en 3FN.
- Existe una dependencia funcional entre
Tabla: CocheOficial
Esquema: CocheOficial(Matricula, marca, modelo, color, DNI)
- Clave Primaria (PK):
Matricula
- Observación de Normalización:
- Existe una dependencia transitiva:
Matricula
determinamodelo
, ymodelo
determinamarca
. Esto significa quemarca
depende deMatricula
a través demodelo
(un atributo no clave). - Anomalías: En las actualizaciones, es crucial verificar que la marca sea correcta para el modelo asociado.
- Violación de la Tercera Forma Normal (3FN): La tabla se encuentra en 2FN, pero no en 3FN debido a la dependencia transitiva.
- Nota: Los atributos
DNI
oMatricula
son esenciales para relacionar esta tabla con otras.
- Existe una dependencia transitiva:
Tabla: Partido
Esquema: Partido(NomPartido, siglasPartido, NomComunidadAuto, siglasComunidadAuto, EscañosObtenidos)
- Clave Primaria (PK):
(NomPartido, NomComunidadAuto)
o(siglasPartido, siglasComunidadAuto)
- Observación de Normalización:
NomPartido
determinasiglasPartido
:siglasPartido
depende parcialmente deNomPartido
, que es parte de la clave primaria compuesta.NomComunidadAuto
determinasiglasComunidadAuto
:siglasComunidadAuto
depende parcialmente deNomComunidadAuto
, que también es parte de la clave primaria compuesta.- Violación de la Segunda Forma Normal (2FN): La tabla se encuentra en 1FN, pero no en 2FN debido a estas dependencias parciales de atributos no clave sobre partes de la clave primaria.
Operaciones con Colecciones en MongoDB
Se asume la existencia de una colección llamada Ocio
con documentos que describen actividades de ocio. Cada documento tiene la siguiente estructura:
TipoOcio
: Categoría de la actividad (ej. "viajes", "música", "esquí", "cine").Empresa
: Entidad que ofrece la actividad (ej. "Warner", "Sony").Elemento
: Nombre específico de la actividad (ej. "viajeCanarias", "conciertoU3", "JuegosTronos").Nivel
: Popularidad o calificación de la actividad (número de estrellas entre 0 y 10).Coste
: Precio de la actividad (ej. precio del "viajeCanarias").Propiedades
: Array de pares (nombre, valor) con características particulares del elemento (ej.[{ "nombre": "dificultad", "valor": "media" }]
).
A continuación, se detallan las instrucciones de MongoDB solicitadas para diversas operaciones.
a) Agregación para Actividades Populares por Tipo y Empresa
Utilizando la operación aggregate
, se busca obtener para cada TipoOcio
y Empresa
: el tipo de ocio, la empresa, la media de los costes de sus actividades más populares (Nivel >= 7
), y el número total de actividades que cumplen dicha condición. Los resultados se filtrarán para incluir solo aquellos TipoOcio
que tengan más de 5 actividades populares. El resultado se clasificará de forma descendente por TipoOcio
y Empresa
.
db.Ocio.aggregate([
{ $match: { "Nivel": { $gte: 7 } } },
{ $group: {
_id: { ElTipo: "$TipoOcio", LaEmpresa: "$Empresa" },
mediaCoste: { $avg: "$Coste" },
totdocs: { $sum: 1 }
}},
{ $match: { "totdocs": { $gte: 5 } } },
{ $sort: { "_id.ElTipo": -1, "_id.LaEmpresa": -1 } }
]);
Nota sobre el ordenamiento: Se ha especificado el ordenamiento por los campos anidados dentro de _id
para mayor claridad y precisión en la clasificación descendente por TipoOcio
y luego por Empresa
.
b) Actualización o Inserción de Documento Único (Upsert)
Se requiere actualizar el primer documento que cumpla con las siguientes condiciones: TipoOcio: "montañismo"
, Empresa: "CabraMonteSA"
, Elemento: "CaminoSchmid"
, y que en sus Propiedades
contenga el par (dificultad: media)
. Si no existe un documento que cumpla estas condiciones, se insertará uno nuevo con estos datos. Los cambios a realizar (o datos a insertar) son: Nivel = 9
, Coste = 5
, y un nuevo atributo MeGusta: "sí"
.
Además, se solicita que ningún otro proceso pueda escribir en la colección hasta que esta operación finalice. Aunque $isolated
está obsoleto y las operaciones de escritura en MongoDB son atómicas a nivel de documento, se incluye para cumplir con la especificación original.
db.Ocio.updateOne(
{
TipoOcio: "montañismo",
Empresa: "CabraMonteSA",
Elemento: "CaminoSchmid",
Propiedades: { $elemMatch: { nombre: "dificultad", valor: "media" } }
},
{
$set: { Nivel: 9, Coste: 5, MeGusta: "sí" }
},
{
upsert: true
// $isolated: 1 // Nota: $isolated está obsoleto y no se recomienda para nuevas implementaciones.
// Las operaciones de escritura en MongoDB son atómicas a nivel de documento por defecto.
}
);
Correcciones: Se ha utilizado updateOne
en lugar de update
(que está obsoleto) y se ha corregido la sintaxis de $elemMatch
asumiendo que Propiedades
es un array de objetos con claves nombre
y valor
. El parámetro $isolated
se ha comentado con una nota sobre su obsolescencia.
c) Actualización Condicional de Costes con forEach
Utilizando una instrucción find
combinada con la función forEach
, se actualizarán las actividades cuyo TipoOcio
sea "montañismo", "rafting", "piragua" o "piano", y que además tengan un Nivel
bajo (menor que 5). La actualización consiste en bajar el coste proporcionalmente y añadir un atributo Oferta
, según las siguientes reglas:
- Si el
Coste
es menor de 1000:- Fórmula:
(10 - (5 - nivel) / 10) * coste
- Atributo:
Oferta = "regular"
- Fórmula:
- Si el
Coste
es mayor o igual a 1000:- Fórmula:
(8 - (5 - nivel) / 10) * coste
- Atributo:
Oferta = "buena"
- Fórmula:
db.Ocio.find({
TipoOcio: { $in: ["montañismo", "rafting", "piragua", "piano"] },
Nivel: { $lt: 5 }
}).forEach(function (myDoc) {
let newCoste;
let ofertaText;
if (myDoc.Coste < 1000) {
newCoste = ((10 - (5 - myDoc.Nivel)) / 10) * myDoc.Coste;
ofertaText = "regular";
} else {
newCoste = ((8 - (5 - myDoc.Nivel)) / 10) * myDoc.Coste;
ofertaText = "buena";
}
// Actualizar el documento en la base de datos
db.Ocio.updateOne(
{ _id: myDoc._id },
{ $set: { Coste: newCoste, Oferta: ofertaText } }
);
});
Correcciones: Se ha corregido la sintaxis de la consulta find
. Se ha reemplazado db.Ocio.save(myDoc)
por db.Ocio.updateOne({ _id: myDoc._id }, { $set: { ... } })
, que es la forma moderna y recomendada para actualizar documentos específicos. Se han eliminado las líneas de depuración y el texto irrelevante.