数据仓库表设计理论

数据仓库表设计理论

数仓顾名思义是数据仓库,其数据来源大多来自于业务数据(例如:关系型数据库),当设计数仓中表类型时(拉链表、增量表、全量表、流水表、切片表)时,应先观察业务数据的特点再设计数仓表结构

首先业务数据是会不断增长的-即增量,而在不断增长的前提下业务数据又可以分为两类:

  • 增量更新数据源:数据源允许新增、修改和删除操作的数据源
  • 增量非更新数据源:数据源只允许新增数据,不允许对历史数据进行修改的数据源

业务数据中的这两种数据源类型直接决定了数仓中的表设计的选择

一、增量更新数据源

增量更新数据源是指允许新增、修改和删除操作的数据源。这种数据源的主要特点是:

  1. 数据可修改:可以对历史数据进行修改、覆盖,以反映数据的变更。
  2. 实时性高:由于数据可以随时更新,具有很强的实时性和即时性。

常见的增量更新数据源包括关系型数据库、NoSQL数据库、文件系统等,它们都支持对数据进行新增、修改和删除操作。

示例:关系型数据库中的用户表,其手机号字段经常会出现修改

在这里插入图片描述

在2017-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:

![ref1]

针对这类数据源,我们将分别阐述下数仓中的各种类型的表设计

1.1、全量表

全量表就是存储了全部数据的表,没有分区之分,可以理解为总共就一个分区。

全量表中存储了截至目前为止最新状态的全部记录,这就表示会存在历史状态的更新。

1.1.1、场景

以业务数据-用户表为例,2020-06-01有三个用户注册,表如下:

![ref1]

2020-06-02有一名用户注册,即新增了一名用户(标红),此时数仓中全量表更新后会记录全量的数据,此时数据表如下:

在这里插入图片描述

1.1.2、实现方式

全量表的实现方式又分为两种:

  • 全量替换:直接获取全表业务数据替换昨日全量单表
  • join替换:通过合并:每日增改数据的临时表 或 流水表,重写后得到全新全量表
1.1.2.1、全量替换
-- 创建一张新的全量单表
CREATE TABLE new_table (col1 STRING, col2 INT)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/user/hive/warehouse/new_table';

-- 将全部的业务数据导入至全量单表, 过程不赘述
insert into new_table ..........

-- 将新表名替换为旧表名
ALTER TABLE old_table RENAME TO old_table_backup;
ALTER TABLE new_table RENAME TO old_table;

-- 删除昨日全量单表
DROP TABLE old_table_backup;
1.1.2.2、join替换

注意:业务数据需要有唯一的ID 和 upadte_time,否则无法使用join替换。

-- 创建一张新的临时增量表
CREATE TABLE incremental_table (id INT, column1 STRING, column2 STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/user/hive/warehouse/incremental_table';

-- 将增量数据加载到一个临时表中,一般是通过业务数据的update_time来区分哪些是增量数据
insert into incremental_table xxxx

-- 使用LEFT JOIN语句将原表和增量数据合并
SELECT COALESCE(i.id, o.id) as id,
       COALESCE(i.column1, o.column1) as column1,
       COALESCE(i.column2, o.column2) as column2,
       ...
FROM original_table o
LEFT JOIN incremental_table i
ON o.id = i.id;

-- 这将生成一个包含原表和增量数据的JOIN结果。在上面的例子中,我们使用了COALESCE函数来选择非空值,以便我们可以从原表和增量数据中选择最新的值。
-- 最后,我们可以使用INSERT OVERWRITE语句将合并后的数据插入到原表中
INSERT OVERWRITE TABLE original_table
SELECT COALESCE(i.id, o.id) as id,
       COALESCE(i.column1, o.column1) as column1,
       COALESCE(i.column2, o.column2) as column2,
       ...
FROM original_table o
LEFT JOIN incremental_table i
ON o.id = i.id;

总结:全量表设计的优势是不会占用太多磁盘空间;弊端也很明显-不支持历史记录溯源

1.2、增量表

增量表是指只负责追加新的数据记录,而不负责历史数据更改记录,新的数据记录保存在新分区中,历史分区中的数据记录不发生变化。

增量表并不适用增量更新数据源,只适用于增量非更新数据源。

1.3、快照表

快照表是用来存储某个时间点的所有数据-通常粒度是天,相当于是对每天的业务数据做了一次快照,存储当天的全量数据!

  • 例如:快照表中某个分区内的数据是历史到此分区前一天的所有数据,如12号分区中的数据是从历史到11号的所有数据,13号分区中的数据是从历史到12号的所有数据,其他的以此类推。

1.3.1、场景

也可以理解为:每天将业务数据的全量数据存储至数仓-快照分区表的当天分区内,这里假设分区的粒度是天

以业务数据-用户表为例,2020-06-01有三个用户注册,表如下:

在这里插入图片描述

2020-06-02有一名用户注册,即新增了一名用户(标红),此时数仓中快照分区表更新后2020-06-02分区内会记录全量的数据,包括2020-06-01的用户数据(标绿),此时快照表如下:

在这里插入图片描述

同理,2020-06-03又有2名用户注册,即新增了1名用户(标蓝),此时数仓中快照分区表更新后2020-06-03分区内会记录全量数据,即包含2020-06-02的用户数据(标黄),此时快照表如下:

在这里插入图片描述

1.3.2、实现

先将业务数据全部同步至hive临时表中,随后将hive临时表的业务数据放置在快照分区表的今日分区内:

-- 创建一个新hive临时表,存放当日业务的全量数据
CREATE TABLE temp_table (col1 STRING, col2 INT)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t'
LOCATION '/user/hive/warehouse/temp_table';

-- 将全部的业务数据导入至hive临时表, 过程不赘述
insert into temp_table ..........

-- 使用INSERT INTO语句将临时表中的数据插入到全量分区表的目标分区中:
INSERT INTO original_table PARTITION (date='2023-05-08')
SELECT * FROM temp_table;

-- 删除hive临时表
DROP TABLE temp_table;

总结:此方式便是快照表,该设计的弊端很明显:会大量占用磁盘空间,故并不推荐使用

1.4、流水表

流水表是用于记录数据变更的表,理论上是对于表的每一个修改都会记录,但在实际应用中通常按天为粒度划分,例如流水表中的2017-01-02分区只记录这一天新增和修改的业务数据,这样可以方便地追溯、计算和分析历史数据,同时也提供了可靠的数据源供其他表和报告使用。

1.4.1、场景

还是用user用户数据举例,2017-01-01这一天表中的数据是:

![ref1]

此时流水表的数据为:

注册日期用户编号手机号码dt-时间分区字段
2017-01-010011111112017-01-01
2017-01-010022222222017-01-01
2017-01-010033333332017-01-01
2017-01-010044444442017-01-01

在2017-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:

在这里插入图片描述

此时流水表的数据为:

注册日期用户编号手机号码dt-时间分区字段
2017-01-010011111112017-01-01
2017-01-010022222222017-01-01
2017-01-010033333332017-01-01
2017-01-010044444442017-01-01
2017-01-010022333332017-01-02
2017-01-010044324322017-01-02
2017-01-020055555552017-01-02

1.4.2、实现

-- 创建一个user_update_temp临时表,用于存放当日业务的增量数据
CREATE EXTERNAL TABLE ods.user_update_temp (
    user_num STRING COMMENT '用户编号', 
    mobile STRING COMMENT '手机号码', 
    reg_date STRING COMMENT '注册日期'
)
COMMENT '每日用户资料更新表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user_update';

-- 创建一张流水表,用来存储每天变化数据
CREATE EXTERNAL TABLE dws.user_stream (
    user_num STRING COMMENT '用户编号', 
    mobile STRING COMMENT '手机号码', 
    reg_date STRING COMMENT '注册日期'
)
COMMENT '用户流水表'
PARTITIONED BY (dt STRING)
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/dws/orders';

-- 通过业务字段update_time将2017-01-02变化的业务数据导入至user_update_temp临时表,过程不赘述
INSERT OVERWRITE ods.user_update_temp .........

-- 使用INSERT INTO语句将ods.user_update中的数据插入到流水表中2017-01-02分区中:
INSERT INTO dws.user_stream PARTITION (dt='2017-01-02')
SELECT * FROM ods.user_update_temp;

注意:流水表很容易和增量表的概念混淆,这里再强调一下:增量表只适用于增量非更新数据源,只负责新增数据,对于历史数据的修改并不记录;而流水表通常用于记录数据变更,包括新增、修改和删除等操作,以便跟踪每个事实或维度的历史变化。为了方便管理和查询,通常将流水表按时间分区。

1.5、拉链表

拉链表是一种维护历史状态以及最新状态数据的表。与快照表类似,算是在快照表的基础上去除了重复状态的数据,也就是一些不变的信息在快照表中每个分区都存储一份,使用拉链表在更新频率和比例不是很大的情况下会十分节省存储。

1.5.1、场景

现在以用户的拉链表来说明2017-01-01这一天表中的数据是:

在这里插入图片描述

在2017-01-02这一天表中的数据是, 用户002和004资料进行了修改,005是新增用户:

![ref1]

在2017-01-03这一天表中的数据是, 用户004和005资料进行了修改,006是新增用户:

![ref1]

如果在数据仓库中设计成历史拉链表保存该表,则会有下面这样一张表,这是最新一天(即2017-01-03)的数据:

![ref1]

说明

  • t_start_date表示该条记录的生命周期开始时间,t_end_date表示该条记录的生命周期结束时间
  • t_end_date = '9999-12-31’表示该条记录目前处于有效状态
  • t_end_date = '2017-01-02’表示该条记录在2017-01-02当日是有效的,在当前日期是无效的
  • 如果查询当前所有有效的记录,则select * from user where t_end_date = ‘9999-12-31’
  • 如果查询2017-01-02的历史快照,则select * from user where t_start_date <= ‘2017-01-02’ and t_end_date >= ‘2017-01-02’。(此处要好好理解,是拉链表比较重要的一块)
  • 解释上一条sql:需求是要查2017-01-02的历史快照,t_start_date是代表这条记录的开始时间,并非是原始数据的时间,例如001用户数据在2017-01-02也有效,故t_start_date <= ‘2017-01-02’;而t_end_date = '2017-01-02’表示该条记录在2017-01-02当日是有效的,又因为t_end_date = '9999-12-31’表示该条记录目前处于有效状态,所以t_end_date >= ‘2017-01-02’

该sql查询结果如下:

![ref1]

和下图2017-01-02的业务数据比较,结果完全一致:

![ref1]

1.5.2、实现

创建拉链表的前提是先根据全量数据表创建初始拉链表,然后再根据每天的增改数据进行合并更新拉链表;

还是以上面的用户表为例,我们要实现用户的拉链表,在实现它之前,我们需要先确定一下我们有哪些数据源可以用。

  1. 我们需要一张ODS层的用户全量表。至少需要用它来初始化。
  2. 流水表-记录每日增改数据。

建表语句:

-- 先创建一张全量表用于初始化
CREATE EXTERNAL TABLE ods.user (
  user_num STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  reg_date STRING COMMENT '注册日期'
) COMMENT '用户资料表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/ods/user';

-- 最后我们创建一张拉链表:
CREATE EXTERNAL TABLE dws.user_his (
  user_num STRING COMMENT '用户编号',
  mobile STRING COMMENT '手机号码',
  reg_date STRING COMMENT '注册日期',
  t_start_date STRING COMMENT '资料开始日期',
  t_end_date STRING COMMENT '资料结束日期'
) COMMENT '用户资料拉链表'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' LINES TERMINATED BY '\n'
STORED AS ORC
LOCATION '/dws/user_his';

数据初始化:我们以2017-01-01的数据作为初始化数据

-- 假设ods.user表已经存储了2017-01-01的全量数据,此时拉链表的初始化sql:
INSERT OVERWRITE TABLE dws.user_his
SELECT user_num, mobile, reg_date, '2017-01-01', '9999-12-31'
FROM ods.user;

初始化后的拉链表数据如下:

注册日期用户编号手机号码t_start_datet_end_date
2017-01-010011111112017-01-019999-12-31
2017-01-010022222222017-01-019999-12-31
2017-01-010033333332017-01-019999-12-31
2017-01-010044444442017-01-019999-12-31

数据更新:我们以2017-01-02的数据更新拉链表;

2017-01-02日的流水表数据如下:

注册日期用户编号手机号码dt-时间分区字段
2017-01-010011111112017-01-01
2017-01-010022222222017-01-01
2017-01-010033333332017-01-01
2017-01-010044444442017-01-01
2017-01-010022333332017-01-02
2017-01-010044324322017-01-02
2017-01-020055555552017-01-02
-- 2017-01-02拉链表更新sql:
INSERT OVERWRITE TABLE dws.user_his
SELECT *
FROM
(
    SELECT A.user_num,
           A.mobile,
           A.reg_date,
           A.t_start_date,
           CASE
                WHEN A.t_end_date = '9999-12-31' AND B.user_num IS NOT NULL THEN '2017-01-01'
                ELSE A.t_end_date
           END AS t_end_date
    FROM dws.user_his AS A
    LEFT JOIN dws.user_stream AS B WHERE dt = '2017-01-02'
    ON A.user_num = B.user_num
UNION
    SELECT C.user_num,
           C.mobile,
           C.reg_date,
           '2017-01-02' AS t_start_date,
           '9999-12-31' AS t_end_date
    FROM dws.user_stream AS C WHERE dt = '2017-01-02'
) AS T;

更新后的拉链表数据如下:

注册日期用户编号手机号码t_start_datet_end_date
2017-01-010011111112017-01-019999-12-31
2017-01-010022222222017-01-012017-01-01
2017-01-010033333332017-01-019999-12-31
2017-01-010044444442017-01-012017-01-01
2017-01-010022333332017-01-029999-12-31
2017-01-010044324322017-01-029999-12-31
2017-01-02005555552017-01-029999-12-31
-- 查询有效数据:
select * from dws.user_his where t_end_date = '9999-12-31'

查询有效数据结果:

注册日期用户编号手机号码t_start_datet_end_date
2017-01-010011111112017-01-019999-12-31
2017-01-010033333332017-01-019999-12-31
2017-01-010022333332017-01-029999-12-31
2017-01-010044324322017-01-029999-12-31
2017-01-02005555552017-01-029999-12-31
-- 查询2017-01-01历史数据: 
select * from dws.user_his where t_start_date <= '2017-01-01' and t_end_date >= '2017-01-01'

2017-01-01历史数据:

注册日期用户编号手机号码t_start_datet_end_date
2017-01-010011111112017-01-019999-12-31
2017-01-010022222222017-01-012017-01-01
2017-01-010033333332017-01-019999-12-31
2017-01-010044444442017-01-012017-01-01

1.6、切片表

切片表根据基础表,往往只反映某一个维度的相应数据。其表结构与基础表结构相同,但数据往往只有某一维度,或者某一个事实条件的数据;切片表以某个维度或者一些特定的条件对事实进行汇总计算,并展示为一个交叉分析的表格。与事实表相比,切片表的数据更加聚合,只包含某些维度或者满足某些特定条件的数据。

1.6.1、场景

假设我们有一个基础表(也称为事实表),记录了一家公司的销售订单信息。该表包含以下字段:订单ID、客户ID、产品ID、销售日期、销售数量和销售额等。

订单ID客户ID产品ID销售日期销售数量销售额
1100120012022-01-013150
2100220022022-01-02280
3100320012022-01-03150
4100120032022-01-045250
5100220022022-01-054160

在这张表中,客户端ID、产品ID、销售日期是维度,而销售数量、销售额是事实。

现在我们希望按照客户维度创建一个切片表,以便分析每个客户的销售情况。

具体来说,我们需要选择客户维度,并对销售数量和销售额这两个度量进行聚合计算,通过多维分析工具或者SQL查询,可以生成如下的切片表:

客户ID销售数量总计销售额总计
10018400
10026240
1003150

在这个切片表中,我们只选择了客户维度,然后,我们使用SUM函数对每个客户的销售数量和销售额进行聚合计算,以便更好地分析不同客户之间的销售情况。

1.6.2、实现

在数据仓库中,切片表的存储方式可以根据不同的需求和性能要求而定,一般来说有以下两种常用的存储方式:

  1. 全量替换:每次运行ETL作业时,都会重新生成整个切片表,并将其覆盖原有的数据。这种方式适用于数据量较小、更新频率低的场景,或者需要保证数据完整性和一致性的场景。
  2. 按照天的粒度进行划分:将切片表按照时间维度(如日、周、月等)进行分区,每个分区存储一段时间内的数据。这种方式适用于数据量较大、更新频率高的场景,或者需要快速查询历史数据的场景。

无论采用哪种存储方式,都需要考虑切片表的设计问题。具体来说,以下是一些设计上的注意点:

  1. 维度表的设计:维度表应该是离线化的静态数据,它们应该是变化不大并且计算时被缓存在内存中,以便提高查询性能。因此,在设计维度表时,应该尽可能地避免经常变化的属性。
  2. 表结构的优化:为了提高查询效率,应该尽可能地减少JOIN操作,尽量将不同维度的数据存储在同一个表中。如果必须进行JOIN操作,则应该尽可能地使用分区表或者基于列存储的数据库,以便提高查询性能。
  3. 时间分区:如果采用按照天的粒度进行划分的方式,那么需要将切片表按照时间维度进行分区,并且使用分区键作为查询条件。这样可以大大提高查询性能,同时也方便进行数据备份和恢复。

总之,在设计和实现切片表时,需要考虑不同的因素,包括数据量、更新频率、查询性能、数据一致性等等,以便得出最优的解决方案。

这里以全量替换举例:

-- 按照上面的例子,假设有一个名为sales_order的基础表
CREATE TABLE sales_order (
  order_id int,
  customer_id int,
  product_id int,
  sale_date date,
  quantity int,
  amount double
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ','
STORED AS TEXTFILE;

-- 我们可以将原始数据加载到sales_order表中
LOAD DATA INPATH 'path/to/sales.csv' OVERWRITE INTO TABLE sales_order;

-- 接下来,我们可以使用INSERT INTO SELECT语句来创建切片表customer_sales,并将数据按照客户维度进行聚合计算:
CREATE TABLE customer_sales (
  customer_id int,
  total_quantity int,
  total_amount double
)
AS
SELECT 
  customer_id, 
  SUM(quantity) as total_quantity, 
  SUM(amount) as total_amount 
FROM sales_order 
GROUP BY customer_id;

-- 这里采用了全量替换的方式,每次重新运行上面的SQL语句都会完全替换customer_sales表中的数据。

二、增量非更新数据源

当数据源只允许新增数据,不允许对历史数据进行修改时,我们称之为增量不更新数据源。这种数据源的主要特点是:

  1. 数据只能增长:无法对历史数据进行修改、删除或覆盖,只能追加新的数据。
  2. 数据变更历史保留:由于数据源只追加新的数据,因此可以完整地保留数据的变更历史,便于后续的分析和回溯。
  3. 数据量大:由于数据只能增长,因此数据量通常会随着时间的推移而不断增加。
  4. 实时性高:由于数据是实时生成的,具有很强的实时性和即时性。

常见的增量不更新数据源包括:

  1. 日志数据:日志数据是指记录了系统或应用程序操作的信息的数据。由于日志数据一般是只追加不修改,所以可以视为增量不更新数据源。
  2. 传感器数据:传感器数据是指由各种传感器采集的环境、设备或系统状态数据等。这些数据也是只追加不修改的,因此可以视为增量不更新数据源。
  3. 消息系统数据:消息系统数据是指通过消息队列、主题或流处理框架等收集的异步消息数据。与日志数据和传感器数据类似,这些数据也是只追加不修改的。
  4. 用户活动数据:用户活动数据是指记录了用户在应用程序中进行的各种操作和行为的数据。由于这些数据通常无法修改,因此也可以视为增量不更新数据源。

在设计数据模型和存储方案时,需要考虑到数据源的特点,以便满足快速查询和分析的需求,一般来说,可以采用以下策略:

  1. 使用列式存储:由于增量不更新数据源的数据一般只用于查询和分析,因此可以采用列式存储方式,以提高查询性能。
  2. 考虑分区:根据数据源的特点,可以将数据按照时间、地理位置等因素进行分区,以便更快地查询历史数据。
  3. 超大表设计:由于增量不更新数据源的数据量很大,因此需要考虑超大表的设计问题。例如,可以使用分区表、分布式存储等技术来支持数据的高效存储和查询。

2.1、全量表

对于增量不更新数据源,使用全量表进行处理可能会导致性能问题和资源浪费,因为全量表需要重新加载所有数据,并进行完全替换,这将消耗大量的计算资源和时间。

相反,使用增量表可以更有效地处理增量不更新数据源,因为增量表只包含最近追加的数据,并且每次只需要更新最新的增量数据即可。这样,可以避免重复加载和处理历史数据,从而提高处理效率和减少资源消耗。

2.2、增量表

增量表可以高效地处理增量不更新数据源,因为增量表只包含最新的新增数据,并且每次只需要更新最新的增量数据即可。这样,可以避免重复加载和处理历史数据,从而提高处理效率和减少资源消耗。

增量表通常按天为粒度

2.2.1、场景

假设我们有一个传感器监测系统,每个传感器会在固定时间间隔内生成一些数据。这些数据在产生后不会被更改。我们需要设计一个增量表来处理这些传感器数据,并按天分区管理数据。

原始数据格式展示:

字段名称数据类型说明
sensor_idINT传感器ID
timestampTIMESTAMP时间戳
valueFLOAT数据值

14号原始数据:

sensor_idtimestampvalue
12023-05-14 10:00:0023.5
22023-05-14 10:00:0018.2
32023-05-14 10:00:0025.0
42023-05-14 11:00:0024.0
52023-05-14 11:00:0018.4

此时我们设计一个名为sensors_incremental的增量表,用于存储每天新增的传感器数据,并按照日期进行分区。该表包含了四个字段:sensor_id、timestamp、value和dt(时间分区字段)。

增量表初始数据展示如下:

sensor_idtimestampvaluedt(时间分区字段)
12023-05-14 10:00:0023.52023-05-14
22023-05-14 10:00:0018.22023-05-14
32023-05-14 10:00:0025.02023-05-14
42023-05-14 11:00:0024.02023-05-14
52023-05-14 11:00:0018.42023-05-14

第二天原始数据如下:

sensor_idtimestampvalue
12023-05-14 10:00:0023.5
22023-05-14 10:00:0018.2
32023-05-14 10:00:0025.0
32023-05-14 11:00:0024.0
52023-05-14 11:00:0018.4
62023-05-15 11:00:0025.2
72023-05-15 12:00:0023.8
82023-05-15 12:00:0018.6
92023-05-15 12:00:0025.5

第二天增量表sensors_incremental数据:

sensor_idtimestampvaluedt(时间分区字段)
12023-05-14 10:00:0023.52023-05-14
22023-05-14 10:00:0018.22023-05-14
32023-05-14 10:00:0025.02023-05-14
42023-05-14 11:00:0024.02023-05-14
52023-05-14 11:00:0018.42023-05-14
62023-05-15 1100:0025.22023-05-15
72023-05-15 12:00:0023.82023-05-15
82023-05-15 12:00:0018.62023-05-15
92023-05-15 12:00:0025.52023-05-15

2.2.2、实现

-- 创建增量表
CREATE TABLE sensors_incremental (
  sensor_id INT,
  timestamp TIMESTAMP,
  value FLOAT
)
PARTITIONED BY (day DATE)
STORED AS PARQUET;

-- 将原始数据加载到sensors_temp临时表中
LOAD DATA INPATH 'path/to/sensors.csv' OVERWRITE INTO TABLE sensors_temp;

-- 将临时表数据初始化加载进增量表
INSERT INTO sensors_incremental PARTITION (date='2023-05-14') SELECT * FROM sensors_temp;

-- 第二天将15号临时表数据加载进增量表
INSERT INTO sensors_incremental PARTITION (date='2023-05-15') SELECT * FROM sensors_temp;


通过使用以上增量表设计和相应的SQL语句,我们可以有效地处理传感器数据,并实现按天分区的管理和查询。

2.3、快照表

快照表同样适用于增量非更新数据源,快照表是用来存储某个时间点的所有数据-通常粒度是天,相当于是对每天的业务数据做了一次快照,存储当天的全量数据!

其设计和实现与增量更新数据源保持一致,详细参考:1.1.3、快照表

2.4、流水表

流水表同样适用于增量不更新数据源,流水表是用于记录数据变更的表,流水表如果是按天为粒度划分,那和增量表几乎一模一样,因为不涉及到数据的修改,故增量非更新数据源下的流水表和增量表的数据几乎保持一致。

其设计和实现与增量更新数据源保持一致,详细参考:1.1.4、流水表

2.5、拉链表

拉链表同样适用于增量不更新数据源,其设计和实现与增量更新数据源保持一致,详细参考:1.1.5、拉链表

2.6、切片表

切片表是根据事实数据生成的维度表,故同样适用于增量不更新数据源,其设计和实现与增量更新数据源保持一致,详细参考:1.1.6、切片表

三、总结

增量表、全量表、快照表、拉链表、切片表和流水表都是常用的数据表格设计方式,每种表格设计方式都有自己的优劣势和适用场景。

  • 增量表:只记录新增或更改操作的数据表,可以减少存储和处理开销,适用于高频产生新增事件的情况。
  • 全量表:记录所有数据的完整表格,可以提供准确的数据,但无法提供历史溯源记录
  • 快照表:记录某个时间点的部分或全部数据,可用于数据快照和查询。通常使用在查询性质的报表系统中。
  • 拉链表:记录数据变化的时间范围。适用于需要准确跟踪数据变化的场景,例如会计系统。
  • 切片表:记录具有多维度属性的实体,例如客户、产品等,适用于类似于分析型场景。
  • 流水表:记录所有事件(包括新增、删除、修改等),可以追溯完整的历史信息,适用于审计和数据分析以及为其他类型的表提供数据源

总体而言,以上的表格设计方式都有其自身的优缺点以及适用场景,应根据具体的业务需求进行合理选择。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/43182.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

STM32外设系列—TB6612FNG

本文涉及到定时器和串口的知识&#xff0c;详细内容可见博主STM32速成笔记专栏。 文章目录 一、TB6612简介二、TB6612使用方法2.1 TB6612引脚连接2.2 控制逻辑2.3 电机调速 三、实战项目3.1 项目简介3.2 初始化GPIO3.3 PWM初始化3.3 电机控制程序3.4 串口接收处理函数 一、TB66…

基于SPDK-vhost的云原生Kubevirt虚拟化存储IO的优化方案

摘要 本文主要介绍针对云原生kubernetes虚拟化IO的应用场景&#xff0c;在Kubevirt中引入SPDK-vhost的支持&#xff0c;来加速虚机中IO存储性能。同时基于Intel开源的Workload Service Framework[1]平台集成部署一套端到端虚拟化IO的应用场景做基本的性能对比测试。 云原生Kube…

Hadoop——Windows系统下Hadoop单机环境搭建

为了便于开发&#xff0c;我在本地Windows系统进行Hadoop搭建。 我使用的版本&#xff1a;hadoop-2.7.0。其他版本也可&#xff0c;搭建流程基本一样&#xff0c;所以参考这个教程一般不会有错。 1、下载安装包和插件 安装包hadoop-2.7.0.tar.gz 必要插件winutils-master 2、…

RocketMQ教程-安装和配置

Linux系统安装配置 64位操作系统&#xff0c;推荐 Linux/Unix/macOS 64位 JDK 1.8 Maven3.0 yum 安装jdk8 yum 安装maven 1.下载安装Apache RocketMQ RocketMQ 的安装包分为两种&#xff0c;二进制包和源码包。 点击这里 下载 Apache RocketMQ 5.1.3的源码包。你也可以从这…

详细介绍Matlab中线性规划算法的使用

Matlab中提供了用于线性规划的优化工具箱&#xff0c;其中包含了多种算法&#xff0c;如单纯形法、内点法等。线性规划是一种优化问题&#xff0c;旨在找到一组变量的最佳值&#xff0c;以最大化或最小化线性目标函数&#xff0c;同时满足一组线性约束条件。 下面将详细介绍Ma…

【Spring Boot】事务的隔离级别与事务的传播特性详解:如何在 Spring 中使用事务?不同隔离级别的区别?

文章目录 1 事务1.1 事务简介与 mysql 中的事务使用1.2 Spring 编程式事务&#xff08;手动操作&#xff09;1.3 Spring 声明式事务&#xff08;自动操作&#xff09;1.4 Transactional 的工作原理 2 事务的隔离级别2.1 事务的四大特性及事务的隔离级别回顾2.2 Spring 事务的隔…

python web开发之WSGI/uwsgi/uWSGI详解

1. 三者的定义 WSGI是一种通信协议。uwsgi是一种传输协议。uWSGI是实现了uwsgi和WSGI两种协议的Web服务器。 2.三者的使用场景 WSGI&#xff0c;全称 Web Server Gateway Interface&#xff0c;是为 Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简单而通用的接…

MySQL之索引(入门级讲解)

目录 一.索引的概念 1.1索引的简介 1.2.索引的优缺点 二.MySQL索引语法 2.1查看索引 2.2创建索引 2.2.1 创建表时创建索引 2.2.2存在的表上创建索引 2.3删除索引 三.索引的数据结构 3.1Btree索引 3.2Hash索引 3.4Hash索引和Btree索引的对比 &#x1f381;个…

文本预处理——文本张量表示方法

目录 文本张量表示one-hot编码word2vecword embedding 文本张量表示 one-hot编码 word2vec word embedding

代码随想录算法训练营第二十一天 | 读PDF复习环节1

读PDF复习环节1 本博客的内容只是做一个大概的记录&#xff0c;整个PDF看下来&#xff0c;内容上是不如代码随想录网站上的文章全面的&#xff0c;并且PDF中有些地方的描述&#xff0c;是很让我疑惑的&#xff0c;在困扰我很久后&#xff0c;无意间发现&#xff0c;其网站上的讲…

使用rknn-toolkit2把YOLOV5部署到OK3588上

使用rknn-toolkit2把YOLOV5部署到OK3588上 虚拟环境搭建软件包安装在PC机上运行yolov5目标检测 虚拟环境搭建 首先在PC的ubuntu系统安装虚拟环境&#xff1a; 我的服务器是ubuntu18.04版本&#xff0c;所以安装python3.6 conda create -n ok3588 python3.6 需要键盘输入y&…

Python 算法基础篇:插入排序和希尔排序

Python 算法基础篇&#xff1a;插入排序和希尔排序 引言 1. 插入排序算法概述2. 插入排序算法实现实例1&#xff1a;插入排序 3. 希尔排序算法概述4. 希尔排序算法实现实例2&#xff1a;希尔排序 5. 插入排序与希尔排序的对比总结 引言 插入排序和希尔排序是两种常用的排序算法…

Day 61-62 决策树(ID3)

代码&#xff1a; package dl;import java.io.FileReader; import java.util.Arrays; import weka.core.*;/*** The ID3 decision tree inductive algorithm.*/ public class ID3 {/*** The data.*/Instances dataset;/*** Is this dataset pure (only one label)?*/boolean …

结构型模式 - 适配器模式

概述 如果去欧洲国家去旅游的话&#xff0c;他们的插座如下图最左边&#xff0c;是欧洲标准。而我们使用的插头如下图最右边的。因此我们的笔记本电脑&#xff0c;手机在当地不能直接充电。所以就需要一个插座转换器&#xff0c;转换器第1面插入当地的插座&#xff0c;第2面供…

springboot+vue开发后台增删改查

效果图 前端代码【User.vue】 <template><div class"data-container"><!--添加 start--><div class"data-header"><el-button round click"addHander" size"large" type"primary"><el-ic…

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测

区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测 目录 区间预测 | MATLAB实现QRBiLSTM双向长短期记忆神经网络分位数回归多输入单输出区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 区间预测 | MATLAB实现QRBiLSTM…

uview中的常用的框

第一步&#xff1a; 先下载 uview UI 框架 详见 项目 引入 uView_vue引入uview_qq_2524963996的博客-CSDN博客【代码】 项目 引入 uView。_vue引入uviewhttps://blog.csdn.net/qq_44161703/article/details/131168976?spm1001.2014.3001.5501 第二步&#xff1a; 可以直接…

解锁潜力,驭数赋能:大数据与云计算的强强联合

随着数字化时代的来临&#xff0c;大数据和云计算已成为信息技术领域的两大热门话题。大数据指的是以海量、高速、多样化的数据为基础&#xff0c;通过分析和挖掘来获得有价值的信息和洞察。而云计算则是一种基于网络的计算模式&#xff0c;通过将数据和应用程序存储在云端服务…

【前端动画】科技感扫描效果 css动画animation

扫描出现动画 参考了网友写的二维码扫描 <template><div><div class"scan-content"><img style"width: 2rem;height: 2rem;" src"../../assets/images/eye.png" alt"" /><div class"line">…