设计需求预测分解的简单模式

时间:2013-02-10 08:35:18

标签: sql oracle database-design oracle11g

编辑:[详细信息]我正在做一个简单的数据库设计任务作为培训练习,我必须针对以下情况提出基本架构设计:

我有产品的父子层次结构(原材料>正在进行的工作>最终产品)。

每个级别都有订单。

订单数量应在未来6个月的每周桶中查看。

可以针对每个产品级别进行需求预测。

对未来6个月的每周需求量进行预测。

通常在层次结构的较高级别(原材料或正在进行的工作)中完成

必须将其分解为较低级别(最终产品)

有两种方法可以将需求预测从较高级别分解为较低级别:

  1. 用户指定最终产品的百分比分布。比如,正在进行的工作预测为1000 ..用户说我想要最终产品1的40%和最终产品2的60%。然后从现在开始的第10周(周日到周六),预测值对于最终产品1,将为400,对于最终产品2,将为600。

  2. 用户说,只是根据针对Bucket 5中针对最终产品的订单进行分解,而针对最终产品1和2的桶5中的订单分别为200和800,那么EP1的预测值将是((200 / 1000)* 100)%和EP2将是(工作进行中)的预测的((800/1000)* 100)%。

  3. 预测应在未来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 个答案:

答案 0 :(得分:1)

你的最后评论正是我的意思。很酷,看到你明白了!

自从我开始这样做以来,我完成了一个例子。与你所说的不同的是,只使用一周的日期,是星期一,以及各种检查,将不会改变的内容(raw_material VS raw_material_hist)分开约束

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过程中或者用于接口的任何内容中。

编辑:同样从你上次评论中你提到预测手动设置VS计算的一个。所以我添加了这样一个专栏,但是积分给了你

编辑之二:对于水桶编号,只需使用适当的日期掩码,例如IWWW。这两个变化是一年中的第一周。

答案 1 :(得分:0)

这是SQL Server语法,你可以google等效的oracle函数

DATEADD(week, DATEDIFF(week, GETDATE(), 0), 0)

这将在当周的第一天给你午夜(0:00:00)。这取决于您的系统设置 - 您可以使用Oracle等效的DATEADD将其移动到您需要的日期。

我会重新考虑你的架构,从你的描述来看,heirachy不是跳到我脑海中的概念 - 听起来更像是一个序列。我认为你最好从业务对象开始,然后回到数据库而不是相反。

答案 2 :(得分:0)

好的,这就是我想出的数据模型。

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"

预测 - 存储产品的预测值(较高级别的商店值,从父级分解后存储较低级别的值)

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;