精华内容
下载资源
问答
  • 数据库表设计

    千次阅读 2018-01-20 02:38:06
    无论表设计的再神奇,那么还是加一个备注字段。  (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。  (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在...

    参考:

    http://blog.csdn.net/famousdt/article/details/6921622

    http://blog.csdn.net/truong/article/details/29825913

    http://www.cnblogs.com/kissdodog/p/3297894.html

    http://www.csdn.net/article/2012-04-11/2804419


    范式:英文名称是 Normal Form,它是英国人 E.F.Codd(关系数据库的老祖宗)在上个世纪70年代提出关系数据库模型后总结出来的,范式是关系数据库理论的基础,也是我们在设计数据库结构过程中所要遵循的规则和指导方法。目前有迹可寻的共有8种范式,依次是:1NF,2NF,3NF,BCNF,4NF,5NF,DKNF,6NF。通常所用到的只是前三个范式,即:第一范式(1NF),第二范式(2NF),第三范式(3NF)。下面就简单介绍下这三个范式。 
    ◆ 第一范式(1NF):强调的是列的原子性,即列不能够再分成其他几列。 
    考虑这样一个表:【联系人】(姓名,性别,电话) 
    如果在实际场景中,一个联系人有家庭电话和公司电话,那么这种表结构设计就没有达到 1NF。要符合 1NF 我们只需把列(电话)拆分,即:【联系人】(姓名,性别,家庭电话,公司电话)。1NF 很好辨别,但是 2NF 和 3NF 就容易搞混淆。 
    ◆ 第二范式(2NF):首先是 1NF,另外包含两部分内容,一是表必须有一个主键;二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分。 
    考虑一个订单明细表:【OrderDetail】(OrderID,ProductID,UnitPrice,Discount,Quantity,ProductName)。 
    因为我们知道在一个订单中可以订购多种产品,所以单单一个 OrderID 是不足以成为主键的,主键应该是(OrderID,ProductID)。显而易见 Discount(折扣),Quantity(数量)完全依赖(取决)于主键(OderID,ProductID),而 UnitPrice,ProductName 只依赖于 ProductID。所以 OrderDetail 表不符合 2NF。不符合 2NF 的设计容易产生冗余数据。 
    可以把【OrderDetail】表拆分为【OrderDetail】(OrderID,ProductID,Discount,Quantity)和【Product】(ProductID,UnitPrice,ProductName)来消除原订单表中UnitPrice,ProductName多次重复的情况。 
    ◆ 第三范式(3NF):首先是 2NF,另外非主键列必须直接依赖于主键,不能存在传递依赖。即不能存在:非主键列 A 依赖于非主键列 B,非主键列 B 依赖于主键的情况。 
    考虑一个订单表【Order】(OrderID,OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity)主键是(OrderID)。 
    其中 OrderDate,CustomerID,CustomerName,CustomerAddr,CustomerCity 等非主键列都完全依赖于主键(OrderID),所以符合 2NF。不过问题是 CustomerName,CustomerAddr,CustomerCity 直接依赖的是 CustomerID(非主键列),而不是直接依赖于主键,它是通过传递才依赖于主键,所以不符合 3NF。 
    通过拆分【Order】为【Order】(OrderID,OrderDate,CustomerID)和【Customer】(CustomerID,CustomerName,CustomerAddr,CustomerCity)从而达到 3NF。 
    第二范式(2NF)和第三范式(3NF)的概念很容易混淆,区分它们的关键点在于,2NF:非主键列是否完全依赖于主键,还是依赖于主键的一部分;3NF:非主键列是直接依赖于主键,还是直接依赖于非主键列。


    3不同类型的数据应该分开管理,例如,财务数据库,业务数据库等。
    4、由于存储过程在不同的数据库中,支持方式不一样,因此不建议过多使用和使用复杂的存储过程。为数据库服务器降低压力,不要让数据库处理过多的业务逻辑,将业务逻辑处理放到应用程序中。

    2、  数据不要物理删除,应该加一个标志位,以防用户后悔时,能够恢复。还有就是比如人员登记加入某系统,采用登记卡的方式,一段时间后该人员退出了该系统,那么此时我们将该人员对应的记录标记为不可用,那么这个人就从系统中移除了,一段时间后这个人又回来了,那么我们直接修改标记字段为可用,这个人就从新加入系统,避免了其它信息的录入。

    6、  增加备注字段,虽然我们考虑了很多用户需要输入信息的需求,但是无论何时我们都不可能考虑全,因此可以定义一个备注字段,允许用户将其它的信息填写在这里。无论表设计的再神奇,那么还是加一个备注字段。

     (1) 在数据库物理设计时,降低范式,增加冗余, 少用触发器, 多用存储过程。
       (2) 当计算非常复杂、而且记录条数非常巨大时(例如一千万条),复杂计算要先在数据库外面,以

    文件系统方式用C++语言计算处理完成之后,最后才入库追加到表中去。这是电信计费系统设计的经验。
       (3) 发现某个表的记录太多,例如超过一千万条,则要对该表进行水平分割。水平分割的做法是,

    以该表主键PK的某个值为界线,将该表的记录水平分割为两个表。若发现某个表的字段太多,例如超过

    八十个,则垂直分割该表,将原来的一个表分解为两个表。



      相信有过开发经验的朋友都曾碰到过这样一个需求。假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复。

      这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系。这是一种典型的递归关系数据。

      对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择。

    一、邻接表:依赖父节点

      邻接表的方案如下(仅仅说明问题):

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        ParentId   int,    --记录父节点
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ParentId)  REFERENCES Comments(CommentId)   --自连接,主键外键都在自己表内
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

      由于偷懒,所以采用了书本中的图了,Bugs就是Articles:

      

      这种设计方式就叫做邻接表。这可能是存储分层结构数据中最普通的方案了。

      下面给出一些数据来显示一下评论表中的分层结构数据。示例表:

      

      图片说明存储结构:

      

      邻接表的优缺分析

      对于以上邻接表,很多程序员已经将其当成默认的解决方案了,但即便是这样,但它在从前还是有存在的问题的。

      分析1:查询一个节点的所有后代(求子树)怎么查呢?

      我们先看看以前查询两层的数据的SQL语句:

      SELECT c1.*,c2.*
      FROM Comments c1 LEFT OUTER JOIN Comments2 c2
      ON c2.ParentId = c1.CommentId

      显然,每需要查多一层,就需要联结多一次表。SQL查询的联结次数是有限的,因此不能无限深的获取所有的后代。而且,这种这样联结,执行Count()这样的聚合函数也相当困难。

      说了是以前了,现在什么时代了,在SQLServer 2005之后,一个公用表表达式就搞定了,顺带解决的还有聚合函数的问题(聚合函数如Count()也能够简单实用),例如查询评论4的所有子节点:

    复制代码
    WITH COMMENT_CTE(CommentId,ParentId,CommentBody,tLevel)
    AS
    (
        --基本语句
        SELECT CommentId,ParentId,CommentBody,0 AS tLevel FROM Comment
        WHERE ParentId = 4
        UNION ALL  --递归语句
        SELECT c.CommentId,c.ParentId,c.CommentBody,ce.tLevel + 1 FROM Comment AS c 
        INNER JOIN COMMENT_CTE AS ce    --递归查询
        ON c.ParentId = ce.CommentId
    )
    SELECT * FROM COMMENT_CTE
    复制代码

      显示结果如下:

      

      那么查询祖先节点树又如何查呢?例如查节点6的所有祖先节点:

    复制代码
    WITH COMMENT_CTE(CommentId,ParentId,CommentBody,tLevel)
    AS
    (
        --基本语句
        SELECT CommentId,ParentId,CommentBody,0 AS tLevel FROM Comment
        WHERE CommentId = 6
        UNION ALL
        SELECT c.CommentId,c.ParentId,c.CommentBody,ce.tLevel - 1  FROM Comment AS c 
        INNER JOIN COMMENT_CTE AS ce  --递归查询
        ON ce.ParentId = c.CommentId
        where ce.CommentId <> ce.ParentId
    )
    SELECT * FROM COMMENT_CTE ORDER BY CommentId ASC
    复制代码

      结果如下:

      

      再者,由于公用表表达式能够控制递归的深度,因此,你可以简单获得任意层级的子树。

      OPTION(MAXRECURSION 2)

      看来哥是为邻接表平反来的。

       分析2:当然,邻接表也有其优点的,例如要添加一条记录是非常方便的。

      INSERT INTO Comment(ArticleId,ParentId)...    --仅仅需要提供父节点Id就能够添加了。

       分析3:修改一个节点位置或一个子树的位置也是很简单.

    UPDATE Comment SET ParentId = 10 WHERE CommentId = 6  --仅仅修改一个节点的ParentId,其后面的子代节点自动合理。

      分析4:删除子树

      想象一下,如果你删除了一个中间节点,那么该节点的子节点怎么办(它们的父节点是谁),因此如果你要删除一个中间节点,那么不得不查找到所有的后代,先将其删除,然后才能删除该中间节点。

      当然这也能通过一个ON DELETE CASCADE级联删除的外键约束来自动完成这个过程。

       分析5:删除中间节点,并提升子节点

      面对提升子节点,我们要先修改该中间节点的直接子节点的ParentId,然后才能删除该节点:

      SELECT ParentId FROM Comments WHERE CommentId = 6;    --搜索要删除节点的父节点,假设返回4
      UPDATE Comments SET ParentId = 4 WHERE ParentId = 6;  --修改该中间节点的子节点的ParentId为要删除中间节点的ParentId
      DELETE FROM Comments WHERE CommentId = 6;          --终于可以删除该中间节点了

      由上面的分析可以看到,邻接表基本上已经是很强大的了。

    二、路径枚举

      路径枚举的设计是指通过将所有祖先的信息联合成一个字符串,并保存为每个节点的一个属性。

      路径枚举是一个由连续的直接层级关系组成的完整路径。如”/home/account/login”,其中home是account的直接父亲,这也就意味着home是login的祖先。

      还是有刚才新闻评论的例子,我们用路径枚举的方式来代替邻接表的设计:

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        Path      varchar(100),    --仅仅改变了该字段和删除了外键
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

       简略说明问题的数据表如下:

      CommentId  Path    CommentBody

      1       1/        这个Bug的成因是什么

      2       1/2/     我觉得是一个空指针

      3       1/2/3     不是,我查过了

      4       1/4/     我们需要查无效的输入

      5       1/4/5/    是的,那是个问题

      6       1/4/6/    好,查一下吧。

      7       1/4/6/7/   解决了

      路径枚举的优点:

      对于以上表,假设我们需要查询某个节点的全部祖先,SQL语句可以这样写(假设查询7的所有祖先节点):

    SELECT * FROM Comment AS c
    WHERE '1/4/6/7/' LIKE c.path + '%'

      结果如下:

      

      假设我们要查询某个节点的全部后代,假设为4的后代:

    SELECT * FROM Comment AS c
    WHERE c.Path LIKE '1/4/%'

      结果如下:

      

      一旦我们可以很简单地获取一个子树或者从子孙节点到祖先节点的路径,就可以很简单地实现更多查询,比如计算一个字数所有节点的数量(COUNT聚合函数)

      

       插入一个节点也可以像和使用邻接表一样地简单。可以插入一个叶子节点而不用修改任何其他的行。你所需要做的只是复制一份要插入节点的逻辑上的父亲节点路径,并将这个新节点的Id追加到路径末尾就可以了。如果这个Id是插入时由数据库生成的,你可能需要先插入这条记录,然后获取这条记录的Id,并更新它的路径。

      路径枚举的缺点:

      1、数据库不能确保路径的格式总是正确或者路径中的节点确实存在(中间节点被删除的情况,没外键约束)。

      2、要依赖高级程序来维护路径中的字符串,并且验证字符串的正确性的开销很大。

      3、VARCHAR的长度很难确定。无论VARCHAR的长度设为多大,都存在不能够无限扩展的情况。

      路径枚举的设计方式能够很方便地根据节点的层级排序,因为路径中分隔两边的节点间的距离永远是1,因此通过比较字符串长度就能知道层级的深浅。

    三、嵌套集

      嵌套集解决方案是存储子孙节点的信息,而不是节点的直接祖先。我们使用两个数字来编码每个节点,表示这个信息。可以将这两个数字称为nsleft和nsright。

      还是以上面的新闻-评论作为例子,对于嵌套集的方式表可以设计为:

    复制代码
      CREATE TABLE Comments(
        CommentId  int  PK,
        nsleft    int,  --之前的一个父节点
           nsright   int,  --变成了两个
        ArticleId  int,
        CommentBody nvarchar(500),
        FOREIGN KEY (ArticleId) REFERENCES Articles(ArticleId)
      )
    复制代码

      nsleft值的确定:nsleft的数值小于该节点所有后代的Id。

      nsright值的确定:nsright的值大于该节点所有后代的Id。

      当然,以上两个数字和CommentId的值并没有任何关联,确定值的方式是对树进行一次深度优先遍历,在逐层入神的过程中依次递增地分配nsleft的值,并在返回时依次递增地分配nsright的值。

      采用书中的图来说明一下情况:

      

      一旦你为每个节点分配了这些数字,就可以使用它们来找到给定节点的祖先和后代。

      嵌套集的优点:

      我觉得是唯一的优点了,查询祖先树和子树方便。

      例如,通过搜索那些节点的ConmentId在评论4的nsleft与nsright之间就可以获得其及其所有后代:

      SELECT c2.* FROM Comments AS c1
       JOIN Comments AS c2  ON cs.neleft BETWEEN c1.nsleft AND c1.nsright
      WHERE c1.CommentId = 1;

      结果如下:

      

      通过搜索评论6的Id在哪些节点的nsleft和nsright范围之间,就可以获取评论6及其所有祖先:

      SELECT c2.* FROM Comment AS c1
      JOIN Comment AS c2 ON c1.nsleft BETWEEN c2.nsleft AND c2.nsright
      WHERE c1.CommentId = 6;

      

      这种嵌套集的设计还有一个优点,就是当你想要删除一个非叶子节点时,它的后代会自动地代替被删除的节点,称为其直接祖先节点的直接后代。

      嵌套集设计并不必须保存分层关系。因此当删除一个节点造成数值不连续时,并不会对树的结构产生任何影响。

      嵌套集缺点:

      1、查询直接父亲。

      在嵌套集的设计中,这个需求的实现的思路是,给定节点c1的直接父亲是这个节点的一个祖先,且这两个节点之间不应该有任何其他的节点,因此,你可以用一个递归的外联结来查询一个节点,它就是c1的祖先,也同时是另一个节点Y的后代,随后我们使y=x就查询,直到查询返回空,即不存在这样的节点,此时y便是c1的直接父亲节点。

      比如,要找到评论6的直接父节点:老实说,SQL语句又长又臭,行肯定是行,但我真的写不动了。

      2、对树进行操作,比如插入和移动节点。

      当插入一个节点时,你需要重新计算新插入节点的相邻兄弟节点、祖先节点和它祖先节点的兄弟,来确保它们的左右值都比这个新节点的左值大。同时,如果这个新节点是一个非叶子节点,你还要检查它的子孙节点。

      够了,够了。就凭查直接父节点都困难,这个东西就很冷门了。我确定我不会使用这种设计了。

    四、闭包表

      闭包表是解决分层存储一个简单而又优雅的解决方案,它记录了表中所有的节点关系,并不仅仅是直接的父子关系。
      在闭包表的设计中,额外创建了一张TreePaths的表(空间换取时间),它包含两列,每一列都是一个指向Comments中的CommentId的外键。

    复制代码
    CREATE TABLE Comments(
      CommentId int PK,
      ArticleId int,
      CommentBody int,
      FOREIGN KEY(ArticleId) REFERENCES Articles(Id)
    )
    复制代码

      父子关系表:

    复制代码
    CREATE TABLE TreePaths(
      ancestor    int,
      descendant int,
      PRIMARY KEY(ancestor,descendant),    --复合主键
      FOREIGN KEY (ancestor) REFERENCES Comments(CommentId),
      FOREIGN KEY (descendant) REFERENCES Comments(CommentId)
    )
    复制代码

      在这种设计中,Comments表将不再存储树结构,而是将书中的祖先-后代关系存储为TreePaths的一行,即使这两个节点之间不是直接的父子关系;同时还增加一行指向节点自己,理解不了?就是TreePaths表存储了所有祖先-后代的关系的记录。如下图:

      

      Comment表:

      

      TreePaths表:

      

      优点:

      1、查询所有后代节点(查子树):

    SELECT c.* FROM Comment AS c
        INNER JOIN TreePaths t on c.CommentId = t.descendant
        WHERE t.ancestor = 4

      结果如下:

      

      2、查询评论6的所有祖先(查祖先树):

    SELECT c.* FROM Comment AS c
        INNER JOIN TreePaths t on c.CommentId = t.ancestor
        WHERE t.descendant = 6

      显示结果如下:

      

       3、插入新节点:

      要插入一个新的叶子节点,应首先插入一条自己到自己的关系,然后搜索TreePaths表中后代是评论5的节点,增加该节点与要插入的新节点的”祖先-后代”关系。

      比如下面为插入评论5的一个子节点的TreePaths表语句:

    复制代码
    INSERT INTO TreePaths(ancestor,descendant)
        SELECT t.ancestor,8
        FROM TreePaths AS t
        WHERE t.descendant = 5
        UNION ALL
        SELECT 8,8
    复制代码

      执行以后:

      

      至于Comment表那就简单得不说了。

      4、删除叶子节点:

      比如删除叶子节点7,应删除所有TreePaths表中后代为7的行:

      DELETE FROM TreePaths WHERE descendant = 7

      5、删除子树:

      要删除一颗完整的子树,比如评论4和它的所有后代,可删除所有在TreePaths表中的后代为4的行,以及那些以评论4的后代为后代的行:

      DELETE FROM TreePaths
      WHERE descendant 
      IN(SELECT descendant FROM TreePaths WHERE ancestor = 4)

      另外,移动节点,先断开与原祖先的关系,然后与新节点建立关系的SQL语句都不难写。

      另外,闭包表还可以优化,如增加一个path_length字段,自我引用为0,直接子节点为1,再一下层为2,一次类推,查询直接自子节点就变得很简单。

    总结

      其实,在以往的工作中,曾见过不同类型的设计,邻接表,路径枚举,邻接表路径枚举一起来的都见过。

      每种设计都各有优劣,如果选择设计依赖于应用程序中哪种操作最需要性能上的优化。 

      下面给出一个表格,来展示各种设计的难易程度:

    设计 表数量 查询子 查询树 插入 删除 引用完整性
    邻接表 1 简单 简单 简单 简单
    枚举路径 1 简单 简单 简单 简单
    嵌套集 1 困难 简单 困难 困难
    闭包表 2 简单 简单 简单 简单

      1、邻接表是最方便的设计,并且很多软件开发者都了解它。并且在递归查询的帮助下,使得邻接表的查询更加高效。

      2、枚举路径能够很直观地展示出祖先到后代之间的路径,但由于不能确保引用完整性,使得这个设计比较脆弱。枚举路径也使得数据的存储变得冗余。

      3、嵌套集是一个聪明的解决方案,但不能确保引用完整性,并且只能使用于查询性能要求较高,而其他要求一般的场合使用它。

      4、闭包表是最通用的设计,并且最灵活,易扩展,并且一个节点能属于多棵树,能减少冗余的计算时间。但它要求一张额外的表来存储关系,是一个空间换取时间的方案。






    展开全文
  • MySQL数据库表设计

    2019-08-12 23:22:35
    MySQL数据库表设计 ​ 数据库设计(Database Design)是指对于一个给定的应用环境,构造最优的数据库模式,建立数据库及其应用系统,使之能够有效地存储数据,满足各种用户的应用需求(信息要求和处理要求)。在数据库...

    MySQL数据库表设计

    ​ 数据库设计(Database Design)是指对于一个给定的应用环境,构造最优的数据库模式,建立数据库及其应用系统,使之能够有效地存储数据,满足各种用户的应用需求(信息要求和处理要求)。在数据库领域内,常常把使用数据库的各类系统统称为数据库应用系统

    数据库设计

    三大范式

    第一范式( 1NF)
    ​ 字段具有原子性,不可再分。 所有关系型数据库系统都满足第一范式)数据库表中的字段都是单一属性的, 不可再分;

    第二范式( 2NF)
    ​ 要求实体的属性完全依赖于主键。 所谓完全依赖是指不能存在仅依赖主键一部分的属性,如果存在, 那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体, 新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的惟一标识。简而言之, 第二范式就是属性完全依赖主键。

    第三范式( 3NF)
    ​ 满足第三范式( 3NF) 必须先满足第二范式( 2NF)。 简而言之, 第三范式( 3NF)要求一个数据库表中不包含已在其它表中已包含的非主键信息。

    简单一点:

    1. 每一列只有一个单一的值,不可再拆分
    2. 每一行都有主键能进行区分
    3. 每一个表都不包含其他表已经包含的非主键信息。

    数据库表设计

    充分的满足第一范式设计将为表建立太量的列

    ​ 数据从磁盘到缓冲区,缓冲区脏页到磁盘进行持久的过程中,列的数量过多会导致性能下降。过多的列影响转换和持久的性能

    过分的满足第三范式化造成了太多的表关联

    ​ 表的关联操作将带来额外的内存和性能开销

    使用innodb引擎的外键关系进行数据的完整性保证

    ​ 外键表中数据的修改会导致Innodb引擎对外键约束进行检查,就带来了额外的开销,一般数据库不用外键,外键逻辑在业务层控制

    举个案例,想一想下面的SQL如何优化

    SELECT
    *
    FROM
    XXXXX
    WHERE
    (convert((price_full * 100 - price * 100) , SIGNED) - convert(coupon_price*100,SIGNED)
    AND
    is_del = 0)
    ORDER BY
    id
    desc
    limit 100
    

    问题:在where条件,对索引列计算,会让索引失效,从而扫描全表。性能比较差

    优化:(仅供参考)

    ​ 假如(convert((price_full * 100 - price * 100) , SIGNED) - convert(coupon_price*100,SIGNED)没有优化的余地,那这条SQL无法拯救了嘛?

    ​ 可以在该表添加一个列(成本列),把这个条件的运算结果在insert之前计算好结果(假设新增之后,price_full、price、coupon_price列不会频繁更新),放入表中。然后对添加的这个成本列和is_del 建一个联合索引。把查询消耗的时间成本转移到了新增,这也只是一个方案,实际还要看具体的业务场景。技术服务于业务。

    附录 58同城军规

    军规适用场景:并发量大、数据量大的互联网业务
    军规:介绍内容
    解读:讲解原因,解读比军规更重要
    一、基础规范

    (1)必须使用InnoDB存储引擎
    解读:支持事务、行级锁、并发性能更好、CPU及内存缓存页优化使得资源利用率更高

    (2)必须使用UTF8字符集 UTF-8MB4
    解读:万国码,无需转码,无乱码风险,节省空间

    (3)数据表、数据字段必须加入中文注释
    解读:N年后谁tm知道这个r1,r2,r3字段是干嘛的

    (4)禁止使用存储过程、视图、触发器、Event
    解读:高并发大数据的互联网业务,架构设计思路是“解放数据库CPU,将计算转移到服务
    层”,并发量大的情况下,这些功能很可能将数据库拖死,业务逻辑放到服务层具备更好的
    扩展性,能够轻易实现“增机器就加性能”。数据库擅长存储与索引,CPU计算还是上移吧

    (5)禁止存储大文件或者大照片
    解读:为何要让数据库做它不擅长的事情?大文件和照片存储在文件系统,数据库里存URI
    多好

    二、命名规范

    (6)只允许使用内网域名,而不是ip连接数据库

    (7)线上环境、开发环境、测试环境数据库内网域名遵循命名规范
    业务名称:xxx
    线上环境:dj.xxx.db
    开发环境:dj.xxx.rdb
    测试环境:dj.xxx.tdb
    从库在名称后加-s标识,备库在名称后加-ss标识
    线上从库:dj.xxx-s.db
    线上备库:dj.xxx-sss.db

    (8)库名、表名、字段名:小写,下划线风格,不超过32个字符,必须见名知意,禁止
    拼音英文混用

    (9)表名t_xxx,非唯一索引名idx_xxx,唯一索引名uniq_xxx
    三、表设计规范

    (10)单实例表数目必须小于500

    (11)单表列数目必须小于30

    (12)表必须有主键,例如自增主键
    解读:
    a)主键递增,数据行写入可以提高插入性能,可以避免page分裂,减少表碎片提升空间和
    内存的使用
    b)主键要选择较短的数据类型, Innodb引擎普通索引都会保存主键的值,较短的数据类
    型可以有效的减少索引的磁盘空间,提高索引的缓存效率
    c) 无主键的表删除,在row模式的主从架构,会导致备库夯住

    (13)禁止使用外键,如果有外键完整性约束,需要应用程序控制
    解读:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响
    sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能,大数据高并发业务场景
    数据库使用以性能优先

    四、字段设计规范

    (14)必须把字段定义为NOT NULL并且提供默认值
    解读:
    a)null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化
    b)null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条
    件下,表中有较多空字段的时候,数据库的处理性能会降低很多
    c)null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标

    d)对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、
    not in这些操作符号。如:where name!=’shenjian’,如果存在name为null值的记
    录,查询结果就不会包含name为null值的记录

    (15)禁止使用TEXT、BLOB类型
    解读:会浪费更多的磁盘和内存空间,非必要的大量的大字段查询会淘汰掉热数据,导致内
    存命中率急剧降低,影响数据库性能

    (16)禁止使用小数存储货币
    解读:使用整数吧,小数容易导致钱对不上

    (17)必须使用varchar(20)存储手机号
    解读:
    a)涉及到区号或者国家代号,可能出现±()
    b)手机号会去做数学运算么?
    c)varchar可以支持模糊查询,例如:like“138%”

    (18)禁止使用ENUM,可使用TINYINT代替
    解读:
    a)增加新的ENUM值要做DDL操作
    b)ENUM的内部实际存储就是整数,你以为自己定义的是字符串?

    五、索引设计规范

    (19)单表索引建议控制在5个以内

    (20)单索引字段数不允许超过5个
    解读:字段超过5个时,实际已经起不到有效过滤数据的作用了

    (21)禁止在更新十分频繁、区分度不高的属性上建立索引
    解读:
    a)更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能
    b)“性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性
    能与全表扫描类似

    (22)建立组合索引,必须把区分度高的字段放在前面
    解读:能够更加有效的过滤数据

    六、SQL使用规范

    (23)禁止使用SELECT *,只获取必要的字段,需要显示说明列属性
    解读:
    a)读取不需要的列会增加CPU、IO、NET消耗
    b)不能有效的利用覆盖索引

    (24)禁止使用INSERT INTO t_xxx VALUES(xxx),必须显示指定插入的列属性
    解读:容易在增加或者删除字段后出现程序BUG

    (25)禁止使用属性隐式转换
    解读:SELECT uid FROM t_user WHERE phone=13812345678 会导致全表扫描,而不
    能命中phone索引

    (26)禁止在WHERE条件的属性上使用函数或者表达式
    解读:SELECT uid FROM t_user WHERE from_unixtime(day)>=‘2017-02-15’ 会导致全
    表扫描
    正确的写法是:SELECT uid FROM t_user WHERE day>= unix_timestamp(‘2017-02-15
    00:00:00’)

    (27)禁止负向查询,以及%开头的模糊查询
    解读:
    a)负向查询条件:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等,会导致全表扫描
    b)%开头的模糊查询,会导致全表扫描

    (28)禁止大表使用JOIN查询,禁止大表使用子查询
    解读:会产生临时表,消耗较多内存与CPU,极大影响数据库性能

    (29)禁止使用OR条件,必须改为IN查询
    解读:旧版本Mysql的OR查询是不能命中索引的,即使能命中索引,为何要让数据库耗费
    更多的CPU帮助实施查询优化呢?

    (30)应用程序必须捕获SQL异常,并有相应处理
    总结:大数据量高并发的互联网业务,极大影响数据库性能的都不让用,不让用哟。

    还可以多看看阿里巴巴开发手册终极版中的关于MySQL部分的

    点击下载《阿里巴巴开发手册终极版》

    链接: https://pan.baidu.com/s/1fvj2a9bF6e4hRB3AMsOGZw 提取码: 8pez 复制这段内容后打开百度网盘手机App,操作更方便哦

    展开全文
  • 商城 商品模块 数据库 表设计

    万次阅读 多人点赞 2017-05-12 23:30:04
    要实现一个商城,对于商品模块中的数据库表设计不懂,主要是:相同类别的产品的产品参数相同,不同类别的不同,这里就不懂要怎么设计了,所以上网找几篇博客了解什么是SPUSKUARPU PHP商城 商品模块 数据库 表设计...

    要实现一个商城,对于商品模块中的数据库表设计不懂,主要是:相同类别的产品的产品参数相同,不同类别的不同,这里就不懂要怎么设计了,所以上网找几篇博客了解



    什么是SPU、SKU、ARPU

    这是一篇存档性笔记,我自己存档一下对这3个词的理解。如果你已经明了了这3个词的意思,请直接忽略之

    首先,搞清楚商品与单品的区别。例如,iphone是一个单品,但是在淘宝上当很多商家同时出售这个产品的时候,iphone就是一个商品了。

    商品:淘宝叫item,京东叫product,商品特指与商家有关的商品,每个商品有一个商家编码,每个商品下面有多个颜色,款式,可以有多个SKU。

    SPU = Standard Product Unit (标准化产品单元)

    SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。通俗点讲,属性值、特性相同的商品就可以称为一个SPU。

    例如,iphone4就是一个SPU,N97也是一个SPU,这个与商家无关,与颜色、款式、套餐也无关。以化妆品为例,下图是拍拍商城给出的SPU信息:
    这里写图片描述

    在商品信息电子化过程中,商品的特性可以由多个“属性及对应的属性值对”进行描述。“属性及对应的属性值对”完全相同的商品,可以抽象成为一个SPU。同 时,这些“属性及对应的属性值对”也在SPU中固化下来,逐步标准化。基于SPU的商品信息结构,可以实现丰富的应用,比如商品信息与资讯、评论、以及其 它SPU的整合。

    P.s:从这个意义上讲,我认为比较购物的产品库以SPU为标准来建立是最合适的。

    SKU=stock keeping unit(库存量单位)

    SKU即库存进出计量的单位, 可以是以件、盒、托盘等为单位。在服装、鞋类商品中使用最多最普遍。 例如纺织品中一个SKU通常表示:规格、颜色、款式。

    也有人解释说SKU就是库存的最小单位,在服装行业,正常情况是“单款单色单码”,国内品牌有把“单款单色”当做一个SKU、也有把“单款”的几个色当一个SKU、也有把一块面料的几个个款式当一个SKU,这些都是误读。

    同时,引申出另外一个概念:SKC:单款、单色。如果一定要打比方的话:SKC是一个桔子,SKU是一瓣桔子,但不管怎么说,一个桔子是桔子,一瓣桔子也是桔子。

    不过,SKU是物理上不可分割的最小存货单元。在使用时要根据不同业态,不同管 理模式来处理。比如一香烟是50条,一条里有十盒,一盒中有20支,这些单位就要根据不同的需要来设定SKU。比如仓储批发式大卖场,一定是按照一箱来设 定的。普通大卖场一定是按照条来设定的。烟酒专卖店一定是按照盒来设定的。过去上海等地的街边小店一定是按一支来设定的。这样一支就是烟的最小零售单位。 但要根据自己的业态和服务模式来设定。

    P.s:关于什么是SKU、SKC,可以参考阿福先生的这篇博客

    【总结一下】:SPU是标准化产品单元,区分品种;SKU是库存量单位,区分单品;商品特指与商家有关的商品,可对应多个SKU

    ARPU=Average Revenue Per User(每用户平均收入)

    ARPU注重的是一个时间段内运营商从每个用户所得到的利润。因此,高端的用户越多,ARPU越高。在这个时间段里,从运营商的运营情况来看,ARPU值高说明利润高,这段时间效益好。

    原文地址:http://www.ikent.me/blog/3017


    PHP商城 商品模块 数据库 表设计

    这里写图片描述

    表关系:
    分类表 <= 商品表 <= SKU表(库存表)
    分类表 <= 属性名 <= 属性值
    商品表 <= 商品和属性关系表 => 属性名|属性值

    业务逻辑:
    1.同一商品不同SKU库存和售价不同.
    2.不同类型的商品具有不同的属性名和属性值(如汽车和服饰),所以属性需要支持后期添加和维护.
    3.在某个商品分类下通过属性筛选商品.
    4.商家某件商品的销量统计,该件商品内几个不同SKU的销量统计.
    5.更多…

    分类表:
    (商品分类编号, 分类名称, 父分类编号)
    (1, 男装, 0)
    (2, 裤子, 1)
    (3, 外套, 1)
    (4, 内裤, 1)
    (5, 袜子, 1)
    
    商品表:
    (商品编号, 商品名称, 商品分类编号, 卖家编号, SPU销量, 评论数)
    (1, '裤子名', 2, 1, 0)
    (2, '外套名', 3, 1, 0)
    (3, '内裤名', 4, 1, 0)
    (4, '袜子名', 5, 1, 0)
    
    SKU表(库存表):
    (SKU编号, 商品编号, SKU属性, 价格, 库存, SKU销量)
    (1, 1, [1:1,2:3], 99, 400, 0) 其中 [1:1,2:3] 表示 "颜色为黑色,尺码为X"
    (2, 1, [1:1,2:4], 99, 200, 0) 其中 [1:1,2:4] 表示 "颜色为黑色,尺码为XL"
    (3, 1, [1:2,2:3], 99, 300, 0) 其中 [1:2,2:3] 表示 "颜色为白色,尺码为X"
    (4, 1, [1:2,2:4], 99, 100, 0) 其中 [1:2,2:4] 表示 "颜色为白色,尺码为XL"
    上面只列出商品1这个分类的4个SKU.
    
    属性名:
    (属性名编号, 属性名, 商品分类编号, 父属性编号)
    (1, 颜色, 2, 0)
    (2, 尺码, 2, 0)
    (3, 品牌, 2, 0)
    上面只列出裤子这个分类的3个属性名.
    
    属性值:
    (属性值编号, 属性值, 属性名编号)
    (1, 黑色, 1)
    (2, 白色, 1)
    (3, X,  2)
    (4, XL, 2)
    (5, 七匹狼, 3)
    (6, 九牧王, 3)
    上面只列出裤子这个分类的6个属性值.
    
    商品和属性关系表:
    (自增编号, 商品编号, 属性名编号, 属性值编号)
    (1, 1, 1, 1) 商品1颜色为黑色
    (2, 1, 1, 2) 商品1颜色为白色
    (3, 1, 2, 3) 商品1尺码为X
    (4, 1, 2, 4) 商品1尺码为XL
    上面只列出商品14个属性关系.
    
    商品和属性筛选表:
    (商品编号, 商品具有的属性值编号)
    (1, [1,2,3,4])
    用SQL全文检索实现筛选.
    如:
    select * from 商品表 
    inner join 商品和属性筛选表 
    on 商品表.商品编号 = 商品和属性筛选表.商品编号
    where 商品表.商品分类编号 = 2 
    and 商品和属性筛选表.商品具有的属性值编号 MATCH '1 3'
    order by 商品表.评论数 DESC LIMIT 10 OFFSET 20;
    
    商品搜索表:
    (商品编号, 商品标题和内容)
    (1, [无需词典,二元分词])
    用SQL全文检索实现搜索.
    

    里面有这么一些表结构设计思想:
    名值: id, name, value (用于实现自定义字段如属性的存储)
    父子: id, pid (用于实现关系树如分类和子分类的存储)
    其中”名值”的思想应该就是EAV(Entity-Attribute-Value)实体属性值模型思想.
    留意过WordPress数据表的也会看到类似设计:
    wp_postmeta(meta_id,post_id,meta_key,meta_value)
    wp_commentmeta(meta_id,comment_id,meta_key,meta_value)
    wp_usermeta(umeta_id,user_id,meta_key,meta_value)
    wp_options(option_id,option_name,option_value,autoload)
    “父子”存储无限极分类:
    wp_term_taxonomy(term_taxonomy_id,term_id,taxonomy,parent)

    上面提到的无需词典的二元分次算法示例:

    <?php
    function cws($str) {
    //找出字符串中的英文单词和数字
    if(preg_match_all('%[A-Za-z0-9_-]{1,}%', $str, $matches)) {
    $arr = $matches[0];
    }
    //以非中文(中文包括简体和繁体)进行正则分割
    $sections = preg_split('%[^\x{4e00}-\x{9fa5}]{1,}%u', $str);
    foreach($sections as $v) {
    //注意:foreach中多次正则匹配会降低性能
    switch(true) {
    case ($v === ''): continue; break;
    case (mb_strlen($v, 'UTF-8') < 3): $arr[] = $v; break;
    case (preg_match_all('%[\x{4e00}-\x{9fa5}]%u', $v, $matches)):
    //前后俩俩组合,实现冗余分词.
    //如"中国好声音"将被分词为: 中国 国好 好声 声音
    $size = count($matches[0]);
    for($i = 0; $i <= $size-2; $i++) {
    $word = '';
    for($j = 0; $j < 2; $j++) {
    $word .= $matches[0][$i+$j]; //echo $i.' '.$j.' '.$matches[0][$i+$j]."\n";
    }
    $arr[] = $word; //echo "\n";
    }
    break;
    }
    }
    return array_unique($arr);
    }

    © 著作权归作者所有
    分类:工作日志 字数:1050
    标签: 商品库 商城 SKU SQL 数据库设计

    原文链接:https://my.oschina.net/eechen/blog/857845


    展开全文
  • 用户登录系统数据库表设计 最近看了看公司后台用户登录系统的设计, 比较混乱, 主要还是因为URS和Oauth以及URS第三方这三个登录形式各不相同导致的。 下面着重介绍一下涉及到第三方登录中需要注意的...

    最近看了看公司后台用户登录系统的设计, 比较混乱, 主要还是因为URS和Oauth以及URS第三方这三个登录形式各不相同导致的。

    下面着重介绍一下涉及到第三方登录中需要注意的问题

     

     

    在一个新项目中, 如果是要建立自己的登录体系的话, 那么直接创建一个Users表,包含usernamepassword两列,这样,就可以实现登录了:

     id | username | password | name等其他字段
    ----+----------+----------+----------------
     A1 | bob      | a1b23f2c | ...
     A2 | adam     | c0932f32 | ...
    

    如果要让用户通过第三方登录,比如微博登录或QQ登录,怎么集成进来呢?

    以微博登录为例,由于微博使用OAuth2协议登录,所以,一个登录用户会包含他的微博身份的ID,一个Access Token用于代表该用户访问微博的API和一个过期时间。

    要集成微博登录,很多童鞋立刻想到把Users表扩展几列,记录下微博的信息:

     id | username | password | weibo_id | weibo_access_token | weibo_expires | name等其他字段
    ----+----------+----------+----------+--------------------+---------------+----------------
     A1 | bob      | a1b23f2c | W-012345 | xxxxxxxxxx         | 604800        | ...
     A2 | adam     | c0932f32 | W-234567 | xxxxxxxxxx         | 604800        | ...
    

    加一个QQ登录Users表就又需要加3列,非常不灵活

     

    那么我们需要对这个表进行拆分。当用户以任意一种方式登录成功后,我们读取到的总是Users表对应的一行记录,它实际上是用户的个人资料(Profile),而登录过程只是为了认证用户(Authenticate),无论是本地用密码验证,还是委托第三方登录,这个过程本质上都是认证。

    所以,如果把Profile和Authenticate分开,就十分容易理解了。Users表本身只存储用户的Profile, 其中ID为关联不同登录方式的外键。

     id | name | birth等其他字段
    ----+------+-----------------
     A1 | Bob  |  ...
     A2 | Adam | ...
    

    而通过用户名口令登录可视为一种Authenticate的方式,利用LocalAuth表维护:

     id | user_id | username | password
    ----+---------+----------+-----------
     01 | A1      | bob      | a1b23f2c
     02 | A2      | adam     | c0932f32

    通过微博登录可视为另一种Authenticate方式,利用OAuth表维护, 但是access_token一般情况也只有几个小时的时效, 所以存储它是没有意义的, 每次登录的时候去微博后台验证一下客户端传来的token就行了。 如果用户只用了第三方登录, 那就拿第三方数据来填充刚才的User表即可。

     id | user_id | weibo_id |
    ----+---------+----------+
     11 | A1      | W-012345 |
     12 | A2      | W-234567 |
    

    如果要添加另一种OAuth登录,比如QQ登录,那就再加一个列标示不同站点也就OK了, 但是要注意用户在不同登录方式的用户名和photo一般不一样, 所以也单独存起来

     id | user_id | oauth_name | oauth_id | nick_name| photo|
    ----+---------+------------+----------+----------+------+
     11 | A1      | weibo      | W-012345 | 
     12 | A2      | weibo      | W-234567 |
     13 | A1      | qq         | Q-090807 |
     14 | A2      | qq         | Q-807060 |
    

    通过这种方式, 无论用户采用哪种方式登录, 都可以锁定到用户的user_id。

    下面再说一下网易的URS登录, 因为我们要直接采用网易通行证, 所以也就不需自己存储密码, 因此我们的架构应该设为User表

     id | user_Email | username | birth
    ----+------------+----------+-----------
     01 | aa@126.com | bob      | 
     02 | bb@126.com | adam     | 

    如果用户只用第三方登录, 显然无法填充user_Email这个字段, 因此userEmail可以为空。 如果第三方登录采用的是URS第三方的接口, 它返回的oauth_id 是aa@wx.163.com这种形式。 具体设计和上面也类似。 整体上使用这种方式比现在后台的逻辑要清晰很多

    转载于:https://www.cnblogs.com/ysq2018China/p/10291779.html

    展开全文
  • 常见电商项目的数据库表设计(MySQL版)

    万次阅读 多人点赞 2019-03-14 11:17:06
    常见电商项目的数据库表设计(MySQL版) 简介: 目的: 电商常用功能模块的数据库设计 常见问题的数据库解决方案 环境: MySQL5.7 图形客户端,SQLyog Linux 模块: 用户:注册、登陆 商品:浏览、管理 订单:...
  • 数据库表设计和优化

    千次阅读 2018-04-23 10:28:08
    一 、数据库表设计原则 1,数据库命名原则:英文字母,多个单词间用下划线’_’,单词尽量简洁、见名知意 2,数据库表命名原则:英文字母,多个单词间用下划线’_’,单词尽量简洁、见名知意 3,数据库表字段类型...
  • 微信点餐数据库表设计

    千次阅读 2018-04-26 15:34:00
    -- 微信点餐数据库表设计-- product_info商品表-- product_category类目表-- order_master订单主表-- order_detail订单详情表-- seller_info卖家信息表 -- 商品表 -- 主键:product_id -- 创建时间:默认当前时间...
  • 数据库表设计 基本思路

    千次阅读 2018-01-30 16:05:06
    好的数据库表设计会影响数据库操作效率。特别是数据多的时候,如果表的结构不好的话操作的时候条件(where后的内容)会变的非常复杂。 SQL是关系数据库中用到的一种语言。所以,为了简化SQL,表的关系(内部和外部...
  • Oracle 数据库设计-数据库表设计

    千次阅读 2018-11-01 21:22:57
    设计表的关系之前 需要先了解关系型数据库特点 1、关系型数据库,是指采用了关系模型来组织数据的数据库; 2、关系型数据库的最大特点就是事务的一致性; 3、简单来说,关系模型指的就是二维表格模型,而一个关系...
  • 常见电商项目的数据库表设计

    千次阅读 2019-05-31 15:04:04
    名称 链接 常见电商项目的数据库表设计(MySQL版) https://cloud.tencent.com/developer/article/1164332
  • mysql数据库表设计和优化

    千次阅读 2017-07-28 15:36:09
    一 mysql数据库表设计原则 1,数据库命名原则:英文字母,多个单词间用下划线'_',单词尽量简洁、见名知意 2,数据库表命名原则:英文字母,多个单词间用下划线'_',单词尽量简洁、见名知意 3,数据库表字段...
  • 数据库表设计的几条准则

    千次阅读 2019-06-11 09:54:15
    前言:数据库设计在平时的工作是必不...数据库表设计的几条黄金准则: 一:字段的原子性 解释:保证每列的原子性,不可分解,意思表达要清楚,不能含糊,高度概括字段的含义,能用一个字段表达清楚的绝不使用第二...
  • 数据库表设计的几点原则

    千次阅读 2018-11-03 10:25:18
    前言:数据库设计在平时的工作是必不...数据库表设计的几条黄金准则: 一:字段的原子性 解释:保证每列的原子性,不可分解,意思表达要清楚,不能含糊,高度概括字段的含义,能用一个字段表达清楚的绝不使用第二...
  • 3.1对1数据库表设计、1对多数据库表设计、多对多数据库表设计三种关联方式:左关联、右关联、内联左关联(left join):以左表为主(左表保持完整,将右表中和左表有依赖关系的记录添加进来形成新的表) 右关联(right...
  • 数据库表设计原则

    千次阅读 2017-02-14 14:16:07
    转自:为爱奔跑 的博客 数据库设计的三大范式:为了建立冗余较小、结构合理的数据库,设计数据库时必须遵循一定的规则。...如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一
  • 树形结构的数据库表设计

    千次阅读 2016-07-30 18:20:20
    树形结构的数据库表设计 最近研究树形菜单网上找了很多例子看了。一下是网上找的一些资料,然后自己重新实践,记录下免得下次又忘记了。 程序设计过程中,我们常常用树形结构来表征某些数据的关联关系,如企业上下级...
  • 数据库表设计感悟

    千次阅读 2018-09-21 02:08:43
     之前设计数据库的时候主要存在以下一些问题:  1.对于关系型数据库来说,如何开始设计? 当然这里不是指常规的软件工程那样的步骤。 就java而言,我所知道的就有,一种先在数据库设计,然后建立相应的实体。...
  • 电商秒杀系统-数据库表设计

    千次阅读 2016-07-11 15:51:09
    电商秒杀系统之数据库表设计 好长时间没有接触到数据库这块,通过一个简单电商秒杀系统回顾以前在项目中用到的一些技术 简单电商秒杀系统这里我们使用的数据库是mysql 这里有几点说明: engine: mysql...
  • 数据库表设计及存储数据爬取数据创建数据库创建数据库表链接数据库表数据存储彩蛋 爬取数据 有的小朋友会说,鱼叔,你这不是数据库表设计吗,怎么还送套餐是咋的? 哈哈~ 买二送一… 我们爬取数据,是为了把数据...
  • GPS定位数据库表设计

    千次阅读 2013-05-16 23:46:56
    GPS定位数据库表设计 在开发工业系统的数据采集功能相关的系统时,由于数据都是定时上传的,如每20秒上传一次的时间序列数据,这些数据在经过处理和计算后,变成了与时间轴有关的历史数据(与股票数据相似,如下...
  • 需求类似于微博,可以为主题点赞,但不能重复点赞。 目前的设计是有一个字段(varchar(max)),记录所有点过的用户id,类似于:311000|3110001|3110002 ...感觉这样设计不是很好,请问这样的怎么设计比较好? 谢谢。
  • 1.数据库表设计,共分为两部分,第一部分为商品管理表,第二部分为用户管理表 具体表关系如下: 商品系列表: 用户系列表: 2.项目效果展示 (1)登录页面 (2) 总界面 (3)用户管理 (4)...
  • 商城数据库表设计介绍

    千次阅读 2020-07-18 23:21:48
     使用PowerDesigner对数据以及他们之间的关系进行了粗略的设计得出了如下E-R图:   1.用户 2. 商品 3.商品类别 4.购物车 5. 订单 6.订单明细 7.支付信息 8.收货信息...
  • 数据库表设计的技巧 1. 原始单据与实体之间的关系  可以是一对一、一对多、多对多的关系。在一般情况下,它们是一对一的关系:即一张原始单据对 应且只对应一个实体。在特殊情况下,它们可能是一对多或多对一的...
  • 数据库表设计最终方案

    千次阅读 2017-03-03 10:19:31
    每天睡觉前必做两件事数据库表设计一对多 多对一 多对多 一对一一对多或多对一:多的一方加外键,描述数据的关系 多对多:加中间表来描述关系一对多或多对一的对象存到数据库时,表的设计方案(部门和员工) create...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,137
精华内容 20,454
关键字:

数据库表设计