Sql server 可选列中的空值

Sql server 可选列中的空值,sql-server,database-design,Sql Server,Database Design,假设我有一个表1,有三列:ID主键、Identity、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 但是,如果90%的时间使用方法3,则会有许多空值。 这是个问题吗?如果是这样,有没有更好的方法来设置 如果我让每个方法都有自己的表实体,那么如何确保每个ID都有一个与之匹配的方法 如果我把它作为一个表,我如何确保只有C,D

假设我有一个表1,有三列:ID主键、Identity、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

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

如果我让每个方法都有自己的表实体,那么如何确保每个ID都有一个与之匹配的方法

如果我把它作为一个表,我如何确保只有C,D,E被填写,如果M是1,F到J是空的

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

假设我有人们练习的记录。 每个记录都有一个ID来唯一标识事件、开始时间和结束时间

然而,根据他们做了哪些练习,将需要不同的属性。假设只有三个练习:

椭圆形:倾斜、水平、速度

紧缩:用户重量、重复次数、延迟、额外重量

恒载提升:提升的重量

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

听起来您在这里遇到了一种情况。在本例中,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 )
听起来你在这里好像遇到了麻烦。在本例中,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 )
有两种方法

首先,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表单,例如部门请假请求,由某人填写并以电子方式发送以供批准。然后出现了一个客户,他希望他们构建一个采购订单web表单,该表单将有不同的字段和值需要记录。它们使用名称-值对模型将所有表单数据(无论是哪种表单)存储在同一个表中,而不是为此创建一个全新的表或向现有表中添加列等

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

FWIW

有几种方法

首先,Jason的答案击中了其中一个——将您的设计分解成多个表——这可能是一个非常好的方法 大多数事情都是h。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表单,例如部门请假请求,由某人填写并以电子方式发送以供批准。然后出现了一个客户,他希望他们构建一个采购订单web表单,该表单将有不同的字段和值需要记录。它们使用名称-值对模型将所有表单数据(无论是哪种表单)存储在同一个表中,而不是为此创建一个全新的表或向现有表中添加列等

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



FWIW

你所说的方法是什么意思?如果您的表只有ID,A,B,那么c,d,e,f,g,h,i,j,m来自哪里?您可能问错了问题。最好从我需要存储X数据开始,什么是最佳模式?目前很难提供帮助,因为我们没有上下文,也不知道ABCDEFGHIJM是什么,但听起来你走的方向非常错误。假设,我记录了一次测试运行,ID、a和B始终是每次运行的一部分。但是,如果使用方法1运行,我需要将值存储在C、D和E列中。这可能与使用F、G、H、I等列运行方法2时的数据类型不同。@M这是为了泛型,我认为这是一件基本的事情。对不起,如果你觉得它太抽象了。抽象和模糊不是一回事。无论哪种方式,最大的问题是,在存储数据之后,您想对数据做什么?您所说的方法是什么意思?如果您的表只有ID,A,B,那么c,d,e,f,g,h,i,j,m来自哪里?您可能问错了问题。最好从我需要存储X数据开始,什么是最佳模式?目前很难提供帮助,因为我们没有上下文,也不知道ABCDEFGHIJM是什么,但听起来你走的方向非常错误。假设,我记录了一次测试运行,ID、a和B始终是每次运行的一部分。但是,如果使用方法1运行,我需要将值存储在C、D和E列中。这可能与使用F、G、H、I等列运行方法2时的数据类型不同。@M这是为了泛型,我认为这是一件基本的事情。对不起,如果你觉得它太抽象了。抽象和模糊不是一回事。不管是哪种方式,最大的问题是,在存储数据之后,您想对数据做什么?我最初是这样想的,但我有一个问题。可以让多个子类型表都指向同一超类型行。如何确保只有一个超类型行应用于一个子类型表和该表中的行。如果我想要不相交的子类型,我希望使用超类型中的列作为子类型鉴别器。对插入的强制执行是否基于我端的逻辑检查?或者DB是否可以设置为自动禁止此类插入?您必须以这种或那种方式进行显式检查。我使用函数+检查的方法,它适合我。我还要确保在客户端代码中进行检查,这样如果用户尝试执行不应该执行的操作,就会收到一条很好的错误消息。对我来说,数据库检查是最后一道防线。你为什么需要这个功能?你可以用Dri来做,我最初是这样想的,但我有一个问题。可以让多个子类型表都指向同一超类型行。如何确保只有一个超类型行应用于一个子类型表和该表中的行。如果我想要不相交的子类型,我希望使用超类型中的列作为子类型鉴别器。对插入的强制执行是否基于我端的逻辑检查?或者DB是否可以设置为自动禁止此类插入?您必须以这种或那种方式进行显式检查。我使用函数+检查的方法,它适合我。我还要确保在客户端代码中进行检查,这样如果用户尝试执行不应该执行的操作,就会收到一条很好的错误消息。对我来说,数据库检查是最后一道防线。你为什么需要这个功能?你可以用DRI来做