事实和维度:动态维度 [关闭]
Posted
技术标签:
【中文标题】事实和维度:动态维度 [关闭]【英文标题】:Facts and dimensions: dynamic dimensions [closed] 【发布时间】:2022-01-15 21:20:59 【问题描述】:(感谢这篇文章对于 SO 来说可能太高层次或太哲学了,我正处于架构规划阶段并寻求一些指导)
在使用我们的生产数据库的克隆进行分析遇到一些困难后,我试图定义一个事件事实表以及一些维度表,以使分析工作更简单。
我在计划中遇到的障碍是这个。我们有不同类别的事件,需要用不同的维度来描述它们。例如。假设我们有“帐户设置”事件类别以及“画廊”事件。
在事实表中,我可能有一个字段 eventCategory 和 eventName,其中包含上面的示例值,例如:
'EventCategory': 'Account Settings'
'EventName': 'Update Card Billing Details'
或者:
'EventCategory': 'Gallery'
'EventName': 'Create New Gallery'
在每种情况下,我都想使用不同的维度集合来描述它们。例如。对于画廊活动,我们想知道“模板”、“图像数量”、“画廊类别”,例如水果'。我们不需要帐户设置事件的这些详细信息,它们有自己独特的一组维度来描述它们。
通过我在网上找到的教科书示例,我将获得画廊事件的维度表和帐户设置事件的维度表。
我的心理障碍是这些维度是动态的而不是静态的。我想在事实表中记录事件发生时这些维度的值,而不是“现在”。例如,用户可以是试用版或付费用户。如果我有一个维度表“用户”,他们的状态当前可能是“付费”,但在以前的一些画廊活动时,他们可能已经处于试用期。
处理这个问题的“正确”方法是什么:
多个事实表,一个用于库事件,一个用于帐户设置事件? 在主要事实表的新字段中使用 json,例如'EventDetail' 包含维度表中的其他内容,除非使用 json,我们知道事件发生时维度的值,而不是现在的那些值? 我可以有一个稀疏的事实表。我将包含所有类别中每个维度的字段,如果不适用,这些字段将为空鉴于我用来描述事件的维度是动态的,那么构建用于分析的事实表的“正确”方法是什么?我刚才看到的方式是,维度表本身必须是事实,才能捕捉这些属性随时间变化的值。
【问题讨论】:
【参考方案1】:向任何 SQL 表添加维度始终以相同的方式完成,即添加一列。
在任何一种历史中,都没有“现在”。每个状态都有一个时间段:开始和结束。我通常将这些列命名为 AsOf
和 Until
,因为 begin/end 经常显示为 SQL 关键字,使得列名更难扫描。通常,只需要AsOf
,因为您可以自联接表以查找后续期间,并使用 NULL 表示“现在”(其中“现在”表示,截至执行查询的时间 em>)。
“用户”他们的状态目前可能是“付费”,但在之前的一些画廊活动时,他们可能已经处于试用期。
没错,所以用户的状态不仅仅是付费/试用。 付费或试用从某个日期开始 AsOf,直到同一用户的更晚 AsOf 日期。
很难提供更多帮助。您的问题中有一些行话,并且是以特定领域的术语表达的。我希望通过为每个状态附加日期/时间,您可以看到自己走出森林的路。
【讨论】:
谢谢。这是有道理的,使用“AsOf”时间戳。您如何看待使用 json 字段来捕获例如某个时间点的画廊尺寸?也许这是一种“偷工减料”的方法?我的目标是数据检索的简单性和便利性。 我想你会发现 JSON,正如 F*** Pascal 喜欢说的那样,增加了复杂性而没有能力。任何可以用 JSON 表达的东西都可以用 SQL 表来表达,而只使用表可以简化问题,因为数据只有一种表示形式。 在这样的任何类型的工作中,一个无法避免的麻烦是重叠的时间段。您必须为两个实体构建 AsOf-Until 对,然后将它们加入where a.AsOf between B.AsOf and B.Until or b.AsOf between a.AsOf and a.Until
。表达起来很尴尬,想起来也很困惑,但没有办法绕过它。没有技术上的简化,这是不可避免的。【参考方案2】:
(A) 在 postgres 中管理时态数据
时态数据在许多类型的业务应用程序中是很常见的需求,但它不是 postgres 中的“内置”功能,也不是许多其他 RDBMS 中的“内置”功能。
正如@James K. Lowden 所说,您可以使用timestamp
类型的一些AsOf
和Until
列,带或不带时区,或者您可以使用tsrange
或@ 类型的单个列来代替987654327@,即一系列时间戳,它会为您提供一些不错的内置功能,请参阅manual。
为了避免与同一数据的不同事件关联的时间戳范围之间的重叠,您可以使用触发器函数实现业务逻辑。
例如,对于同一用户,您可以实现以下触发函数,以便在将相应行插入到user
表,并且同一用户的现有行的范围会相应更新:
CREATE OR REPLACE FUNCTION before_insert_user ()
RETURNS trigger LANGUAGE plpgsql AS
$$
BEGIN
-- update all the existing rows (ie status) for the same user_id whose valid_ranges are valid as of now
UPDATE user
SET valid_range = tstzrange(lower(valid_range), Now())
WHERE user_id = NEW.user_id
AND valid_range @> Now() ;
-- set up the valid_range for the new row (ie the new status)
NEW.valid_range = tstzrange(Now(), NULL) ;
END ;
$$ ;
CREATE OR REPLACE TRIGGER before_insert_user BEFORE INSERT ON user
FOR EACH ROW EXECUTE FUNCTION before_insert_user () ;
(B) 管理不同类别的不同维度
如前所述,json
可以是在同一列中存储各种维度的解决方案。
另一种解决方案可能是表inheritance 具有一些有趣的功能:
CREATE TABLE Event
( EventCategory varchar
, EventName varchar
, ValidityRange tstzrange
, primary key (EventCategory , EventName, ValidityRange )
) ;
CREATE TABLE user
( status varchar
) INHERITS Event ;
CREATE TABLE Gallery
( template varchar
, "count of images" integer
, "gallery category e.g. fruits" varchar
) INHERITS Event ;
【讨论】:
【参考方案3】:事实表需要定义其粒度;如果事实与该粒度不匹配,则无法将它们存储在该事实表中 => 如果您的事实具有不同的集合 if 维度,那么您需要使用不同的事实表。
关于维度变化中的值,您需要阅读Slowly changed Dimensions
【讨论】:
以上是关于事实和维度:动态维度 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章