NRF 零售日历是专门为解决销售比较的业务问题而创建的 -零售行业在 1930 年代通过将日历标准化为“4-5-4”日历解决了这个问题,其中每个三个月的第一个月有 4周,第二个有 5 周,第三个有 4 个,四个季度共 52 周,每年 364 天。他们通过定期制作 53 周年来解决另一个问题 -更多详细信息,请参见下文)。它考虑了闰年,并确保总是将星期五与星期五进行比较。
4-5-4 日历的目的是什么?
4-5-4 日历可作为零售行业的自愿指南,通过根据 4 周 - 5 周 - 4 周的格式将年份划分为月份,确保年份之间的销售可比性。日历的布局将假期排成一行,并确保可比月份中的周六和周日数量相同。因此,出于销售报告的目的,将相似天数与相似天数进行比较。
第 1 步:设置
通过将此日历建模到某个dbo.RetailTime
表格中,您可以更轻松地比较从正确日期开始的销售额 -TY week 26 day 6
比较LY week 26 day 6
,无论哪个实际日历日期是。日历是时间概念的抽象。
像这样的东西:
public interface IRetailTime
{
DateTime Date { get; } // does not have "time" info ("00:00:00")
int DayHour { get; }
int WeekDay { get; }
int YearWeek { get; }
int YearMonth { get; }
int YearQuarter { get; }
int Year { get; }
}
您可以通过添加字段QuarterDay
、QuarterWeek
、QuarterMonth
、MonthDay
和来进一步充实这一点MonthWeek
,具体取决于您的报告需求。零售商通常将Year
与 连接起来YearWeek
以标识每个日历周,因此 2013 年的第 26 周将是“201326”。
然后你编写一个脚本来将 NRF 日历数据导入到你的模型中,你可以创建一个函数、存储过程、视图或其他任何东西,为你提供RetailTimeId
forLY
和,哎呀为什么不,为LLY
(2 年前)字段(这可以都为空)为Id
您的日历表中的每一个。
结果给你这样的东西(下面假设小时级别的粒度,每天 24 小时):
RetailTimeId LYId LLYId
1 NULL NULL
2 NULL NULL
... ... ...
8737 1 NULL
8738 2 NULL
... ... ...
17472 8737 1
17473 8738 2
... ... ...
这为您提供了一个查找表(将其持久化到一个实际的dbo.RetailTimeLookup
表中不会受到伤害),其中包含一个Id
用于 LY 和 LLY 的表,用于dbo.RetailTime
表中的每个 Id ( RetailTimeId
)。您需要RetailTimeId
列上的唯一索引,而不是其他两个列上的唯一索引,因为有 53 周年,您可能希望将第 53 周与同年的第 1 周进行比较。
第 2 步:与您的销售数据相关联
下一步是通过将“日期”部分(没有“时间”部分)与“时间”部分(只是小时)与匹配来查找Id
与您对应的。这可能是一项昂贵的操作,您可能更喜欢安排一个通宵流程 (ETL),该流程将使用已查找的数据填充“SalesInfo”数据表,因此销售数据的格式如下:PaymentDate
RetailTime.Date
RetailTime.DayHour
RetailTimeId
PaymentDate
public interface ISalesInfo
{
int RetailTimeId { get; }
int UnitsSold { get; }
}
所缺少的只是从上方加入 TY/LY/LLY 查找视图,您现在可以在“时间”维度上“切片”您的销售数据——我曾经查看今年的销售额和去年的另一个视图最低粒度级别的销售,如下所示:
CREATE VIEW vwSALES_TY AS
BEGIN
SELECT t.Id RetailTimeId,
t.Year,
t.YearQuarter,
t.YearMonth,
t.YearWeek,
t.WeekDay,
t.DayHour,
sales.UnitsSold Units -- total units sold
--,sales.CostAmount CostAmount -- cost value of sold units
--,sales.RetailAmount RetailAmount -- full-price value of sold units
--,sales.CurrentAmount CurrentAmount -- actual sale value of sold units
FROM dbo.RetailTime t
INNER JOIN dbo.SalesInfo sales ON t.Id = sales.RetailTimeId
WHERE t.Year = 2013
END
CREATE VIEW vwSALES_LY AS
BEGIN
SELECT t.Id RetailTimeId,
t.Year,
t.YearQuarter,
t.YearMonth,
t.YearWeek,
t.WeekDay,
t.DayHour,
sales.UnitsSold Units -- total units sold
--,sales.CostAmount CostAmount -- cost value of sold units
--,sales.RetailAmount RetailAmount -- full-price value of sold units
--,sales.CurrentAmount CurrentAmount -- actual sale value of sold units
FROM dbo.RetailTime t
INNER JOIN dbo.SalesInfo sales ON t.Id = sales.RetailTimeId
WHERE t.Year = 2012
END
数字的含义
我将CostAmount、RetailAmount和CurrentAmount放在那里,因为从商业角度来看,知道已售出的单位是件好事,但它并不能告诉您这些销售的利润有多大——如果您以高折扣,您的毛利率 (GM%) 可能非常微薄甚至为负,而销售 TY 单位数量的一半可能会变成一个好得多的情况……如果库存以健康的速度转动 -每一点信息都与另一种、一种或另一种方式相关。
GM% 是- 这是每件西装都需要知道(1-CostAmount/CurrentAmount)*100
的盈利能力数据。%Discount 是- 这就是您的销售折扣。仅“售出单位”的数字并不能说明什么。零售界有句谚语“销售是为了虚荣,利润是为了理智”。但我在飘。我们的想法是在您的详细销售数据中包含尽可能多的信息——这确实包括产品(理想的是 SKU)、销售点甚至客户信息(如果有的话)。任何遗漏的东西都永远不会出现在报告中。(1-CurrentAmount/RetailAmount)*100
第 3 步:……利润!
一个视图可以为您提供 TY 销售,另一个可以为您提供 LY 销售准备好排队,剩下要做的就是询问数据库:
SELECT t.Year,
t.YearQuarter,
t.YearMonth,
t.YearWeek,
t.WeekDay,
t.DayHour,
SUM(ISNULL(ty.Units,0)) UnitsTY,
SUM(ISNULL(ly.Units,0)) UnitsLY
FROM dbo.RetailTime t
INNER JOIN dbo.RetailTimeLookup lookup ON t.Id = lookup.RetailTimeId
LEFT JOIN dbo.vwSALES_TY ty ON lookup.RetailTimeId = ty.RetailTimeId
LEFT JOIN dbo.vwSALES_LY ly ON lookup.LYId = ly.RetailTimeId
WHERE t.Year = 2013
现在,这将为您提供 2013 年零售日历年每一天的每一小时的 TY 与 LY(保留 2012 年的历史,而 2013 年还没有记录),但这还不是您想要的,尽管所有信息都已经存在。
如果您将上述内容选择到临时表中(或将其用作子查询),则需要执行以下操作才能仅获取您感兴趣的数字:
SELECT t.DayHour,
SUM(lw.UnitsTY) LastWeekUnitsTY,
SUM(lw.UnitsLY) LastWeekUnitsLY,
SUM(tw.UnitsTY) ThisWeekUnitsTY,
SUM(tw.UnitsLY) ThisWeekUnitsLY
FROM (SELECT DayHour FROM #above GROUP BY DayHour) t
LEFT JOIN (SELECT UnitsTY, UnitsLY
FROM #above
WHERE YearWeek = 25 AND WeekDay = 6) lw
ON t.DayHour = lw.DayHour
LEFT JOIN (SELECT UnitsTY, UnitsLY
FROM #above
WHERE YearWeek = 26 AND WeekDay = 6) tw
ON t.DayHour = tw.DayHour
GROUP BY t.DayHour
...但这只是比较周五的销售额。如果您想计算与上一年一致的本周至今 (WTD) 金额,您只需在两个子句中替换WeekDay = 6
为。这就是我放s 和 a的原因。WeekDay <= 6
WHERE
SUM
GROUP BY
笔记
TY 和 LY 之间的百分比方差为(TY/LY - 1) * 100
。如果您有多个销售点 (/store),则 LY 的商店数量可能少于 TY 的数量,这会阻碍比较。零售商已经解决了门对门百分比差异的另一个问题,通常称为“补偿增加”。这是通过不仅排列When(“时间”维度),而且还排列Where(“商店”维度)来实现的,仅计算LY开设的商店,忽略“非comp商店”。对于分解产品层次结构的报表,What还需要连接一些产品数据。
最后一件事
这个想法是将苹果与苹果进行比较——你需要提取这些数字是有原因的:每个零售商都想知道他们是否比 LY 数据有所改善。任何人都可以将两个数字相除并得出一个百分比数字。不幸的是,在现实的商业世界中,报告准确的数据并不总是那么简单。
免责声明:我在零售行业工作了 9 年。