![]() |
La diferencia entre tapar un síntoma y quitar la causa. Añadir un parche que compensa el error deja el defecto dentro; encontrar la línea que falla y corregirla lo elimina.
Un buen diseño de base de datos facilita el mantenimiento, protege la información y ayuda a que las consultas funcionen bien. Estas recomendaciones sirven como guía: conviene adaptarlas al motor de base de datos, a la aplicación y a las necesidades del proyecto.
Elige una convención y aplícala a tablas y columnas. Los nombres en singular son una opción habitual: Estudiante en vez de Estudiantes. Usa nombres descriptivos, como FechaInscripcion en lugar de Fecha.
Ejemplo: Estudiante, Curso e Inscripcion, con columnas como FechaInscripcion y EstadoInscripcion.
Los espacios pueden obligar a escribir los nombres entre comillas o delimitadores, según el motor de base de datos.
Ejemplo: usa FechaNacimiento en vez de Fecha de nacimiento. También puedes usar fecha_nacimiento si esa es la convención elegida.
El nombre de un objeto debe describir su propósito. No hace falta indicar que es una tabla si ya se sabe por el contexto.
Ejemplo: usa Escuela en vez de TblEscuela o EscuelaTabla.
Las claves primarias permiten identificar registros y relacionarlos. No todas tienen que ser enteros: elige el tipo que mejor represente el dominio y considera si necesitas una clave natural, una clave generada o ambas.
Ejemplo: EstudianteId puede ser un entero generado por la base de datos. Para un país, un código como MX podría ser una clave natural adecuada.
Nunca guardes contraseñas como texto legible ni como valores que se puedan descifrar. La aplicación debe verificarlas mediante una función de hash diseñada para contraseñas, como Argon2id, bcrypt o scrypt.
Ejemplo: almacena el resultado del hash y sus parámetros. Al iniciar sesión, verifica la contraseña ingresada contra ese valor.
Los tipos compactos y compatibles con las relaciones pueden ayudar al rendimiento, pero el tipo de columna debe corresponder a los datos y las consultas reales. Una columna de texto puede ser una clave o un índice válido cuando el dominio lo justifica.
Ejemplo: usa un entero para EstudianteId. Para un código postal, conserva el texto si puede incluir ceros iniciales o letras.
Usa el tipo booleano que ofrezca el motor de base de datos. En algunos motores se representa internamente como BIT; en otros, como un tipo distinto. El nombre de la columna puede indicar claramente que contiene una condición.
Ejemplo: usa EsActivo o IsActive, con un valor verdadero o falso.
Cada aplicación o servicio debe usar una cuenta propia con solo los permisos necesarios. Reserva las cuentas administrativas para tareas administrativas.
Ejemplo: la cuenta de la aplicación puede leer y modificar las tablas que necesita, pero no crear usuarios ni cambiar la configuración del servidor.
Evita SELECT * en las consultas de la aplicación. Solicitar campos innecesarios puede aumentar el tráfico y complicar los cambios en el esquema.
Ejemplo: usa SELECT EstudianteId, Nombre FROM Estudiante si la pantalla solo muestra el identificador y el nombre.
Un ORM puede simplificar el acceso a los datos y el mantenimiento. Aun así, conviene revisar las consultas que genera y configurar correctamente las relaciones y la carga de datos.
Ejemplo: usa carga anticipada cuando necesites consultar los cursos de varios estudiantes. Evita cargar colecciones que la pantalla no utiliza. Para consultas especializadas, puede ser más conveniente escribir SQL directamente.
Los índices pueden acelerar búsquedas, ordenamientos y uniones, pero también ocupan espacio y pueden hacer más lentas las escrituras. Usa planes de ejecución y herramientas de análisis para decidir cuáles crear.
Ejemplo: si las consultas filtran con frecuencia por Correo, evalúa crear un índice sobre esa columna. Si buscan por EscuelaId y ordenan por FechaInscripcion, analiza un índice compuesto para ese patrón.
No existe una regla universal según la consulta sea por rango o puntual. Influyen el motor, la distribución de los datos y el orden de las columnas indexadas.
Ejemplo: revisa el plan de ejecución para comprobar si un índice sobre FechaInscripcion ayuda a consultar las inscripciones de un periodo.
Usa claves primarias y foráneas, restricciones CHECK, valores NOT NULL y reglas de unicidad cuando correspondan. La aplicación también puede validar los datos, pero la base de datos debe proteger las reglas fundamentales.
Ejemplo: una clave foránea de Inscripcion.EstudianteId a Estudiante.EstudianteId evita referencias a estudiantes inexistentes.
Mantén diagramas entidad-relación, definiciones de tablas, convenciones y notas sobre procedimientos, disparadores y scripts. Actualiza la documentación cuando cambie el diseño.
Ejemplo: documenta por qué EstadoInscripcion tiene ciertos valores válidos y qué proceso actualiza ese campo.
Define la frecuencia de las copias, cuánto tiempo se conservarán y cuánto tiempo de interrupción o pérdida de datos es aceptable. Prueba periódicamente la restauración: una copia que no se puede recuperar no es suficiente.
Ejemplo: una aplicación crítica puede requerir respaldos automáticos, replicación y conmutación por error, además de pruebas de recuperación.
Mantener el servidor de base de datos y el servidor web en máquinas distintas puede mejorar el aislamiento, la seguridad y la gestión de recursos. En sistemas pequeños o administrados en la nube, otras arquitecturas pueden ser más adecuadas.
Ejemplo: una aplicación de alto tráfico puede alojar la base de datos en una instancia privada, accesible solo desde los servicios autorizados.
Las imágenes y otros archivos grandes pueden almacenarse en un sistema de archivos o en un servicio de almacenamiento de objetos, guardando en la base de datos solo una referencia. Si deben residir en la base, considera colocarlos en una tabla separada para evitar cargarlos en consultas frecuentes.
Ejemplo: guarda FotoPerfilUrl en Estudiante y almacena el archivo en un servicio de almacenamiento.
La normalización ayuda a mantener la coherencia. Si una consulta importante requiere muchas uniones y el rendimiento lo justifica, considera una desnormalización controlada, documentada y mantenida correctamente.
Ejemplo: conserva los datos de Escuela en una tabla propia. Si un informe muy usado necesita datos resumidos, puedes crear una copia que se actualice mediante un proceso definido.
Identifica las entidades, relaciones, reglas y consultas principales antes de construir el esquema. Revisa el diseño con ejemplos de datos y escenarios de cambio.
Ejemplo: antes de crear Inscripcion, define si un estudiante puede inscribirse varias veces en el mismo curso y qué regla debe impedir duplicados
Leer un valor de un XML con indexOf y substring, o fabricar el documento sumando cadenas, parece más rápido que usar un analizador. Funciona con el primer ejemplo que pruebas y falla con el tercero. Lo comprobamos con seis casos, todos XML válidos.
También te puede interesar
Esta es la versión que suele aparecer, con el error que trae casi siempre:
String extraer(String xml) {
int inicio = xml.indexOf("<nombre>");
int fin = xml.indexOf("</nombre>");
return xml.substring(inicio, fin);
}Devuelve <nombre>Ana: indexOf da la posición donde empieza la etiqueta, y hay que saltársela. Corregido eso, la probamos con seis documentos que cualquier analizador lee sin problema:
String[] casos = {
"<persona><nombre>Ana</nombre></persona>",
"<persona><nombre>Gómez & Hijos</nombre></persona>",
"<persona><nombre><![CDATA[<b>Ana</b>]]></nombre></persona>",
"<persona><nombre >Ana</nombre></persona>",
"<persona>\n <!-- <nombre>viejo</nombre> -->\n <nombre>Ana</nombre>\n</persona>",
"<p:persona xmlns:p=\"urn:x\"><p:nombre>Ana</p:nombre></p:persona>",
};java AMano.javaoriginal: [<nombre>Ana] [Ana] [Gómez & Hijos] [<![CDATA[<b>Ana</b>]]>] [a><nombre >Ana] [viejo] excepción: Range [7, -1) out of bounds for length 63
| Caso | Resultado a mano | Qué pasó |
|---|---|---|
| Simple | Ana | Bien |
| Con una entidad | Gómez & Hijos | No traduce & a & |
| Con CDATA | <![CDATA[…]]> | Devuelve el envoltorio en vez del contenido |
| Con un espacio en la etiqueta | a><nombre >Ana | <nombre > es válido, pero indexOf no lo encuentra y devuelve basura |
| Con un comentario delante | viejo | Lee el nombre que estaba comentado |
| Con espacio de nombres | excepción | <p:nombre> no coincide y el programa se cae |
Cinco de seis mal, y lo peor es que solo uno avisa. Los demás devuelven un valor equivocado sin ningún error.
El JDK trae uno, así que no hace falta añadir ninguna dependencia:
String nombre(String xml) throws Exception {
var f = DocumentBuilderFactory.newInstance();
f.setNamespaceAware(true);
var doc = f.newDocumentBuilder().parse(new InputSource(new StringReader(xml)));
var nodos = doc.getElementsByTagNameNS("*", "nombre");
return nodos.getLength() == 0 ? null : nodos.item(0).getTextContent();
}java ConParser.java[Ana] [Gómez & Hijos] [<b>Ana</b>] [Ana] [Ana] [Ana]
Los seis bien. El analizador traduce las entidades, abre el CDATA, ignora los comentarios y entiende los espacios de nombres (por eso se activa setNamespaceAware y se busca con "*").
Construir el documento concatenando tiene el problema contrario: hay caracteres que en XML tienen significado y deben escaparse.
String aMano(String nombre, String atributo) {
return "<root>"
+ "<nombre atr=\"" + atributo + "\">"
+ nombre
+ "</nombre>"
+ "</root>";
}Con nombre = "Gómez & Hijos <S.A.>" y un atributo con comillas, el resultado ni siquiera es XML:
salida<root><nombre atr="dijo "hola"">Gómez & Hijos <S.A.></nombre></root> [Fatal Error] :1:26: Element type "nombre" must be followed by either attribute specifications, ">" or "/>". al leerlo: Element type "nombre" must be followed by either attribute specifications, ">" or "/>".
La línea [Fatal Error] la escribe el analizador por su cuenta en la salida de errores, antes de lanzar la excepción. Este es el error que verá quien reciba el archivo.
var doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
var root = doc.createElement("root");
var n = doc.createElement("nombre");
n.setAttribute("atr", atributo);
n.setTextContent(nombre);
root.appendChild(n);
doc.appendChild(root);
var t = TransformerFactory.newInstance().newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
var out = new StringWriter();
t.transform(new DOMSource(doc), new StreamResult(out));salida<root> <nombre atr="dijo "hola"">Gómez & Hijos <S.A.></nombre> </root> de vuelta: [Gómez & Hijos <S.A.>] atr=[dijo "hola"]
El &, los < > y las comillas se escapan solos. Al volver a leerlo se recupera exactamente lo que se escribió, y eso es lo que hay que exigir a cualquier forma de generar XML.
Entrada publicada en 2011 y revisada en octubre de 2026. Todo el código se compiló y se ejecutó con Java 25 LTS en un contenedor limpio; las salidas están copiadas de esa ejecución.