目录
一 Codd的RDBMS12法则——RDBMS的起源
二 关系型数据库设计阶段
三 设计原则
四 命名规则
数据库设计,一个软件项目成功的基石。很多从业人员都认为,数据库设计其实不那么重要。现实中的情景也相当雷同,开发人员的数量是数据库设计人员的数倍。多数人使用数据库中的一部分,所以也会把数据库设计想的如此简单。其实不然,数据库设计也是门学问。
从笔者的经历看来,笔者更赞成在项目早期由开发者进行数据库设计(后期调优需要DBA)。根据笔者的项目经验,一个精通OOP和ORM的开发者,设计的数据库往往更为合理,更能适应需求的变化,如果追其原因,笔者个人猜测是因为数据库的规范化,与OO的部分思想雷同(如内聚)。而DBA,设计的数据库的优势是能将DBMS的能力发挥到极致,能够使用SQL和DBMS实现很多程序实现的逻辑,与开发者相比,DBA优化过的数据库更为高效和稳定。如标题所示,本文旨在分享一名开发者的数据库设计经验,并不涉及复杂的SQL语句或DBMS使用,因此也不会局限到某种DBMS产品上。真切地希望这篇文章对开发者能有所帮助,也希望读者能帮助笔者查漏补缺。
一 Codd的RDBMS12法则——RDBMS的起源
Edgar Frank Codd(埃德加·弗兰克·科德)被誉为“关系数据库之父”,并因为在数据库管理系统的理论和实践方面的杰出贡献于1981年获图灵奖。在1985年,Codd 博士发布了12条规则,这些规则简明的定义出一个关系型数据库的理念,它们被作为所有关系数据库系统的设计指导性方针。
二 关系型数据库设计阶段
(一)规划阶段
规划阶段的主要工作是对数据库的必要性和可行性进行分析。确定是否需要使用数据库,使用哪种类型的数据库,使用哪个数据库产品。
(二)概念阶段
概念阶段的主要工作是收集并分析需求。识别需求,主要是识别数据实体和业务规则。对于一个系统来说,数据库的主要包括业务数据和非业务数据,而业务数据的定义,则依赖于在此阶段对用户需求的分析。需要尽量识别业务实体和业务规则,对系统的整体有初步的认识,并理解数据的流动过程。理论上,该阶段将参考或产出多种文档,比如“用例图”,“数据流图”以及其他一些项目文档。如果能够在该阶段产出这些成果,无疑将会对后期进行莫大的帮助。当然,很多文档已超出数据库设计者的考虑范围。而且,如果你并不精通该领域以及用户的业务,那么请放弃自己独立完成用户需求分析的想法。用户并不是技术专家,而当你自身不能扮演“业务顾问”的角色时,请你选择与项目组的相关人员合作,或者将其视为风险呈报给PM。再次强调,大多数情况,用户只是行业从业者,而非职业技术人员,我们仅仅从用户那里收集需求,而非依赖于用户的知识。
记录用户需求时,可以使用一些技巧,当然这部分内容有些可能会超出数据库设计人员的职责:
- 努力维护一系列包含了系统设计和规格说明信息的文档,如会议记录、访谈记录、关键用户期望、功能规格、技术规格、测试规格等。
- 频繁与干系人沟通并收集反馈。
- 标记出你自己添加的,不属于客户要求的,未决内容。
- 与所有关键干系人尽快确认项目范围,并力求冻结需求。
此外,必须严谨处理业务规则,并详细记录。在之后的阶段,将会根据这些业务规则进行设计。
当该阶段结束时,你应该能够回答以下问题:
- 需要哪些数据?
- 数据该被怎样使用?
- 哪些规则控制着数据的使用?
- 谁会使用何种数据?
- 客户想在核心功能界面或者报表上看到哪些内容?
- 数据现在在哪里?
- 数据是否与其他系统有交互、集成或同步?
- 主题数据有哪些?
- 核心数据价值几何,对可靠性的要求程度?
并且得到如下信息:
- 实体和关系
- 属性和域
- 可以在数据库中强制执行的业务规则
- 需要使用数据库的业务过程
(三)逻辑阶段
逻辑阶段的主要工作是绘制E-R图,或者说是建模。建模工具很多,有不同的图形表示方法和软件。这些工具和软件的使用并非关键,笔者也不建议读者花大量时间在建模方法的选择上。对于大多数应用来说,E-R图足以描述实体间的关系。建模关键是思想而不是工具,软件只是起到辅助作用,识别实体关系才是本阶段的重点。
除了实体关系,我们还应该考虑属性的域(值类型、范围、约束)
最近在给一家客户做系统维护时,需要查看数据库表的大小,相关的sql如下:
1. exec sp_spaceused '表名' --(SQL统计数据,大量事务操作后可能不准)
2. exec sp_spaceused '表名', true --(准确的表空间大小,但可能会花些统计时间)
3. exec sp_spaceused -- (数据库大小查询)
4. exec sp_MSforeachtable "exec sp_spaceused '?'" --(所有用户表空间表小,SQL统计数据,,大量事务操作后可能不准)
5. exec sp_MSforeachtable "exec sp_spaceused '?',true" --(所有用户表空间表小,大数据库慎用)
本文链接
群里面有位网友提出了这样一个SQL挑战问题(原话不是这样,为了说明问题,我略做调整些):
问题说明如下
有一条个销售报表TEST :
商品 金额
A 1400
B 800
C 790
... ...
现在有这样一个需求(要写一个SQL取出如下数据):先按销售金额倒叙排序,然后从这个报表取出前N条记录,并且这N条记录的销售金额之和是总金额的80%(<= 80%), 80%将作为一个查询条件(有可能是20%,30%).他的想法是用嵌套函数 递归做法记录砍掉一半判断是否达到百分比 如果不足再取后半部分的一半 类似这样的方法;还有就是逐行金额相加当金额到达80%的时候 记录就从头显示到该条的前一条。 现在他想有没有其他跟高效的方法来实现这个需求?
那么我看到这个问题的时候,觉得有嵌套函数递归处理的方法的效率肯定是最低的,毕竟SQL不擅长于做逻辑处理。那么有没有更好的方法来实现呢?大家在看后面方法的时候,可以思考一下有没有更好的方法,能够达到一击必杀的效果(一个SQL搞定)。欢迎大家探讨!
下面是我的一个解决思路和方法,我在按销售金额倒叙排列的时候,能否得到一个销售金额的累加值?然后把这个累加值除以销售总金额即得到累加金额占总金额的一个比例,然后我们再加上查询条件。即可解决问题。
新建一个测试表TEST
(
PRODUCT_NAME CHAR(32), --商品名称
SALE_AMOUNT FLOAT --销售金额
)
插入测试数据
SELECT 'A' ,13000 UNION ALL
SELECT 'A' ,12000 UNION ALL
SELECT 'A' ,9000 UNION ALL
SELECT 'B' ,167000 UNION ALL
SELECT 'B' ,137000 UNION ALL
SELECT 'B' ,107000 UNION ALL
SELECT 'C' ,78000 UNION ALL
SELECT 'C' ,12000;
实现销售金额的累加值字段的脚本(这个脚本效率没有测试,小量数据应该没有问题)
T.SALE_AMOUNT,
(SELECT SUM(SALE_AMOUNT) AS ACCUMATE_SALE FROM TEST WHERE T.SALE_AMOUNT <= SALE_AMOUNT ) AS ACCUMATE_SALE
FROM TEST T
(截图)
那么接下来我们来实现上面的思路
T.PRODUCT_NAME,
T.SALE_AMOUNT,
(SELECT SUM(SALE_AMOUNT) SUM_SALE
FROM TEST
WHERE T.SALE_AMOUNT <= SALE_AMOUNT) / L.SALE_AMOUNT AS SUM_RAT
FROM TEST T, (SELECT SUM(SALE_AMOUNT) AS SALE_AMOUNT FROM TEST) L
截图
接下来就水到渠成了
(
SELECT ROW_NUMBER() OVER(ORDER BY T.SALE_AMOUNT DESC) AS ROW,
T.PRODUCT_NAME,
T.SALE_AMOUNT,
(SELECT SUM