数据库设计---关于建表的时候选择横表和竖表(纵表)的一点思考

2023-10-29 21:20

本文主要是介绍数据库设计---关于建表的时候选择横表和竖表(纵表)的一点思考,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在做数据统计类数据库设计的时候,在考虑数据存储的时候,经常会遇到逻辑上同一个BusinessID对应多个数据点的情况,
比如工资表中的员工ID以及各项工资信息,财务表中的各个报表Id和多个数据点之间的信息
面对这种情况,如何来设计表结构,是横表,还是竖表,各有那些优缺点,本文将做一个粗浅的分析。

 本文出处:http://www.cnblogs.com/wy123/p/6677073.html 

横标和竖表的表现形式

日常生活中也有很多类似的例子,先用一个Excel画一个例子,比如工资表
这么做就是“横表”,特点是,一个ID对应所有的值信息,以行Key-Value1-Value2-Value3的方式存储

如下是竖表(纵表),特点是每行仅存储该ID的某一个类别字段的值,以行的方式存储Key-Value的方式存储

 

横标和竖表的设计示例

  下面通过一个具体的例子来说明横标和竖表的一些特点

复制代码
--横标
CREATE TABLE HorizontalTable
(Id                int identity(1,1),BusinessId        varchar(50)         ,CategoryVal1    varchar(20)         ,CategoryVal2    decimal(20,5)     ,CategoryVal3    datetime         ,CategoryVal4    varchar(20)         ,CategoryVal5    varchar(20)         ,CategoryVal6    varchar(20)
)
insert into HorizontalTable  values ('BH000001','value1',89.12,'20170406','abc4','abc5','abc6')
insert into HorizontalTable  values ('BH000002','value2',99.11,'20170407','abc4','abc5','abc6')--竖表
CREATE TABLE VerticalTable
(Id                int identity(1,1),BusinessId        varchar(50),CategoryKey        varchar(20),Val                varchar(20)
)
insert into VerticalTable values ('BH000001','CategoryKey1','values1')
insert into VerticalTable values ('BH000001','CategoryKey2',89.12)
insert into VerticalTable values ('BH000001','CategoryKey3','20170406')
insert into VerticalTable values ('BH000001','CategoryKey4','abc4')
insert into VerticalTable values ('BH000001','CategoryKey5','ab5')
insert into VerticalTable values ('BH000001','CategoryKey6','ab6')
insert into VerticalTable values ('BH000002','CategoryKey1','values2')
insert into VerticalTable values ('BH000002','CategoryKey2',99.12)
insert into VerticalTable values ('BH000002','CategoryKey3','20170407')
insert into VerticalTable values ('BH000002','CategoryKey4','abc4')
insert into VerticalTable values ('BH000002','CategoryKey5','abc5')
insert into VerticalTable values ('BH000002','CategoryKey6','abc6')
复制代码

横表中的数据:

竖表中的数据

  

可能实际应用中,要比这个示例中的情况更加复杂,那么在设计表结构的时候,如何选择横标或者竖表?
首先来看横标的特点

对于横表
  1,同一个Key值对应的列是固定的,比如,比如HorizontalTable中有6个字段
  2,各个字段的值是自由的,比如HorizontalTable中的CategoryVal1是varchar类型的,CategoryVal2是decimal的
  3,表中并不存储描述性字段本身(相比纵表)
  4,相比竖表,存储同样多的数据,行数要少
对于竖表
  1,同一个Key值对应的列是动态的,因为是按照行存储的,可以存储成Key1—Value1,Key1—Value2,Key1—Value3的方式存储
  2,字段的类型是固定的,但是类似是要兼容的,不能有个性化的字段,比如VerticalTable中的CategoryKey+Val,因为固定了这么一个字段
  3,表中需要存储描述字段本身(相比横标),要根据BusinessKey值的不同,重复存储CategoryKey
  4,相比横表,存储同样多的数据,行数要多

综上可以看出,
  横标的优点:横标的有点事显示的较为清晰直观,同时在字段的选择上更为科学合理,具体的字段可以根据具体情况划分字段类型,
  横标的缺点:不方便扩展和公用,也就是说设计了一张横标,只能在固定的某一种特定的相对不变的场景下使用,
        比如加字段,或者类似的业务想公用一张横表,都有局限

  竖表的优点:最大的特点是可以灵活扩展存储的内容,同时具有一定的公用性
        因为竖表的存储结构不受字段个数的限制,可以存储具有一定共性的业务数据。
  竖表的缺点:竖表的字段类型要兼容,比如横标可以根据具体的值设计成varchar,decimal,datetime等,
        横标为了兼容以上字段类型,只能设计成varchar的,可能会浪费一定的空间

 

  横标和竖表主要考虑的是扩展性和共同性,对于显示方式问题,个人认为倒是问题不大,无非是行转列和列转行的问题
  如下是一个将上述设计的横表转竖表和竖表转横标的示例,也不复杂,因此说,显示的问题不是大问题

复制代码
select * from HorizontalTable
--列转行
;WITH HorizontalCET
AS
(SELECT Id,BusinessId,CategoryVal1,cast(CategoryVal2 as varchar(20)) as CategoryVal2,cast(CategoryVal3 as varchar(20)) as CategoryVal3,CategoryVal4,CategoryVal5FROM HorizontalTable
)
SELECT Id,BusinessId,ColumnName,ColumnVal
FROM HorizontalCET
UNPIVOT (ColumnVal FOR ColumnName IN (CategoryVal1,CategoryVal2,CategoryVal3,CategoryVal4,CategoryVal5)) tmp--列转行
select * from VerticalTable
SELECT * FROM 
(select BusinessId ,CategoryKey,Val from VerticalTable
)tPIVOT( MIN(Val) FOR CategoryKey IN (CategoryKey1,CategoryKey2,CategoryKey3,CategoryKey4,CategoryKey5,CategoryKey6)
)a
复制代码

  

 

关于横表和竖表的性能问题

  关于性能问题,很难一概而论,还要结合具体的情况作分析,比如查询方式,查询数据了,索引结构等等都有一定的关系。
  表面上看,竖表存储了大量冗余的数据,浪费了一定量的磁盘空间是事实,但是极端情况下横表也有可能造成极大的空间浪费
  了解SQL Server的同学肯定知道,
  SQL Server中正常来来说是行存储,一行数据不能跨页存储(当然forwarded存储方式的数据除外,有机会说这个),
  SQL Server的最小存储单位是页(Page),一个页的大小是8kb,除去page信息固定占用的空间之外是8060个字节,
  每一行固定的一行数据除了数据自身占用的空间外,至少(不是一定,表结构越复杂占用的额外空间越大)还要占用1+1+2+2+1=7个字节

  对于宽表,一旦字段长度达到一定的程度,
  比如每行长度为800个字节,理论上将,在一个page上,存储9行记录之后,还剩余800字节的空间(具体剩余多少跟表结构有关,这里只是举例说明),
  对不起,第十行数据来了已经存不进去了,只能新开页面分配存储空间,这样,当前这个页面就浪费了800字节的存储空间
  反观竖表,因为存储的数据行都非常短,即便发生上述情况,也只会浪费很少的一点数据空间(小于一行数据的空间)
  极端情况下会更加有意思,参考这个http://www.cnblogs.com/studyzy/archive/2008/11/27/1342003.html

   

  从读取的另外角度来看,大多数情况下,建表的方式都是行存储,意味着每一行的数据是存储在一起的(字段大的时候当然可以跨页面),单个页面存储的数据行数就变得较少
  sqlserver读取数据的时候是按照行来读取的,不管你查询几个字段,最终都是要将整个行的数据读取出来,
  而存储的最小单元是页,也就是page,
  如果一个表的字段非常多,那么一次查询,即便是需要这些字段中的一部分,也要将所有的字段读取出来,这意味着,你读取的行数多的话,读取出来的不需要的字段也将变得更多
  比如一个表中设计了30个字段,正常情况下,一个查询只需要读取6个字段,即便是这样,sqlserver在执行查询的时候依然读取的是30个字段出来,
  这样的的话,读取同样多的数据,就可能需要读取更多的页才能完成这个查询
  如果是设计成竖表,根据具体的Id来,如果有合理的索引,使用索引就可以完成查询,而不需要再去读表的page,这样就可以避免横表读取时候的这种情况。

  有上述可见,对于横表和竖表,不管是设计上还是存储上,优点和缺点都是看站在哪个角度来看的,
  从一个角度来看是有点,从另外一个角度看就可能会变成缺点,只有舍弃一部分,根据实际情况权衡之后做出取舍。
  凡事无绝对,适合即可。

 

总结:

  本文从适应场景、存储、性能等方面粗浅第分析了表设计时候横标和竖表的特点和优缺点,
  具体设计的时候可综合考虑,做出合理的选择。
  另外,本文肯定还有没有预计或者说想到的情况以及评估方向,也希望有想法的同学补充,谢谢。

这篇关于数据库设计---关于建表的时候选择横表和竖表(纵表)的一点思考的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/303409

相关文章

MySQL数据库中ENUM的用法是什么详解

《MySQL数据库中ENUM的用法是什么详解》ENUM是一个字符串对象,用于指定一组预定义的值,并可在创建表时使用,下面:本文主要介绍MySQL数据库中ENUM的用法是什么的相关资料,文中通过代码... 目录mysql 中 ENUM 的用法一、ENUM 的定义与语法二、ENUM 的特点三、ENUM 的用法1

Java中调用数据库存储过程的示例代码

《Java中调用数据库存储过程的示例代码》本文介绍Java通过JDBC调用数据库存储过程的方法,涵盖参数类型、执行步骤及数据库差异,需注意异常处理与资源管理,以优化性能并实现复杂业务逻辑,感兴趣的朋友... 目录一、存储过程概述二、Java调用存储过程的基本javascript步骤三、Java调用存储过程示

Go语言数据库编程GORM 的基本使用详解

《Go语言数据库编程GORM的基本使用详解》GORM是Go语言流行的ORM框架,封装database/sql,支持自动迁移、关联、事务等,提供CRUD、条件查询、钩子函数、日志等功能,简化数据库操作... 目录一、安装与初始化1. 安装 GORM 及数据库驱动2. 建立数据库连接二、定义模型结构体三、自动迁

嵌入式数据库SQLite 3配置使用讲解

《嵌入式数据库SQLite3配置使用讲解》本文强调嵌入式项目中SQLite3数据库的重要性,因其零配置、轻量级、跨平台及事务处理特性,可保障数据溯源与责任明确,详细讲解安装配置、基础语法及SQLit... 目录0、惨痛教训1、SQLite3环境配置(1)、下载安装SQLite库(2)、解压下载的文件(3)、

MySQL数据库的内嵌函数和联合查询实例代码

《MySQL数据库的内嵌函数和联合查询实例代码》联合查询是一种将多个查询结果组合在一起的方法,通常使用UNION、UNIONALL、INTERSECT和EXCEPT关键字,下面:本文主要介绍MyS... 目录一.数据库的内嵌函数1.1聚合函数COUNT([DISTINCT] expr)SUM([DISTIN

MySQL追踪数据库表更新操作来源的全面指南

《MySQL追踪数据库表更新操作来源的全面指南》本文将以一个具体问题为例,如何监测哪个IP来源对数据库表statistics_test进行了UPDATE操作,文内探讨了多种方法,并提供了详细的代码... 目录引言1. 为什么需要监控数据库更新操作2. 方法1:启用数据库审计日志(1)mysql/mariad

postgresql数据库基本操作及命令详解

《postgresql数据库基本操作及命令详解》本文介绍了PostgreSQL数据库的基础操作,包括连接、创建、查看数据库,表的增删改查、索引管理、备份恢复及退出命令,适用于数据库管理和开发实践,感兴... 目录1. 连接 PostgreSQL 数据库2. 创建数据库3. 查看当前数据库4. 查看所有数据库

从入门到精通MySQL 数据库索引(实战案例)

《从入门到精通MySQL数据库索引(实战案例)》索引是数据库的目录,提升查询速度,主要类型包括BTree、Hash、全文、空间索引,需根据场景选择,建议用于高频查询、关联字段、排序等,避免重复率高或... 目录一、索引是什么?能干嘛?核心作用:二、索引的 4 种主要类型(附通俗例子)1. BTree 索引(

Oracle 数据库数据操作如何精通 INSERT, UPDATE, DELETE

《Oracle数据库数据操作如何精通INSERT,UPDATE,DELETE》在Oracle数据库中,对表内数据进行增加、修改和删除操作是通过数据操作语言来完成的,下面给大家介绍Oracle数... 目录思维导图一、插入数据 (INSERT)1.1 插入单行数据,指定所有列的值语法:1.2 插入单行数据,指

k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)

《k8s上运行的mysql、mariadb数据库的备份记录(支持x86和arm两种架构)》本文记录在K8s上运行的MySQL/MariaDB备份方案,通过工具容器执行mysqldump,结合定时任务实... 目录前言一、获取需要备份的数据库的信息二、备份步骤1.准备工作(X86)1.准备工作(arm)2.手