2009-07-20 10 views
1

Compte tenu de la structure suivante:Question de conception/modélisation de base de données - Contraintes ou pas de contraintes?

City 
Area 
User 

Chaque zone a 1 et seulement 1 Ville.
Chaque utilisateur a au moins un, mais peut-être plusieurs zones.
Chaque utilisateur a 1 et seulement 1 ville.

Quelle est la manière la plus élégante de modéliser cela?

Actuellement, j'ai:

User, 
UserArea, 
Area, 
City 

Où est UserArea 1: relation M w/utilisateur, et la région est de 1: 1 avec la ville.

Le problème est le suivant:

Un utilisateur peut avoir 3 ou 4 zones sous le modèle actuel, mais deux des zones pourraient être dans la ville de « 1 » et les 2 autres zones pourraient être dans la ville « 2 » . Ceci est une violation des règles commerciales. Dois-je simplement mettre une contrainte pour empêcher ce genre de chose, ou est-ce une meilleure approche pour normaliser davantage ce type de paradoxe? Si oui, comment modélise-t-on ce système de telle sorte que:

1 Utilisateur = 1 Ville;
1 Zone = 1 Ville;
1 Utilisateur = M Zones;

Merci pour vos idées.

+0

un utilisateur peut avoir une ville, mais pas de zones? –

Répondre

0

Cette réponse m'a été fournie par SQLServerCentral, et elle fait exactement ce que je cherchais. Il y a une redondance (comme l'a souligné rexum dans ce forum), mais il n'y a aucune possibilité d'anomolies.

Je suis très intéressé par vos commentaires et suggestions.


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]) 
+0

Cela fonctionne pour les règles de votre entreprise, mais je ne l'ai toujours pas construit de cette façon pour changer les zones de soutien qui pourraient s'étendre sur les villes/municipalités/districts/etc. Il y a beaucoup moins de maintenance impliquée dans la construction pour supporter plusieurs villes et la gestion de sproc que d'avoir à stocker de manière redondante des données dans de nombreux endroits. –

1

Je voudrais avoir une table pour chacun des utilisateurs, zones et villes, puis avoir une quatrième table avec Columns User (FK), Cities (FK) et Areas (FK) où les utilisateurs & Cities (en combinaison) est contraint d'être Unique. Ensuite, chaque fois qu'une combinaison User-Area est insérée, elle n'autorise pas une ville non unique.

+0

La ville ne peut pas être unique dans cette quatrième table, car un utilisateur aura plusieurs zones, chacune avec la même ville. Ainsi, vous devez avoir un ID utilisateur, un ID de ville, un ID de zone et une ID de ville pour chaque zone de cette ville. – Scott

+0

@ Scott, Merci, fixé cela. –

0

La seule chose que je peux penser est désinvolture:

Donnez la table Surface composite clé alternative de CityID et Areaid. Rendre AreaID primaire (de sorte qu'il ne peut avoir qu'une seule ville).

Utilisez cette clé alternative (AK1) pour former une relation FK entre Area et UserArea.

Donnez à la table utilisateur une clé alternative composite de UserID et CityID. Rend l'ID utilisateur primaire.

Utilisez cette clé alternative (AK2) pour former une relation FK entre Utilisateur et UserArea.

Ainsi, votre table UserArea ressemblera à ceci:

UserID CityID Areaid

La clé étrangère basée sur AK2 vous forcer à choisir une ville qui correspond à la ville natale de l'utilisateur et le AK1- La clé étrangère basée vous forcera à choisir une zone qui appartient à cette ville. En substance, les clés étrangères AK1 et AK2 se chevaucheront, forçant ce que vous voulez.

0

Je pense que votre approche "User, UserArea, Area, City" est correcte. Faites confiance aux contraintes et à la logique métier pour éviter les violations.

0

Pouvez-vous fournir plus de détails sur ce qu'est une zone? Permettez-moi de formuler mes hypothèses:
L'utilisateur habite dans une ville.
Chaque ville a des zones.
Une zone peut tomber dans une seule ville.
Un utilisateur peut vivre dans une seule ville
Compte tenu de ces conditions, vous semblez avoir les dépendances fonctionnelles suivantes dans vos spécifications de conception:
Area -> Ville
Utilisateur -> Ville
Votre modèle d'affaires suggère que l'utilisateur peut avoir plusieurs adresses dans la même ville mais ne peut pas avoir d'adresse dans deux villes différentes. Est-ce une contrainte de conception réaliste? Si je peux avoir plusieurs adresses, pourquoi pas dans différentes villes?
Si vous souhaitez stocker toutes les zones d'un utilisateur donné, vous avez besoin d'une troisième table (comme vous l'avez suggéré). La table ressemblerait à
UserArea (userID, AreaID). Vous devez implémenter la logique métier à l'aide d'un déclencheur ou d'une procédure stockée.

+0

Exemple: Un utilisateur ne peut être qu'à New York. Queens, Brooklyn, Manhattan, Long Island et Harlam sont toutes des zones, dont chacune appartient exclusivement à "New York City". Un utilisateur peut avoir plusieurs zones, mais seulement dans une ville. Un utilisateur peut appartenir à "Queens, Brooklyn et Manhattan" ou "Harlam, Bronx, Brooklyn", etc. Un utilisateur ne peut pas appartenir à "Queens, Harlam et Newark" parce que la zone "Newark" est dans la ville "New Jersey" et non "New York". Un utilisateur peut appartenir à une ou plusieurs zones, mais chacune de ces zones doit appartenir à la même ville. – Scott

0

USER_AREAS ne nécessite que les colonnes suivantes:

  • USER_ID (pk, fk pour USERS.USER_ID)
  • AREA_ID (pk, fk pour AREA.AREA_ID)

Une zone est associée à une ville la table AREAS; vous savez quelles villes sont associées à l'utilisateur en roulant de la table ZONES:

AREA

  • AREA_ID (pk)
  • CITY-ID (fk pour CITY.CITY_ID)

Mettre CITY_ID en la table USER_AREAS est redondante. Deuxièmement, placer CITY_ID dans la table USER_AREAS ne garantit pas que le AREA_ID dans cet enregistrement est réellement associé au CITY_ID dans la table AREA. Une contrainte CHECK impose uniquement l'intégrité du domaine en limitant les valeurs acceptées par une colonne et ne peut pas référencer les colonnes dans d'autres tables doit moins une fonction définie par l'utilisateur.

Vous ne pouvez pas appliquer la règle métier des zones d'un utilisateur appartenant uniquement à une seule ville dans la base de données. Cela devrait être fait au niveau de l'application (quel que soit le sproc qui gère l'insertion/la mise à jour de la table USER_AREAS).

+0

Rexum, vous avez raison de mettre redondant cityID dans le User_Areas, et c'est le compromis ici. Soit vous avez une redondance qui est gérée via des contraintes, soit vous avez une normalisation incomplète qui permet l'existence d'anomolies de mise à jour/suppression. Vous avez tort quand "placer City_Id dans la table User_Areas ne garantit pas que l'Area_Id dans cet enregistrement est réellement associé à CityID dans la table Area ..." Le code sql ajouté ci-dessous fait exactement cela via la clé étrangère. relations sur plusieurs colonnes. S'il vous plaît voir la réponse postée ci-dessous. Je serais intéressé par vos commentaires. – Scott

+0

La redondance est une normalisation incomplète - Vous savez à quelle ville un utilisateur est associé par l'association de zone. Vous stockez désormais ces informations de manière redondante dans chaque enregistrement d'utilisateur, et de nouveau dans chaque enregistrement UserAreas. Sans le FK ref à deux tables (ne savait pas que cela pouvait être fait), il n'y avait aucun moyen de garantir que la ville-id était effectivement associée à l'ID de zone. –

0

Je ne suis pas sûr de ce que vous entendez par "zones".

Je crois que la division urbaine est la suivante:

La planète a des pays. Un pays a des régions (états, provinces, etc.) Les régions ont des zones (villes, villages, etc.) Les zones (si elles sont assez grandes) peuvent avoir des districts.

utilisateur => Pays + Région/+ Ville (+ District)

Pourriez-vous s'il vous plaît donner des détails sur les zones?

+0

cerveau, s'il vous plaît voir mon commentaire deux messages qui donne un exemple de ce dont je parle. – Scott

Questions connexes