设计用于分解需求预测的简单模式
Posted
技术标签:
【中文标题】设计用于分解需求预测的简单模式【英文标题】:Designing Simple Schema for Disaggregation of Demand Forecast 【发布时间】:2013-02-10 08:35:18 【问题描述】:编辑:[详细信息]我正在做一个简单的数据库设计任务作为训练练习,我必须为以下情况提出一个基本的模式设计:
我有一个父子产品层次结构(原材料 > 在制品 > 最终产品)。
订单在每个级别下。
在接下来的 6 个月内,每周可以查看订单数量。
可以对每个产品级别进行需求预测。
对未来 6 个月的每周桶进行需求预测。
通常在层次结构的较高级别(原材料或正在进行的工作)中完成
必须将其分解到较低级别(最终产品)
有两种方法可以将需求预测从较高级别分解到较低级别:
用户指定最终产品的百分比分布。说,有 1000 的预测工作正在进行中.. 用户说我想要 40% 的最终产品 1 和 60% 的最终产品 2 在桶 10.. 然后从现在开始的第 10 周(周日到周六),预测值最终产品 1 为 400,最终产品 2 为 600。
用户说,只是根据对桶 5 中终端产品的订单进行分解,桶 5 中终端产品 1 和 2 的订单分别为 200 和 800,则 EP1 的预测值为 ((200/ 1000) * 100)%,对于 EP2,将是“正在进行的工作”预测的 ((800/1000) * 100)%。
未来 6 个月的预测应在每周存储桶中查看,理想的格式应为:
product name | bucket number | week start date | week end date | forecast value
这种需求的基本理想模式是什么?
Product_Hierarchy 表可能如下所示:
id | name | parent_id
__________________________________________
1 | raw material | (null)
2 | work in progress | 1
3 | end product 1 | 2
4 | end product 2 | 2
这是存储订单的好方法吗?
订单
id | prod_id | order_date | delivery_date | delivered_date
在哪里,
prod_id
是引用product_hierarchy表id
的外键,
每周26桶的订单可以选择为
SELECT
COUNT(*) TOTAL_ORDERS,
WIDTH_BUCKET(
delivery_date,
SYSDATE,
ADD_MONTHS(sysdate, 6),
TO_NUMBER( TO_CHAR(SYSDATE,'DD-MON-YYYY') - TO_CHAR(ADD_MONTHS(sysdate, 6),'DD-MON-YYYY') ) / 7
) BUCKET_NO
FROM
orders_table
WHERE
delivery_date BETWEEN SYSDATE AND ADD_MONTHS(sysdate, 6);
但这将给出从今天开始的每周存储桶,无论哪一天。我希望他们在星期天到星期六星期。
请帮助设计这个数据库结构。
(将使用 Oracle 11g)
【问题讨论】:
如果预测是计算值,请不要为其添加字段。使用虚拟列,您可以在其中动态计算它,或者如果您愿意,可以使用视图。这是关系数据库设计的基础,有普通的表格等。为什么不 3 个表,一个字段用于一周,另一个用于订单,只是用 FK 引用母亲的一张? 用户也可以手动输入一周桶对应的预测,需要保存。无论如何,预测值将被保存到层次结构中的更高级别。较低级别最初可能没有价值 - 但在分解后可能会有。我应该如何处理这个?你有什么建议? 嗯,我不知道,我什么都不懂。 :( 订单已经存在于您的表中,它们是数据,对吧?作为条目数据,您只有当前日期、预测。预测是否必须拆分到较小的级别? 是的,对,订单就是数据,有一个表格。有一个产品层次结构表,其中包含有关各个级别的产品的数据。必须在任何级别记录预测(用户指定的预测).. 计算的预测 = 从较高级别拆分到较低级别。 我的意思是,你能预测 raw_material=3,而且它的所有子都不精确吗? 【参考方案1】:您最后的评论正是我的意思。很高兴看到你得到它!
自从我开始做以来,我完成了一个示例代码。与您所说的不同之处在于,仅使用一周的日期(是星期一)和各种检查约束来区分会改变的和不会改变的(raw_material
VS raw_material_hist
)。 p>
CREATE TABLE raw_material
(
material_id NUMBER PRIMARY KEY,
material_blabla VARCHAR2(20)
);
CREATE TABLE wip
(
wip_id NUMBER PRIMARY KEY,
parent_raw NUMBER REFERENCES raw_material(material_id),
wip_desc VARCHAR2(20)
);
CREATE TABLE end_product
(
end_product_id NUMBER PRIMARY KEY,
parent_wip NUMBER REFERENCES wip(wip_id),
description VARCHAR2(20)
);
CREATE TABLE rm_histo
(
material_id NUMBER REFERENCES raw_material(material_id),
week_start DATE CHECK (To_char(week_start, 'D')=1),
forecast NUMBER(8) CHECK (forecast >0),
CONSTRAINT pk_rm_histo PRIMARY KEY (material_id, week_start)
);
CREATE TABLE wip_histo
(
wip_id NUMBER REFERENCES wip(wip_id),
week_start DATE CHECK(To_char(week_start, 'D')=1),
wip_user_forecast NUMBER(8) CHECK (wip_user_forecast>0),
CONSTRAINT pk_wip_histo PRIMARY KEY (wip_id, week_start)
);
CREATE TABLE end_prod_histo
(
end_product_id NUMBER REFERENCES end_product(end_product_id),
week_start DATE CHECK(To_char(week_start, 'D')=1),
end_prod_user_forecast NUMBER(8) CHECK (end_prod_user_forecast >0)
);
最后,您确实使用视图来查看预测的事物,或者如果您拥有大量数据,则使用物化视图。通过使用视图,您不会复制数据,因此更改/更新更安全、更容易。
对于您的用例 1 或 2,这不涉及数据库模式。归根结底,它只是为预测更新一些值,用例 1 或 2 的逻辑可以进入 PL/SQL 过程或您用于接口的任何内容。
编辑:同样从您的最后一条评论中,您提到手动设置预测值与计算值。所以我添加了这样一个专栏,但学分归你
编辑之二:至于桶号,只需为日期使用适当的掩码,如IW
或WW
。这两个变化是一年中的第一周。
【讨论】:
感谢@Plouf 的回答,但raw_material
、wip
和end_products
表的结构基本相同。看看我的问题中的 product_heirarchy 表.. SELECT LEVEL, LPAD(' ', 2 * LEVEL - 1) ||名称 AS product_level_name FROM product_hierarchy 以 id = 1 开头连接 id = parent_id;将帮助您轻松可视化父子关系。
我认为您的问题是关于替代模式的。因为我知道我不是单个表中的层次结构的超级粉丝。只需尝试使用connect by
和许多行...而使用多个表,您可以使用索引、优化连接、分区...
唯一担心的是,如果产品级别众多,事情很快就会失控。但是,真的,非常感谢您的回答和 cmets - 他们给了我更广阔和更清晰的视角。我有一个解决方案,我会在验证我的方法后很快发布详细的答案。谢谢@Plouf【参考方案2】:
好的,这就是我想出的数据模型。
PRODUCT -- 存储产品信息并维护父子层次结构
id NUMBER "Primary Key Not Null"
level_code VARCHAR2 Not Null
name VARCHAR2 Not Null
description VARCHAR2
parent_id NUMBER Foreign Key references PRODUCT(id)
ORDERS -- 存储产品订单
id NUMBER "Primary Key Not Null"
prod_id NUMBER "Foreign Key references PRODUCT(id) Not Null"
order_type VARCHAR2 "Not Null Default 'Default'"
order_qty NUMBER Not Null
order_date NUMBER Foreign Key references DATE_INFO(date_key)
delivery_date NUMBER "Foreign Key references DATE_INFO(date_key)
Check delivery_date >= order_date"
FORECAST -- 存储产品的预测值(存储更高级别的值,从父级分解后存储更低级别的值)
id NUMBER "Primary Key Not Null"
product_id NUMBER "Foreign Key references PRODUCT(id) Not Null"
forecast_value NUMBER Not Null
week NUMBER "Foreign Key references DATE_INFO(date_key) Not Null"
DISAGGREGATION_RULES -- 存储用于将值从较高级别分解到较低级别的方法以及分配到较低级别的百分比
id NUMBER "Primary Key Not Null"
parent_product_id NUMBER "Foreign Key id references PRODUCT(id) Not Null"
child_product_id NUMBER "Foreign Key id references PRODUCT(id) Not Null"
method VARCHAR2 Not Null
from_week NUMBER "Foreign Key references DATE_INFO(date_key) Not Null"
to_week NUMBER "Foreign Key references DATE_INFO(date_key) Not Null Check end_week >= start_week"
percent_distribution NUMBER Not Null
DATE_INFO -- 日期维度,包含关于开始日期(必须是星期六)和结束日期的信息,对应于特定日期所在的星期
date_key NUMBER "Primary Key
Not Null"
full_date DATE Not Null
week_begin_date DATE Not Null
week_end_date DATE Not Null
至于桶数/周的事情..我正在使用以下函数计算周开始日期(在我的情况下是星期六的日期)
CREATE OR REPLACE FUNCTION get_week_start_date(v_bucket_num IN NUMBER)
RETURN DATE
IS
week_start_date DATE;
BEGIN
SELECT (TRUNC(SYSDATE+2, 'IW')-2) + ((v_bucket_num-1) * 7)
INTO week_start_date FROM dual;
RETURN week_start_date;
END;
【讨论】:
【参考方案3】:这是 SQL Server 语法,你可以谷歌搜索等效的 oracle 函数
DATEADD(week, DATEDIFF(week, GETDATE(), 0), 0)
这将为您提供当前一周第一天的午夜 (0:00:00)。这是哪一天取决于您的系统设置 - 您可以使用 DATEADD 的 Oracle 等效项将其移动到您需要的那一天。
我会重新考虑你的架构,从你的描述来看,heirachy 并不是我脑海中闪现的概念——它听起来更像是一个序列。我认为您最好从业务对象开始并返回数据库,而不是相反。
【讨论】:
您能详细解释一下您对架构的建议吗?我将这项任务作为一项培训练习(详细说明我需要什么) - 所以我愿意重新思考我的模式。我可以使用一些指导。您需要任何其他详细信息吗? 好的,从根本上说计算机程序是为了解决现实世界的问题。编程 IMO 的基本前提不是从软件可以做什么开始,而是从目前解决问题的方式开始,并建立一个概念模型。一般来说,会有事物(代码中的对象)和过程(代码中的方法)允许对象诞生、发展和消亡。数据库只是存储这些状态的一种方式。如果您想发布问题的具体细节,我会在那里更具体。 为了更清楚,我更新了我的问题。请看看它是否有助于可视化案例。以上是关于设计用于分解需求预测的简单模式的主要内容,如果未能解决你的问题,请参考以下文章