【问题标题】:T-SQL query relative week numbering issue when turning the year翻年时T-SQL查询相对周数问题
【发布时间】:2015-01-13 17:05:43
【问题描述】:

我有这个视图,它有一个特定的相对周编号列,称为 WeekNr。源表中的两列(WeekNr 和 CURWEEK)使用通常的日历周编号:1、2、3... 到 52。

此视图用于 Excel 文件,企业希望查看从 -12 到 +6 的相对周数:过去 12 周和 6 周的投影,相对于第 0 周,即当前周(今天,2015 年 1 月 13 日,这是日历周 2)。

所以,这段代码工作得很好,直到这一年过去了。 52 岁之后,WeekNr - CURWEEK 不再按预期工作。

您对此有什么优雅的解决方案吗?

CREATE VIEW [dbo].[vw_NOSSCE_HealthKPIs_DEV]
 AS 
 (
SELECT 
[Region]
      ,[Country]
      ,[GlobalMaterialCode]
      ,[Material]
      ,[MaterialCode]
      ,[PlantCode]
      ,[Plant]
      ,[Brand]
      ,
      (WeekNr - (SELECT TOP 1 CURWEEK FROM [TO_BDB].[dbo].[TO_BDB_NOSSCE_FACT_NORMALIZED]) ) as 
      WeekNr 
      ,[MEDICAL_NEED]
      ,[TOP_SELLER]
      ,[PHARMABU_CODE]
      ,[PharmaBU]
      ,[SourceLocationCode]
      ,[SourceLocation]
      ,[InventoryTotal]
      ,[MaxStockQtyTotal]
      ,[TotalSafetyStockTotal]
      ,[DangerTreshold]
      ,[FORECAST_FLG]
      ,[DangerZoneDesc]
 FROM [TO_BDB].[dbo].[TO_BDB_NOSSCE_PAST12_HIST] 
 WHERE (WeekNr - (SELECT TOP 1 CURWEEK
 FROM [TO_BDB].[dbo].[TO_BDB_NOSSCE_FACT_NORMALIZED]) )
  BETWEEN -12 and 6
  GROUP BY [Region]
      ,[Country]
      ,[GlobalMaterialCode]
      ,[Material]
      ,[MaterialCode]
      ,[PlantCode]
      ,[Plant]
      ,[Brand]
,WeekNr, [MEDICAL_NEED]
      ,[TOP_SELLER]
      ,[PHARMABU_CODE]
      ,[PharmaBU]
      ,[SourceLocationCode]
      ,[SourceLocation]
      ,[InventoryTotal]
      ,[MaxStockQtyTotal]
      ,[TotalSafetyStockTotal]
      ,[DangerTreshold]
      ,[FORECAST_FLG]
      ,[DangerZoneDesc]
 UNION ALL
 SELECT * FROM [dbo].[vw_NOSSCE_HealthKPIs] WHERE WeekNr > 0
)

更新,我创建了日历表,为什么它给了我第 53 周?

CREATE TABLE [dbo].[TO_BDB_NOSSCE_CALENDAR](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [DATE] [date] NOT NULL,
    [YEAR]  AS (datepart(year,[DATE])) PERSISTED,
    [SEMESTER]  AS (case when datepart(month,[DATE])<(7) then '1' else '2' end) PERSISTED NOT NULL,
    [TRIMESTER]  AS (case when datepart(month,[DATE])<(4) then '1' else case when datepart(month,[DATE])<(7) then '2' else case when datepart(month,[DATE])<(10) then '3' else '4' end end end) PERSISTED NOT NULL,
    [MONTH]  AS (case when len(CONVERT([varchar](2),datepart(month,[DATE]),0))=(1) then '0'+CONVERT([varchar](2),datepart(month,[DATE]),0) else CONVERT([varchar](2),datepart(month,[DATE]),0) end) PERSISTED,
    [WEEK]  AS (case when len(CONVERT([varchar](2),datepart(week,[DATE]),0))=(1) then '0'+CONVERT([varchar](2),datepart(week,[DATE]),0) else CONVERT([varchar](2),datepart(week,[DATE]),0) end),
    [DAY]  AS (case when len(CONVERT([varchar](2),datepart(day,[DATE]),0))=(1) then '0'+CONVERT([varchar](2),datepart(day,[DATE]),0) else CONVERT([varchar](2),datepart(day,[DATE]),0) end) PERSISTED,
    [WEEKNUMBER]  AS (datepart(week,[DATE])),
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

【问题讨论】:

  • 我想说的是 (WeekNr - CURWEEK + 52) % 52,这就是我用来计算接近 0 或 360 的角度差的方法,我认为这与您遇到的问题相同。
  • 您能否向我们提供一些示例数据,说明它现在在本周的情况?在解决问题时查看它的外观会有所帮助。

标签: sql sql-server tsql


【解决方案1】:

首先,如果您使用代码生成 Excel 文件,最好使用 Excel 公式进行相对编号,而不是使用 SQL。

第二,周数不能确定周数。要确定一周,您需要年份和周数,或者您需要日期。这意味着您可能需要更改表格以存储除星期之外的年份。

这里我们使用日历表来简化这样的查询。一个最小的、简化的日历表可能如下所示。我们使用 ISO 年和周,但不要让您感到困惑。您可以使用几乎任何类型的编号。 (为 PostgreSQL 编写,但原理与 SQL Server 相同。)

create table calendar (
  cal_date date primary key,
  iso_year integer not null,
  iso_week integer not null
);

create index on calendar (iso_year, iso_week);

我们使用检查约束(未显示)来保证 iso_year 和 iso_week 的值对于 cal_date 的每个值都是正确的。极少数(高度信任的)人有权插入和删除行。

在标准 SQL 中,我可能会写这样的东西来获得我们感兴趣的绝对周数。

select distinct iso_year, iso_week
from calendar
where cal_date between cast(current_date - interval '12 weeks' as date) 
                   and cast(current_date + interval  '6 weeks' as date) 
order by iso_year, iso_week;
iso_year iso_week 2014 43 2014 44 2014 45 ... 2015 7 2015 8 2015 9

它返回 19 行,这似乎是适合您的情况的数字。 (注意我的错误。)把它放到一个通用的表表达式或视图中,你可以用简单的算术计算相对周数。 (我使用了 CTE,所以这个答案或多或少是独立的。)

with absolute_weeks as (
  select distinct iso_year, iso_week
  from calendar
  where cal_date between cast(current_date - interval '12 weeks' as date) 
                     and cast(current_date + interval  '6 weeks' as date) 
)
select *, (row_number() over (order by iso_year, iso_week) - 13) as relative_week
from absolute_weeks 
order by iso_year, iso_week;
iso_year iso_week relative_week -- 2014 43 -12 2014 44 -11 2014 45 -10 ... 2015 7 4 2015 8 5 2015 9 6

如果您的数据包含年份和星期,您可以在 iso_year 和 iso_week 上将您的查询加入到此。如果您的数据包含日期,您可以将之前的查询移动到 CTE 或视图中,并加入日历表以获取每周的日期。

with absolute_weeks as (
  select distinct iso_year, iso_week
  from calendar
  where cal_date between cast(current_date - interval '12 weeks' as date) 
                     and cast(current_date + interval  '6 weeks' as date) 
), relative_weeks as (
  select *, (row_number() over (order by iso_year, iso_week) - 13) as relative_week
  from absolute_weeks 
)
select c.cal_date, c.iso_year, c.iso_week, r.relative_week
from calendar c
inner join relative_weeks r on c.iso_year = r.iso_year and c.iso_week = r.iso_week
order by c.cal_date;
cal_date iso_year iso_week relative_week -- 2014-10-20 2014 43 -12 2014-10-21 2014 43 -12 2014-10-22 2014 43 -12 ... 2015-01-12 2015 3 0 2015-01-13 2015 3 0 2015-01-14 2015 3 0 ... 2015-02-27 2015 9 6 2015-02-28 2015 9 6 2015-03-01 2015 9 6

当前周(2015 年 1 月 13 日这一周)是 ISO 第 3 周,而不是您编号的第 2 周。你自己的日历表应该反映你自己的逻辑,而不是我的。

此查询返回 133 行,这似乎是正确的数字。每个 ISO 周有 7 天,因此 7 * 19 行是基于 ISO 周的日历表的正确数字。您自己的要求可能会给您一个偶尔有不同天数的一周。

【讨论】:

  • 您好,迈克,感谢您的贡献。将其传递给 Excel 部分会很好,但目前还不可能。 CTE 的解决方案看起来不错,尽管我没有可用的日历日期。我在名为 CURWEEK 和 CALWEEK 的原始数据表中只有一个 nvarchar(32) 字符串,例如。 '02.2015' 。我想我会使用 GETDATE() 作为 cal_date,如果我的理解是正确的,CURRENT week,从周一到周日,等于来自 GETDATE() 的周数。
  • 您可以从日历表中即时构建像“02.2015”这样的表达式。 (或者将它们与合适的检查约束一起存储在在日历表中。)对于 SQL Server,将表达式 right('00'+ convert(varchar, iso_week),2) + '.' + cast(iso_year as varchar) 添加到“absolute_weeks”应该会给你一个可以加入的列。跨度>
  • 我有一个日历表,你能告诉我为什么它给了我 53 周吗?我用这个表结构更新了上面的代码。
  • 你(我)有第 53 周,因为52 * 7 = 364。每年超过 364 天。您的公司可能有一个规则,即第 52 周是每年的最后一周,而第 52 周可能有超过 7 天。它可能有一些其他类型的规则。无论如何,您自己的日历表应该反映您自己公司的逻辑。 (ISO weeks基于不同的逻辑。)
  • 这是否意味着我无法创建包含计算列的表,因为它们不是 ISO?无论如何,我的公司也只使用 52 周。
猜你喜欢
  • 1970-01-01
  • 2011-05-21
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 1970-01-01
  • 2018-01-17
  • 2021-07-01
相关资源
最近更新 更多