可选列中的空值

时间:2012-06-15 18:44:49

标签: sql-server database-design

假设我有一个Table1,有三个列:ID(主键,标识),A和B

现在,假设我有3种方法,假设它们在列中没有共同点:

方法1:C,D,E

方法2:F,G,H,I

方法3:J

我可以制作一张桌子: ID,A,B,C,D,E,F,G,H,I,J,M

其中M是方法的名称(或方法ID)。

但是,如果使用方法3的90%的时间,则会有许多空值 这是一个问题吗?如果是这样,有没有更好的方法来设置它?

如果我将每个方法设为自己的表实体,我如何确保每个ID只有一个方法匹配呢?

如果我将它作为一个表格保存,如果M为1,我如何确保只填写C,D,E并且F到J是否为空?


好的,似乎有些人难以抽象地思考,所以我将创建一个应用上述内容的随机具体示例:

假设我有人进行练习的记录 每条记录都有一个ID(用于唯一标识事件),TIME_STARTED和TIME_ENDED。

但是,根据他们所做的练习,需要有不同的属性。假设只有三个练习:

Elliptical:INCLINE,LEVEL,SPEED

Crunches:User_Weight,Reps,Delay,Extra_Weight

Dead Lift:Weight_Lifted

对于每个ID,只能有一个“方法”。应用此方法,请参阅上述问题。

2 个答案:

答案 0 :(得分:3)

听起来你有supertype/subtype的情况。在这种情况下,Table1保存您的超类型,您可能希望创建一个不同的表来保存每个子类型。这些子类型表上的PK也是超类型表的FK。所以你会有这样的事情:

Supertype_table
|    ID(PK)   |  A  |  B  |

Subtype1_table
|  ID(PK&FK)  |  C  |  D  |  E  |

Subtype2_table
|  ID(PK&FK)  |  F  |  G  |  H  |  I  |

Subtype3_table
|  ID(PK&FK)  |  J  |

这个模式的要点是确保你没有一堆主要是空的行。对于每个方法,您必须编写一个单独的查询来插入/更新相应的表。使用SQL Server,您可以创建一个视图,将所有这些表和摘要组合在一起:

CREATE VIEW MyView
SELECT Super.ID, Super.A, Super.B, 
Sub1.C, Sub1.D, Sub1.E, 
Sub2.F, Sub2.G, Sub2.H, Sub2.I, 
Sub3.J
FROM Supertype_table as Super
LEFT OUTER JOIN Subtype1_table as Sub1 on Super.ID = Sub1.ID
LEFT OUTER JOIN Subtype2_table as Sub2 on Super.ID = Sub2.ID
LEFT OUTER JOIN Subtype3_table as Sub3 on Super.ID = Sub3.ID

那么你可以写下这样的东西:

SELECT ID, A, B, J
FROM MyView
WHERE J is not null

编辑:OP的评论

为了确保每个ID都在一个且只有一个表中,您需要在超类型表上使用某种标识符。因此,如果您有一个名为TypeID的列,则可以创建函数:

CREATE FUNCTION [dbo].[SubtypeofSuperID](@ID int)
RETURNS int
AS
BEGIN
  DECLARE @TypeID int;

    SELECT @TypeID = TypeID 
    FROM Supertype_table
    WHERE ID = @ID

  RETURN @TypeID 
END

然后,使用它,您可以创建每个子类型表的检查:

ALTER TABLE Subtype1_table CONSTRAINT [CK_CorrectSubtype1]
CHECK ( [dbo].[SubtypeofSuperID]([ID]) = 1 )
ALTER TABLE Subtype2_table CONSTRAINT [CK_CorrectSubtype2]
CHECK ( [dbo].[SubtypeofSuperID]([ID]) = 2 )
ALTER TABLE Subtype3_table CONSTRAINT [CK_CorrectSubtype3]
CHECK ( [dbo].[SubtypeofSuperID]([ID]) = 3 )

答案 1 :(得分:1)

有两种方式......

首先,Jason的回答击中其中一个 - 将您的设计分成多个表 - 这对于大多数事情来说都是一个非常好的方法。 (a.k.a.更“标准化”的方法)

...替代

1)您可以在最后一个字段中使用一串名称 - 值对。例如:

ID   Method   DateTimeStart   DateTimeEnd   ValueString
1    1        2012-01-01...   2012-01-01... C:value,D:value,E:value
2    2        2012-01-01...   2012-01-01... F:value,G:value,H:value,I:value

因此,如果您无法提前计划价值类型,这可能会非常方便。例如,您可能需要能够即时决定开始记录方法1的“W”值,并且在更标准化的设计中进行结构化模拟是不可行的。

这是我们的电子签名表格开发人员使用的常用方法。您可以想象一个Web表单,例如“部门请假”,有人会填写并以电子方式路由以获得批准。然后是一个客户希望他们建立一个“采购订单”的网络表单,这将有不同的领域和必须记录的值。它不是为此创建一个全新的表(或向现有表添加列等),而是使用名称 - 值对模型在同一个表中存储所有表单数据,无论它是哪种形式。 / p>

2)如果您不信任在单个大表上运行插入的人员/进程,则可以使用表触发器来强制执行完整性。例如,更新前触发器可以检查方法编号,然后通过使任何不适当的数据值无效来修改人员或进程尝试插入的数据。

FWIW