【问题标题】:storing weekly targets in database在数据库中存储每周目标
【发布时间】:2010-12-10 12:55:27
【问题描述】:

我有以下要求

Sales Officer: Bob

             Week1 Week2  Week3 ................. Week52
    Prod1     10    15    12    .................    14
    Prod2     20    14    10    .................    17
    .                                                 .   
    .                                                 .
    .                                                 .

销售主管每周都会为每位销售人员设定目标。 销售人员可以根据设定的目标通过类似的网格输入每个产品每天的实际销售额,例如
编辑
在上述情况下,主管为第一周设定了 10 个单位的目标现在销售人员将每天输入销售额为 1,2,0,1,3,2=9(第一周的实际销售额),因此与目标相反在 10 个单位中,他在第一周卖出了 9 个单位。

我已经创建了 Employee 和 Product 表。任何人都可以指导有关如何在数据库中存储的最佳实践,并据此存储目标并记录实际销售额。

我正在考虑将数据存储在下表中

EmpSales (EmployeeID,ProductID,SaleTarget,Actual Sale,Date,WeekNo,Month)

提前致谢

【问题讨论】:

  • 尽管您选择了答案,但由于您要求我出席,我还是提供了答案。有机会得到一些反馈,所以我可以完成它;根据 cmets,我们不知道您的技能水平;你是否了解 DATE 算术;等等
  • @PerformanceDBA 感谢您的回复,我正在处理您和其他回复,并将为您提供反馈

标签: sql-server database database-design relational-database


【解决方案1】:

这在纯关系建模方面非常简单。我认为不需要任何形式的“非规范化”。

Sales Data Model.

如果您不熟悉关系数据库建模标准,IDEF1X Notation 可能会有所帮助。

纯 5NF;完整的声明性引用完整性;没有空值,没有更新异常;没有GROUP BYs;纯日期算术。

  • SaleTarget 通过投影与SaleActual 进行比较,并且可能在同一结果集中。

  • 如果您有月度和年度销售会计,则所需的扩展是具有一些控制或结构的通用日历表;例如。类似于Week,包括每个月和年的行。请告诉我,我会更新模型。

  • 我说 5NF 是因为这是我为消除更新异常而提供的最低要求,而且大多数建模者都熟悉它。但如果它没有吓跑你,这两个销售表实际上是第六范式

  • 这允许在没有临时表或复杂 SQL 的情况下进行完整的数据透视(顶部数周或数月;产品或员工向下;反之亦然;任意组合)。 (随便问问。)

我认为它甚至可以不言自明,但我会提供说明业务规则的动词短语,只是因为每个都涉及三个父母:

  • 每个员工都被安排了一周的产品销售目标
  • 每个产品都按员工每周安排 SaleTarget
  • 每位员工在当天完成了产品的销售实际情况
  • 每个产品在当天都按员工进行了 SaleActual

比较

  1. 我应该提到的。请注意,没有垂直(行)或水平(列)重复。当列重复时,例如StartDate EndDate,您已经破坏了 3NF(引入了功能依赖),并引入了更新异常。任何一行中的EndDate 是下一行中的StartDate(负1 秒算作欺骗,是一种设计);更新时,现在必须更改两行而不是一行。更重要的是,这种结构非常简单(它不是时间序列或“时间”要求),不需要EndDate

回复评论

  1. 数据模型已更新为包括 MonthYear 要求。您现在需要对 SaleTarget 的检查约束,以确保 DateType 在一周内为 W。加载 Date 表很简单,不需要 SQLTeam 上发布的无意义代码(手动重复剪切和粘贴);他们以愚蠢和不合标准而闻名。

  2. SaleActual 表现在包含每日、每周、每月和每年的值。当然,您可以在每周、每月、每天的第一天以编程方式进行总结。首先将新行添加到Date

  3. 5NF 几乎是当今标准合规性所需的最低要求,因此您需要习惯它。基本上,在 3NF 和 5NF 之间的 NF 的学术界(加上维基百科等发布完全错误条目的地方)存在很多争论。 5NF 的简短定义是 3NF 的本意,零数据重复,零更新异常(没有重复的列以事务方式更新)。

  4. 暂时忘记 6NF。任何属于 6NF 的表都属于 5NF(以及 4NF 和 BCNF 和 3NF)。只需将两个销售表视为 5NF。当你必须写一个透视报告时,比如说一年后,你就会意识到这个结构的价值。

【讨论】:

  • 更新的月份和年份模型会很棒。 5NF 和 6NF 有点吓人:)
【解决方案2】:

我个人会将目标和实际值存储在单独的行中,并且很可能存储在单独的表中:

目标: EmployeeId、PeriodId、ProductId、TargetValue

销售: EmployeeId、PeriodId、ProductId、SalesValue

事实上,在集成系统中,第二个表通常是不必要的(假设您有一个完整的销售记录系统,这应该是实际记录销售的投影/视图 - 适当分配员工、期间和产品基于该子系统的模型)。

为了满足您的日历要求,我几乎可以肯定会有一个日期表,它可以让您确保所有各种业务规则都用于定义周和月,而无需复杂的日期逻辑。然后,只需加入日历表即可确定周期和聚合。

所以 ActualSales 看起来像这样(只有一个通用的 Period 表,它本身可能是一个周期和日期表):

SELECT sp.EmployeeId
       , p.ProductId
       , pd.PeriodType
       , pd.PeriodId
       , SUM(id.Quantity * id.UnitProce) AS TotalSales
FROM Invoice AS i
INNER JOIN InvoiceDetail AS id
    ON id.InvoiceId = i.InvoiceId
INNER JOIN Employee AS sp
    ON sp.EmployeeId = i.SalesPersonId
INNER JOIN Product AS p
    ON id.ProductId = p.ProductId
INNER JOIN Period AS pd
    ON pd.StartDate <= i.InvoiceDate
    AND pd.EndDate > i.InvoiceDate
GROUP BY sp.EmployeeId, p.ProductId, pd.PeriodType, pd.PeriodId

在这种情况下,如果您有重叠的时间段(如每天、每周、每月),数据将会重复,因此您只需要聚合一种类型的时间段 - 这就是为什么我在此示例视图中特别包含它的原因这里是多余的。

我希望一个通用的周期表看起来像:

PeriodId
PeriodType
StartDate
EndDate

这将预先填充您要报告的各个时期:

'Q', 1/1/2010, 4/1/2010
'M', 1/1/2010, 2/1/2010
'M', 2/1/2010, 3/1/2010
'M', 3/1/2010, 4/1/2010
'W', 1/3/2010, 1/10/2010
'W', 1/10/2010, 1/17/2010
etc.
'D', 1/1/2010, 1/2/2010
'D', 1/2/2010, 1/3/2010
etc.

担心假期没有什么意义,只是如果他们不工作,您可能不会分配目标,这主要是关于管理分配,以便它们可能是现实的。你可以有一个带有各种标志的日历表

Calendar
DateId
Date
IsHoliday

然后您可以在加入时将其包括在内,以计算一段时间内的假期/周末数等。

这通常是会计/业务方面的事情,但您可能需要考虑标准化您的日历。例如,在电视广告的媒体购买中,他们使每个“季度”相等,并使每个“月”标准化——4 周、4 周、5 周。显然,他们将假期和特殊电视活动排除在外,但这有助于理顺会计并更轻松地比较类似的时期。

【讨论】:

  • 能否请你指导我关于周期表的属性说我有标准的两天假期和一些公共假期。你能详细说明周期表吗
  • @Tassadaque 周期表就是周期。我添加了一些关于它的细节和一个单独的日历表,当需要考虑日历的变化无常时,您可以使用它来帮助更好地分配目标。
【解决方案3】:

我个人会选择更通用的“周期”表。

期间(期间Id,startDate,endDate,weekNo,Month,Year)

然后添加

empSales(EmployeeId,ProductId,SaleTarget,ActualSale,periodId)。

这更灵活一些(您可以轻松引入不同的时间跨度,或者将相对周字段设为空,或者定义一些将周期映射到“标准”周的规则),冗余更少(请注意月份和星期已从 empSales 表中移出),它允许您进行报告和计算(顺便说一句,您没有包含年份字段,有什么原因吗?)。

统计数据应该更容易,因为假设您有按天排序的销售额,那么在间隔之间总结这些数据会更容易,除非您想在整个数据库中复制“周”字段。

另请注意,您可以轻松地在不同的重叠时期设定目标。

例如,您可以为 11 月 22 日至 28 日设置每周目标(我使用的是欧洲惯例,即每周从星期一开始)并在黑色星期五设置一个特殊的一天时间

所以:

Period:  

 periodId|startDate  |endDate    |weekNo|Month    |  Year|
  0030020|22-NOV-2010|28-NOV-2010|  43  |November | 2010 |
  0030026|26-NOV-2010|26-NOV-2010| null |November | 2010 |

empSales:

 EmployeeId|ProductId|SaleTarget|ActualSale|periodId|
     567689|   788585|      58  |       42 | 0030020|
     567689|   788585|      28  |       32 | 0030026|

请注意员工 567689 如何未能完成他的每周目标,但却成功地完成了他的黑色星期五目标。

顺便说一句,在处理此示例时,我认为您最好删除“empSales”表,将其重命名为“empTargets”:

empTargets(EmployeeId,ProductId,SaleTarget,periodId)。

因为实际销售额很容易使用 UDF 或放置在视图中即时计算 - 毕竟,它只是一个

select sum(items_sold) 
from sales 
where sales.employeeId = empTargets.employeeId and 
      sales.ProductId= empTargets.ProductId and 
      sales.saleDate between empTargets.startDate and 
                             empTargets.endDate)

因此无需将其直接存储在表格中(事实上,如果退回物品或其他未来更正,它可能会成为负担)。

【讨论】:

  • Yes Year 字段也有忘记提及
  • 我的理解是,对于每周目标和每日目标,我需要创建两个时段。如果这是正确的,我怎样才能保存每周分配的每日目标
  • 您的问题有点混乱。也许您最好在问题中提供更详细的示例,以便我可以更好地解释(或撤回我的建议)?
  • @p.marino。 SQL Team 代码是我见过的最糟糕的代码。想象一下,在太空飞行的时代,手动重复。他们不懂向量。你知道他们被写在To Laugh or Cry中。
  • @p.marino。 1)足够公平。 2) OP 有 sql-server 标签。完整的 DATE 函数和算术。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-04-18
  • 2010-10-13
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多