我想设计一些类似于动态表单的东西,管理员可以在其中定义每个表单字段。 我设计了 3 个表:用于共享属性的 mainform 表,然后是将 mainformID 作为外键的 formfield 表,并定义了每个表单字段 例如:
AutoID | FormID | FieldName
_____________________________
100 | Form1 | weight
101 | Form1 | height
102 | Form1 | color
103 | Form2 | Size
104 | Form2 | Type
....
至少有一个像下面这样的表单值表:
FormFieldID | Value | UniqueResponseID
___________________________________________
100 | 50px | 200
101 | 60px | 200
102 | Red | 200
100 | 30px | 201
101 | 20px | 201
102 | Black | 201
103 | 20x10 | 201
104 | Y | 201
....
对于每个表单,我必须加入这 3 个表以捕获所有字段和值。我想知道它是否是设计这种场景的唯一方法?它会降低sql性能吗?或者有什么更快更好的方法吗?
最佳答案
这是 EAV 的一种形式,我假设您绝对必须这样做,而不是“静态”设计。
does it decrease sql performance?
是的,获取一堆行(在 EAV 下)总是比只获取一行(在静态设计下)慢。
or is there any fast and better way?
不是从逻辑的角度来看,而是可以在物理层面进行重大优化(至少对于查询性能而言)。具体来说,您可以仔细设计 key 以最大程度地减少 I/O(通过将相关数据放在一起),甚至消除 JOIN 本身。
例如:
此模型通过 FOREIGN KEY 层次结构一直向下迁移到 ATTRIBUTE_VALUE 表。 ATTRIBUTE_VALUE 表中生成的自然组合键使我们能够:
通过单个索引范围扫描 + 表堆访问获取给定形式的所有属性1 em> 在 ATTRIBUTE_VALUE 表上,并且根本不执行任何 JOIN。除此之外,您还可以 cluster 2 它消除了表堆访问,只剩下索引范围扫描3。
如果您只需要获取特定响应的数据,请更改复合键中字段的顺序,使 RESPONSE_ID 位于前沿。
如果您需要同时“按表单”和“按响应”查询,您将需要两个索引,在这一点上,我建议二级索引也为 cover 4 VALUE 字段。
例如:
-- Since we haven't used NONCLUSTERED clause, this is a B-tree
-- that covers all fields. Table heap doesn't exist.
CREATE TABLE ATTRIBUTE_VALUE (
FORM_ID INT,
ATTRIBUTE_NAME VARCHAR(50),
RESPONSE_ID INT,
VALUE VARCHAR(50),
PRIMARY KEY (FORM_ID, ATTRIBUTE_NAME, RESPONSE_ID)
-- FOREIGN KEYs omitted for brevity.
);
-- We have included VALUE, so this B-tree covers all fields as well.
CREATE UNIQUE INDEX ATTRIBUTE_VALUE_IE1 ON
ATTRIBUTE_VALUE (RESPONSE_ID, FORM_ID, ATTRIBUTE_NAME)
INCLUDE (VALUE);
1 或特定属性,或特定属性的特定响应。
2 除非您指定 NONCLUSTERED 子句,否则 MS SQL Server 默认对所有表进行集群。
3 对集群的友好性和 JOIN 的消除是自然键(相对于代理键)的一些主要优势。但它们也使表“更胖”并且不会与 ON UPDATE CASCADE 隔离。我相信在这种特殊情况下利大于弊。有关自然键与代理键的更多信息,请查看 here .
4 幸运的是,MS SQL Server supports仅出于覆盖目的在索引中包含字段(而不是实际搜索索引)。这使得索引比相同字段上的“正常”索引更精简。
关于asp.net - 分析场景性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20934239/