事实和维度:动态维度
Facts and dimensions: dynamic dimensions
(感谢 post 对于 SO 来说可能太高层次或太哲学了,我正处于模式规划阶段并寻求一些指导)
在使用我们的生产数据库的克隆进行分析遇到一些困难之后,我试图定义一个事件事实 table 以及一些维度 table 以使分析工作更简单。
我在计划中遇到的障碍是这个。我们有不同类别的事件,需要用不同的维度来描述它们。例如。假设我们有 'account settings' 个事件类别以及 'Gallery' 个事件。
事实上 table 我可能有一个字段 eventCategory 和 eventName,其中包含上面的示例值,例如:
'EventCategory': 'Account Settings'
'EventName': 'Update Card Billing Details'
或者:
'EventCategory': 'Gallery'
'EventName': 'Create New Gallery'
在每种情况下,我都想使用不同的维度集合来描述它们。例如。对于画廊事件,我们想知道 'template'、'count of images'、'gallery category e.g. fruits'。我们不需要帐户设置事件的这些详细信息,它们有自己独特的一组维度来描述它们。
通过我在网上找到的教科书示例,我将有一个尺寸 table 用于图库事件和一个尺寸 table 用于帐户设置事件。
我的心理障碍是这些维度是动态的而不是静态的。我想在事实中记录 table 事件发生时这些维度的值而不是 'now'。例如,用户可以是试用用户或付费用户。如果我有一个维度 table 'user',他们目前的状态可能是 'paid',但在之前的一些画廊活动中,他们可能正在试用中。
'right' 处理此问题的方法是什么:
- 多项资料table,一项用于图库事件,一项用于帐户设置事件?
- 在主要事实的新字段中使用 json table 例如'EventDetail' 包含维度 table 中的内容,除非使用 json 我们知道事件发生时的维度值,而不是现在的值?
- 我可能有一个稀疏的事实table。我会在所有类别中包含每个维度的字段,如果不适用,这些字段将为空
鉴于我用来描述事件的维度是动态的,'right' 构造事实 table 用于分析的方法是什么?我刚才看到的方式是维度 tables 本身必须是事实才能捕获这些属性随时间变化的值。
向任何 SQL table 添加维度的方法始终相同,即添加一列。
任何一种历史,都没有“现在”。每个状态都有一个时间段:开始和结束。我通常将这些列命名为 AsOf
和 Until
,因为 begin/end 经常作为 SQL 关键字出现,使得列名更难扫描。通常,只需要 AsOf
,因为可以自加入 table 来查找后续时间段,并使用 NULL 来表示 'now'(其中“现在”表示 截至执行查询时).
'user' their status might currently be 'paid' but at the time of some previous gallery event they may have been in trial.
对,所以用户的状态不只是 paid/trial。它是 paid 或 trial 从某个日期开始,直到同一用户的更晚的 AsOf 日期。
很难提供更多帮助。您的问题中有一些行话,并且是用特定领域的术语表达的。我希望通过为每个状态附加一个date/time,您可以看到走出森林的路。
(A) 在 postgres 中管理时间数据
时态数据在多种业务应用程序中是很常见的需求,但它不是 postgres 或许多其他 RDBMS 中的“内置”功能。
如@James K. Lowden 所述,您可以使用一些 AsOf
和 Until
类型的 timestamp
列,有或没有时区,或者您可以使用单个tsrange
或 tstzrange
类型的列,即一系列时间戳,这将为您提供一些不错的内置函数,请参阅 manual.
为了避免同一数据的不同事件关联的时间戳范围重叠,您可以实现带有触发器功能的业务逻辑。
比如对于同一个用户,可以实现如下的触发函数,使得状态'in trial'关联的范围r1和状态'paid'关联的范围r2在对应的行被自动设置插入 user
table,同一用户的现有行的范围相应更新:
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
可以作为在同一列中存储各种维度的解决方案。
另一个解决方案可能是 table 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 ;
一个事实 table 需要定义它的粒度;如果事实与该粒度不匹配,则它们无法存储在该事实中 table => 如果您的事实具有不同的维度集,那么您需要使用不同的事实 tables.
关于维度变化中的值,您需要阅读缓慢变化的维度
(感谢 post 对于 SO 来说可能太高层次或太哲学了,我正处于模式规划阶段并寻求一些指导)
在使用我们的生产数据库的克隆进行分析遇到一些困难之后,我试图定义一个事件事实 table 以及一些维度 table 以使分析工作更简单。
我在计划中遇到的障碍是这个。我们有不同类别的事件,需要用不同的维度来描述它们。例如。假设我们有 'account settings' 个事件类别以及 'Gallery' 个事件。
事实上 table 我可能有一个字段 eventCategory 和 eventName,其中包含上面的示例值,例如:
'EventCategory': 'Account Settings'
'EventName': 'Update Card Billing Details'
或者:
'EventCategory': 'Gallery'
'EventName': 'Create New Gallery'
在每种情况下,我都想使用不同的维度集合来描述它们。例如。对于画廊事件,我们想知道 'template'、'count of images'、'gallery category e.g. fruits'。我们不需要帐户设置事件的这些详细信息,它们有自己独特的一组维度来描述它们。
通过我在网上找到的教科书示例,我将有一个尺寸 table 用于图库事件和一个尺寸 table 用于帐户设置事件。
我的心理障碍是这些维度是动态的而不是静态的。我想在事实中记录 table 事件发生时这些维度的值而不是 'now'。例如,用户可以是试用用户或付费用户。如果我有一个维度 table 'user',他们目前的状态可能是 'paid',但在之前的一些画廊活动中,他们可能正在试用中。
'right' 处理此问题的方法是什么:
- 多项资料table,一项用于图库事件,一项用于帐户设置事件?
- 在主要事实的新字段中使用 json table 例如'EventDetail' 包含维度 table 中的内容,除非使用 json 我们知道事件发生时的维度值,而不是现在的值?
- 我可能有一个稀疏的事实table。我会在所有类别中包含每个维度的字段,如果不适用,这些字段将为空
鉴于我用来描述事件的维度是动态的,'right' 构造事实 table 用于分析的方法是什么?我刚才看到的方式是维度 tables 本身必须是事实才能捕获这些属性随时间变化的值。
向任何 SQL table 添加维度的方法始终相同,即添加一列。
任何一种历史,都没有“现在”。每个状态都有一个时间段:开始和结束。我通常将这些列命名为 AsOf
和 Until
,因为 begin/end 经常作为 SQL 关键字出现,使得列名更难扫描。通常,只需要 AsOf
,因为可以自加入 table 来查找后续时间段,并使用 NULL 来表示 'now'(其中“现在”表示 截至执行查询时).
'user' their status might currently be 'paid' but at the time of some previous gallery event they may have been in trial.
对,所以用户的状态不只是 paid/trial。它是 paid 或 trial 从某个日期开始,直到同一用户的更晚的 AsOf 日期。
很难提供更多帮助。您的问题中有一些行话,并且是用特定领域的术语表达的。我希望通过为每个状态附加一个date/time,您可以看到走出森林的路。
(A) 在 postgres 中管理时间数据
时态数据在多种业务应用程序中是很常见的需求,但它不是 postgres 或许多其他 RDBMS 中的“内置”功能。
如@James K. Lowden 所述,您可以使用一些 AsOf
和 Until
类型的 timestamp
列,有或没有时区,或者您可以使用单个tsrange
或 tstzrange
类型的列,即一系列时间戳,这将为您提供一些不错的内置函数,请参阅 manual.
为了避免同一数据的不同事件关联的时间戳范围重叠,您可以实现带有触发器功能的业务逻辑。
比如对于同一个用户,可以实现如下的触发函数,使得状态'in trial'关联的范围r1和状态'paid'关联的范围r2在对应的行被自动设置插入 user
table,同一用户的现有行的范围相应更新:
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
可以作为在同一列中存储各种维度的解决方案。
另一个解决方案可能是 table 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 ;
一个事实 table 需要定义它的粒度;如果事实与该粒度不匹配,则它们无法存储在该事实中 table => 如果您的事实具有不同的维度集,那么您需要使用不同的事实 tables.
关于维度变化中的值,您需要阅读缓慢变化的维度