逻辑数据库设计 - 多列属性(多列转行)

  假设有一个要开发一个试题系统,全是不定项选择题。一道题可能有2,3,4...个答案,数据应如何设计呢?本处旨在说明问题所在,例如同类问题还有存储电话,一个人可能有多个号码等等。

一、存储多值属性

  反模式:创建多个列。

  我们知道每列最好只存储一个值,因此先看如下设计:

  CREATE TABLE Question(
      QuestionId int   PK,
      QuestionBody nvarchar(500),
      Answer1  nvarchar(500),
      Answer2  nvarchar(500),
      Answer3  nvarchar(500),
      Answer4  nvarchar(500)
  )

  类似上面的设计,假设答案只有两个,那么后面的两个Answer3,Answer4就为NULL,展示表如下:

  

  这是很传统的属性设计,导致即使现在很简单的任务也变得很简单了!

  1、查询数据

  假设有一道题的答案出错了,但是你只记得答案。

SELECT * FROM Question WHERE
    Answer1 = ‘猪有4条腿‘
    OR Answer2 = ‘猪有4条腿‘
    OR Answer3 = ‘猪有4条腿‘
    OR Answer4 = ‘猪有4条腿‘

  显示结果如下:

  

  假如,另外也有一道题目,也有一个答案猪有3条腿,那么你要查得这条数据,就需要这样:

SELECT * FROM Question WHERE
    (Answer1 = ‘猪有4条腿‘ OR Answer2 = ‘猪有4条腿‘ OR Answer3 = ‘猪有4条腿‘ OR Answer4 = ‘猪有4条腿‘)
    AND
    (Answer1 = ‘猪有3条腿‘ OR Answer2 = ‘猪有3条腿‘ OR Answer3 = ‘猪有3条腿‘ OR Answer4 = ‘猪有3条腿‘)

  怎么样又长又臭吧!不怕,哥安慰下你,其实有简单点的方法:

  SELECT * FROM Question WHERE
      ‘猪有3条腿‘ IN (Answer1,Answer2,Answer3,Answer4)
      AND
      ‘猪有4条腿‘ IN (Answer1,Answer2,Answer3,Answer4)

  怎么样短了很多吧,相信你也还是不会卖帐。

  2、添加、更新以及删除值

  在以上设计中,假设我要删除答案‘猪有3条腿‘的SQL语句怎么写呢?大家被考到了吧。

  UPDATE Question
      SET Answer1 = NULLIF(Answer1,‘猪有3条腿‘),
          Answer2 = NULLIF(Answer2,‘猪有3条腿‘),
          Answer3 = NULLIF(Answer3,‘猪有3条腿‘),
          Answer4 = NULLIF(Answer4,‘猪有5条腿‘)
  WHERE QuestionId = 2

  添加一个答案,更新一个答案都有点难写, 有兴趣的朋友可以自己敲敲。

  3、确保唯一性

  如何确保同一个值不出现在多个列中,即有可能你并不想一道题中有两个答案是一样的。我们很难阻止重复的答案出现,因为Unique只能用于行。

  4、值在不断增长

  当我们有一道题有5个答案的时候,悲剧,你要更改表结构了。

  以上种种问题说明,以上设计根本不堪一击。

二、解决方案 - 从属表

  问题的根源在于:存储一个具有多个值的属性。对于以上设计,如果每道题都规定是4个答案,以上设计是可以用的。问题在于,答案个数不确定。

  因此,我们需要创建一个从属表,将不确定个数的值提取出来作为行存储,而不是列。

  总体设计如下:

CREATE TABLE Question(
    QuestionId    int    PK,
    QuestionBody Body nvarchar(500)
)

CREATE TABLE Answer(
    AnswerId    int    PK,
    AnswerBody    nvarchar(500),
    QuestionId    int,
    FOREIGN KEY(QuestionId) REFERENCES Question(QuestionId)
)

  对于以上设计,多少个答案都没问题了,而且增删查改都简单了不止一个档次。

  其实,只不过是一个一对多的关系。而且这个问题很容易一眼就看出,不过变种问题你却未必会条件反应式地提出一样从属表。

时间: 2024-08-27 04:53:28

逻辑数据库设计 - 多列属性(多列转行)的相关文章

逻辑数据库设计 - 单纯的树(递归关系数据)(转)

逻辑数据库设计 - 单纯的树(递归关系数据) 相信有过开发经验的朋友都曾碰到过这样一个需求.假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复. 这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系.这是一种典型的递归关系数据. 对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择. 一.邻接表:依赖父节点 邻接表的方案如下(仅仅说明问题): CREATE TABLE Comments( CommentId int PK, ParentId int, --

Css3之高级-3 Css多列属性(分隔列、列间隔、列规则、浏览器兼容性)

一.多列属性 分割列 - column-count 属性规定元素应该被分隔的列数 列间隔 - column-gap 属性规定列之间的间隔 列规则 - column-rule 属性规定列之间的宽度.样式和颜色 - 语法为: column-rule: width style color; 浏览器兼容性 - Internet Explorer 10 和 Opera 支持多列属性 - FireFox 需要前缀 -moz- - Chrome 和 Safari 需要前缀 -webkit- 总结:本章内容主要

逻辑数据库设计 - 可变属性(继承)

可变属性的需求:我们需要在数据库里面存储很多电器,比如电视,冰箱等等.通常,在程序中,我们的类图为: EVA设计 对于这种继承下来的可变属性时,有一种办法是创建另外一张表,将属性当成行来存储. 其中存储的数据类似下面这样: 这样的设计称为:实体-属-值,简称:EVA,或者又叫开放架构.无模式. 这种设计有如下3种好处: 1.这两张表的列都很少. 2.新增的属性不会对现有的表结构造成影响,不需要新增列. 3.避免由于空值而造成的表内容混乱. 但是这样也有如下缺点: 1.查询属性 本来,我们想要按出

逻辑数据库设计 - 需要ID(谈主键Id)

本文的目标就是要确认那些使用了主键,却混淆了主键的本质而造成的一种反模式. 一.确立主键规范 每个了解数据库设计的人都知道,主键对于一张表来说是一个很重要,甚至必需的部分.这确实是事实,主键是好的数据库设计的一部分.主键是数据库确保数据行在整张表唯一性的保障.它是定位到一条记录并且确保不会重复存储的逻辑机制.主键也同时可以被外键引用来建立表与表之间的关系. 难点是选择那一列作为主键.大多数表中的每个属性值都有可能被很多行使用.例如姓名,电子邮件地址等等都不能保证不会重复. 在这样的表中,需要引入

MySQL:列属性(列约束)

1. 是否允许为空(Null/not Null) 规定一个字段的值是否可以是null.默认是可以为空. 此时,插入值a = 10 , b 为空,发现可以正确插入: 若试图插入值b = 11,而a 不做处理,发现不能正确插入,提示 a 没有默认值. 即当对某个字段未输入信息时,列属性趋向于先查找有无默认值. tip: NULL与空字符串不同,Null会占用空间,需要表示某些字段可以为NULL. 2. 设置默认值(default value) 当插入数据中,有空字段时,会尝试查找该字段有没有设置默认

逻辑数据库设计 - 无视约束(谈外键)

有一些开发人员不推荐使用完整性约束,你可能听过以下这么几点不使用外键的原因. 1.数据更新有可能和约束冲突. 2.当前的数据库设计如此灵活,以致于不支持引用完整性约束. 3.数据库为外键建立的索引会影响性能. 4.当前使用的数据库不支持外键. 5.定义外键的语法并不简单,还需要查阅. 一.反模式:无视约束 即使第一感觉告诉你,省略外键约束能使得数据库设计更加简单.灵活,或者执行更加高效,你还是不得不在其他方面付出相应的代价 -- 必须增加额外的代码来手动维护引用完整性. 1.完整性问题 很多人对

事物的一致性:向数据库中插入同一属性不同列的值,出现异常,致使出现异常前后,数据都无法插入

主要是使用connection的三个方法实现: 开始事物:取消默认提交:connection.setAutoCommit(false);//其可选择false和true 都成功提交事物:connection.commit(); 回滚事物:connection.rollback(); ------------------------------------------------------------------------------------------------------------

逻辑数据库设计 - 单纯的树(递归关系数据)

相信有过开发经验的朋友都曾碰到过这样一个需求.假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复. 这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系.这是一种典型的递归关系数据. 对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择. 一.邻接表:依赖父节点 邻接表的方案如下(仅仅说明问题): CREATE TABLE Comments( CommentId int PK, ParentId int, --记录父节点 ArticleId int, Co

逻辑数据库设计 - 乱穿马路(多对多关系)

一.乱穿马路模式介绍 程序员通常使用逗号分隔的列表来避免在多对多关系中创建交叉表,这种设计方式定义为一种反模式,称为乱穿马路. 例如:在一个产品管理系统中,一个人可以有多个产品,一个产品必须对应一个人,因此有如下数据库: 但是,随着时间的推移,出现了一个产品可能会有多个联系人.于是为了最小限度地修改数据库,可能不少人会将Account_Id的类型修改成varchar,这样就可以列出该列中的多个账号Id,每个Id之间用逗号分隔.这样的设计貌似可行,因为并没有创建额外的表或者列.仅仅改变了一个字段的