简体   繁体   中英

SQL Server Table constraints design

I have a design question for SQL server tables.
I have equipments in a table as follows:

Table Equipments
id int PK
idCommunications int FK to table Communications (defined below)
Name varchar  

Each equipment can have only one type of communications system (table Communications).

Table Communications
id int PK
Name varchar

Each communication system can have an arbitrary number of parameters. I don't want to create an specific table for each communications system I know as they evolve so I created a table that list the parameters

Table ParamCommunications
id int PK
idCommunications int FK to table Communications
Parameter varchar

and another table that contains the value of each parameter for each equipment

Table ConfComEquipment
id int PK
idParamCommunications int FK to table ParamCommunications
idEquipment int FK to table Equipment
Value varchar

This table structure allows me to create the list of parameters for each communication system and assign them to one equipment. The problem I found is that in the last table I can enter a configuration for an idParamCommunications that does not belong to the same idCommunications that the equipment gets defined.

For example. I have two communications systems

id Name
1 Serial
2 TCP/IP

and I have the following parameters in table ParamCommunications:

id idCommunications Parameter
1        1            BaudRate
2        1            COMPort
3        2            IPAddress

The table Equipment have:

id idCommunications Name
1        1           SerialEquipment
2        2           EthernetEquipment

When I do the config the table the constraints won't allow me to use any undefined equipment or undefined parameter but will allow me to do this:

id idCommunications idParamCommunications idEquipment Value
 1      1                   1                2       "somevalue"

Equipment with id=2 has an idCommunications = 2 but in this setup that is not a constraint.

I understand I have the following potential solutions:

  1. Create a composite primary key that uses the equipment and the communication to define it. This will create the problem to me that I will need to use the composite to link to some other tables (not shown here) that don't care about the communication system.
  2. Create another table: CommunicationByEquipment that will link the equipment to its communications system and then refer to the PK of this table from the ConfComEquipment table.
  3. Control the data integrity in the program side. Probably more prone to error and orphan records

Is there another option I don't see? If not, which one would you recommend?

Thanks for your help and sorry for the long post. I was going to paste figures showing the table structure but I'm not allowed until I get a higher rank.

Rusty

Primary keys aren't the only possibility for the target of a foreign key - any unique index can also be targeted by a foreign key constraint. So, I think the below structure enforces your requirements:

create table Communications (
    ID int not null,
    Name varchar(20) not null,
    constraint PK_Communications PRIMARY KEY (ID),
    constraint UQ_Communication_Names UNIQUE (Name)
)
go
create table CommunicationParameters (
    ID int not null,
    CommunicationID int not null,
    Parameter varchar(20) not null,
    constraint PK_CommunicationParameters PRIMARY KEY (ID),
    constraint UQ_CommunicationParameter_Parameters UNIQUE (Parameter),
    constraint FK_CommunicationParameters_Communications
          FOREIGN KEY (CommunicationID) references Communications (ID),

    constraint UQ_CommunicationParameters_Communication_XRef
          UNIQUE (ID,CommunicationID)
)
go
create table Equipments (
    ID int not null,
    CommunicationID int not null,
    Name varchar(20) not null,
    constraint PK_Equipments PRIMARY KEY (ID),
    constraint UQ_Equipment_Names UNIQUE (Name),
    constraint FK_Equipments_Communications
          FOREIGN KEY (CommunicationID) references Communications (ID),

    constraint UQ_Equipment_Communication_XRef
          UNIQUE (ID,CommunicationID)
)

And finally:

create table ConfComEquipment (
    ID int not null,
    CommunicationID int not null,
    CommunicationsParameterID int not null,
    EquipmentID int not null,
    Value varchar(99) not null,
    constraint PK_ConfComEquipment PRIMARY KEY (ID),
    constraint FK_ConfComEquipment_CommunicationParameters
          FOREIGN KEY (CommunicationsParameterID) references
                CommunicationParameters (ID),
    constraint FK_ConfComEquipment_Equipment
          FOREIGN KEY (EquipmentID) references
                Equipments (ID),

    constraint FK_ConfComEquipment_CommunicationParameters_XRef
          FOREIGN KEY (CommunicationsParameterID,CommunicationID) references
                CommunicationParameters (ID,CommunicationID),
    constraint FK_ConfComEquipment_Equipment_XRef
          FOREIGN KEY (EquipmentID,CommunicationID) references
                Equipments (ID,CommunicationID)
)

Note that it is the addition of these constraints with XRef at the end of their names that allows is to enforce the constraint that the CommunicationID column in this last table agrees with the values stored in both the CommunicationParameters and Equipments tables, whilst leaving the primary keys on these tables as just the ID column.

Strictly, this now makes the "real" foreign keys, FK_ConfComEquipment_CommunicationParameters and FK_ConfComEquipment_Equipment as redundant, the XRef constraints are sufficient. It's optional whether you keep these still in your final table design.

There are no declarative constraints that will enforce the integrity rules you specified. These types of integrity rules cannot be enforced with any combination of UNIQUE constraints, FOREIGN KEY constraints or CHECK constraints.

If you need these integrity rules enforced in/by the database, the options you have are to either 1) implement triggers (to be fired when DML operations are performed) and/or 2) implement stored procedures (to be called in place of DML (INSERT/UPDATE/DELETE) operations).

And those triggers and/or stored procedures will require maintenance, as the integrity rules evolve. As you mentioned, this same logic could be implemented in the application, as another layer between the application and the database.

The crux of the problem is the "impedance mismatch" between the relational model and the Entity-Attribute-Value (EAV) model.

With the EAV model, we can't define any useful foreign keys (apart from the elementary association of attribute values to entities). And the queries are more complex, and order of magnitude more complex, if the attempt to return an entity and the attribute values as a single row, that is, to convert from the EAV model back into a form that looks like rows from a relational model.

Given the information above I think a many-to-many relationship might work between equipment and parameters if you make one initial change in the equipment table design:

  1. Since each type of equipment has only one type of comm system just add comm system to the equipment table (by doing this your parameters are now properties of the equipment instead of the comm system)
  2. (optional) Build a table defining various comm systems and use it in a drop down.
  3. (two options here)
    • a.build a table defining just the spec names (place values in many-to-many). Not recommended as the specs become more dynamic.
    • b.build aa table defining all parameters/specs
  4. build a table that connects equipment to parameters (this is a many-to-many relationship)

1.Table: Equipment
- pk
- fk_comm_sys (drop down comm sys defined)
- name

2.Table: comm_sys_defined
- pk
- name (ethernet/serial/etc.)
- etc.

3a.Table: parameters_defined
- pk
- name (eg baud rate)
note: in this scenario values are stored in the many-to-many table

3b. Table: parameters_defined
- pk
- name (name: baud rate)
- etc (value: speed, classes, range, etc.)

4a.Table: equip_parameters
- pk
- fk_equip
- fk_parameters (name)
- speed (value)
Note: strictly speaking this isn't 2NF

4b. Table: equipment_parameters
- pk (consider a composite key)
- fk_equipment
- fk_parameter
Note: if you wish to avoid dups you will need a composite key here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM