Pregunta de diseño/modelado de la base de datos: ¿restricciones o sin restricciones?
-
18-09-2019 - |
Pregunta
Dada la siguiente estructura:
City
Area
User
Cada área tiene 1 y solo 1 ciudad.
Cada usuario tiene al menos una pero posiblemente múltiples áreas.
Cada usuario tiene 1 y solo 1 ciudad.
¿Cuál es la forma más elegante de modelar esto?
Actualmente, tengo:
User,
UserArea,
Area,
City
Donde el Userea es una relación de 1: M con usuario, y el área es 1: 1 con la ciudad.
El problema es este:
Un usuario puede tener 3 o 4 áreas bajo el modelo actual, pero 2 de las áreas podrían estar en la ciudad "1" y las otras 2 áreas podrían estar en la ciudad "2". Esta es una violación de las reglas comerciales.
¿Debería poner una restricción para evitar este tipo de cosas, o es un mejor enfoque para normalizar aún más para que este tipo de paradoja no sea posible? Si es así, ¿cómo se modela este sistema para que:
1 usuario = 1 ciudad;
1 área = 1 ciudad;
1 usuario = m áreas;
Gracias por tus ideas.
Solución 7
Esta respuesta me fue proporcionada desde SQLServerCentral, y hace exactamente lo que estaba buscando. Hay una redundancia (como señaló Rexum en este foro), pero no hay posibilidad de anomolías.
Estoy muy interesado en sus comentarios y sugerencias.
CREATE TABLE [dbo].[Cities](
[CityID] [int] IDENTITY(1,1) NOT NULL,
[CityName] [varchar](50) NOT NULL,
CONSTRAINT [PK_Cities] PRIMARY KEY CLUSTERED
(
[CityID] ASC
)
)
CREATE TABLE [dbo].[Users](
[UserID] [int] IDENTITY(1,1) NOT NULL,
[UserName] [varchar](50) NOT NULL,
[CityID] [int] NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[UserID] ASC
)
)
ALTER TABLE [dbo].[Users] WITH CHECK ADD CONSTRAINT [FK_Users_Cities] FOREIGN KEY([CityID])
REFERENCES [dbo].[Cities] ([CityID])
GO
ALTER TABLE [dbo].[Users] CHECK CONSTRAINT [FK_Users_Cities]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_UsersCity] ON [dbo].[Users]
(
[UserID] ASC,
[CityID] ASC
)
CREATE TABLE [dbo].[Areas](
[AreaID] [int] IDENTITY(1,1) NOT NULL,
[AreaName] [varchar](50) NOT NULL,
[CityID] [int] NOT NULL,
CONSTRAINT [PK_Areas] PRIMARY KEY CLUSTERED
(
[AreaID] ASC
))
GO
ALTER TABLE [dbo].[Areas] WITH CHECK ADD CONSTRAINT [FK_Areas_Cities] FOREIGN KEY([CityID])
REFERENCES [dbo].[Cities] ([CityID])
GO
ALTER TABLE [dbo].[Areas] CHECK CONSTRAINT [FK_Areas_Cities]
GO
CREATE UNIQUE NONCLUSTERED INDEX [IX_AreasCity] ON [dbo].[Areas]
(
[AreaID] ASC,
[CityID] ASC
)
GO
CREATE TABLE [dbo].[UserCityArea](
[UserID] [int] NOT NULL,
[CityID] [int] NOT NULL,
[AreaID] [int] NOT NULL,
CONSTRAINT [PK_UserCityArea] PRIMARY KEY CLUSTERED
(
[UserID] ASC,
[CityID] ASC,
[AreaID] ASC
)
)
GO
ALTER TABLE [dbo].[UserCityArea] WITH CHECK ADD FOREIGN KEY([UserID], [CityID])
REFERENCES [dbo].[Users] ([UserID], [CityID])
GO
ALTER TABLE [dbo].[UserCityArea] WITH CHECK ADD FOREIGN KEY([AreaID], [CityID])
REFERENCES [dbo].[Areas] ([AreaID], [CityID])
Otros consejos
Tendría una tabla cada uno para usuarios, áreas y ciudades, luego tendría una cuarta tabla con el usuario de columnas (FK), las ciudades (FK) y las áreas (FK) donde los usuarios y las ciudades (en combinación) se limitan a ser únicos. Luego, cada vez que se inserta una combinación de área de usuario, no permitirá una ciudad no unique.
Lo único que se me ocurre es:
Dé a la tabla de área una llave alternativa compuesta de Cityid y AreaID. Haga que AREAID sea primaria (para que solo pueda tener una ciudad).
Use esta clave alternativa (AK1) para formar una relación FK entre el área y el userea.
Ofrezca a la tabla de usuario una clave alternativa compuesta de UserID y CityID. Haga que UserID sea primario.
Use esta clave alternativa (AK2) para formar una relación FK entre el usuario y el usuario de usuario.
Entonces su tabla de usuarios se verá así:
UserID Cityid AreaId
El extranjero con sede en AK2 lo obligará a elegir una ciudad que coincida con la ciudad natal del usuario, y la clave externa con sede en AK1 lo obligará a elegir un área que pertenece a esa ciudad. En esencia, las claves extranjeras AK1 y AK2 se superpondrán, forzando lo que quieres.
Creo que su enfoque de "usuario, usuario, área, ciudad" es correcto. Confíe en las limitaciones y la lógica comercial para evitar violaciones.
¿Puede proporcionar más detalles sobre qué es un área? Déjame decir mis suposiciones:
El usuario vive en una ciudad.
Cada ciudad tiene áreas.
Un área puede caer en una sola ciudad.
Un usuario puede vivir en una sola ciudad
Dadas estas condiciones, parece tener las siguientes dependencias funcionales en su especificación de diseño:
Área -> Ciudad
Usuario -> Ciudad
Su modelo de negocio sugiere que el usuario puede tener múltiples direcciones dentro de la misma ciudad, pero no puede tener una dirección en dos ciudades diferentes. ¿Es esta una restricción de diseño realista? Si puedo tener múltiples direcciones, ¿por qué no en diferentes ciudades?
Si desea almacenar todas las áreas de un usuario determinado, necesita una tercera tabla (como ha sugerido). La mesa se vería como
UserAea (UserId, AreaId). Debe implementar la lógica comercial utilizando un disparador o un procedimiento almacenado.
USER_AREAS
Solo requiere las siguientes columnas:
USER_ID
(PK, FK paraUSERS.USER_ID
)AREA_ID
(PK, FK paraAREA.AREA_ID
)
Un área está asociada a una ciudad en la tabla de áreas; Sabes qué ciudades están asociadas con el usuario al rodar de la tabla de áreas:
AREA
AREA_ID
(paquete)CITY-ID
(FK paraCITY.CITY_ID
)
Poniendo CITY_ID
en el USER_AREAS
La mesa es redundante. En segundo lugar, colocando CITY_ID
en el USER_AREAS
La tabla no garantiza que el AREA_ID
en ese registro se asocia en realidad con el CITY_ID
en la mesa del área. Una restricción de verificación solo impone la integridad del dominio al limitar los valores que aceptan una columna, y no pueden referencia a las columnas en otras tablas deben menos una función definida por el usuario.
No puede hacer cumplir la regla comercial de las áreas de un usuario que solo pertenecen a una sola ciudad en la base de datos. Tendría que hacerse a nivel de aplicación (lo que SPROC administre la inserción/actualización de la USER_AREAS
mesa).
No estoy seguro de a qué te refieres con "áreas".
Creo que la división urbana es la siguiente:
El planeta tiene países. Un país tiene regiones (estados, provincias, etc.) las regiones tienen áreas (ciudades, pueblos, pueblos, etc.), las áreas (si lo suficientemente grandes) pueden tener distritos.
Usuario => País + Región/Área + Ciudad ( + Distrito)
¿Podrías elaborar las áreas?