Dentro de una aplicación web en la que estoy trabajando, todas las operaciones de la base de datos se abstraen utilizando algunos repositorios genéricos definidos sobre Entity Framework ORM.
Sin embargo, para tener un diseño simple para los repositorios genéricos, todas las tablas involucradas deben definir un número entero único ( Int32
en C #, int
en SQL). Hasta ahora, este siempre ha sido el PK de la tabla y también el IDENTITY
.
Las claves foráneas se usan mucho y hacen referencia a estas columnas enteras. Son necesarios tanto para la coherencia como para generar propiedades de navegación mediante el ORM.
La capa de aplicación generalmente realiza las siguientes operaciones:
- carga de datos inicial de la tabla (*) -
SELECT * FROM table
- actualización -
UPDATE table SET Col1 = Val1 WHERE Id = IdVal
- eliminar -
DELETE FROM table WHERE Id = IdVal
- Insertar -
INSERT INTO table (cols) VALUES (...)
Operaciones menos frecuentes:
- Inserción masiva :
BULK INSERT ... into table
seguida (*) de toda la carga de datos (para recuperar identificadores generados) - Eliminación masiva : esta es una operación de eliminación normal, pero "voluminosa" desde la perspectiva de ORM:
DELETE FROM table where OtherThanIdCol = SomeValue
- Actualización masiva : esta es una operación de actualización normal, pero "voluminosa" desde la perspectiva de ORM:
UPDATE table SET SomeCol = SomeVal WHERE OtherThanIdCol = OtherValue
* todas las tablas pequeñas se almacenan en caché a nivel de aplicación y casi todas SELECTs
no llegarán a la base de datos. Un patrón típico es la carga inicial y muchos INSERT
s, UPDATE
sy DELETE
s.
Según el uso actual de la aplicación, hay muy pocas posibilidades de alcanzar los 100 millones de registros en cualquiera de las tablas.
Pregunta: Desde la perspectiva de un DBA, ¿existen problemas importantes con los que pueda tener esta limitación de diseño de tabla?
[EDITAR]
Después de leer las respuestas (gracias por los excelentes comentarios) y los artículos de referencia, siento que tengo que agregar más detalles:
Datos específicos de la aplicación actual : no mencioné la aplicación web actual, porque quiero entender si el modelo también se puede reutilizar para otras aplicaciones. Sin embargo, mi caso particular es una aplicación que extrae muchos metadatos de un DWH. Los datos de origen son bastante desordenados (desnormalizados de una manera extraña, con algunas inconsistencias, sin un identificador natural en muchos casos, etc.) y mi aplicación está generando entidades separadas y claras. Además,
IDENTITY
se muestran muchos de los identificadores generados ( ), para que el usuario pueda usarlos como claves comerciales. Esto, además de una refactorización masiva de código, excluye el uso de GUID ."No deberían ser la única forma de identificar de forma única una fila" (Aaron Bertrand ♦), es un muy buen consejo. Todas mis tablas también definen una RESTRICCIÓN ÚNICA para garantizar que no se permitan duplicados comerciales.
Diseño dirigido por la aplicación front-end versus diseño dirigido por la base de datos : la elección del diseño es causada por estos factores
Limitaciones de Entity Framework : se permiten PK de varias columnas, pero sus valores no se pueden actualizar
Limitaciones personalizadas : tener una sola clave entera simplifica enormemente las estructuras de datos y el código que no es SQL. Por ejemplo: todas las listas de valores tienen una clave entera y valores mostrados. Más importante aún, garantiza que cualquier tabla marcada para el almacenamiento en caché podrá colocarse en un
Unique int key -> value
mapa.
Consultas de selección complejas : esto casi nunca sucederá porque todos los datos de tablas pequeñas (<20-30K registros) se almacenan en caché a nivel de aplicación. Esto hace la vida un poco más difícil al escribir el código de la aplicación (más difícil de escribir LINQ), pero la base de datos se ve mucho mejor:
Vistas de lista : no generará
SELECT
consultas en la carga (todo está en caché) o consultas que se vean así:SELECT allcolumns FROM BigTable WHERE filter1 IN (val1, val2) AND filter2 IN (val11, val12)
Todos los demás valores requeridos se obtienen mediante búsquedas en caché (O (1)), por lo que no se generarán consultas complejas.
Editar vistas : generará
SELECT
declaraciones como esta:SELECT allcolumns FROM BigTable WHERE PKId = value1
(todos los filtros y valores son int
s)