【问题标题】:Sql query to overlay date/period overrides over default dates覆盖默认日期的 Sql 查询覆盖日期/期间
【发布时间】:2009-10-28 10:14:13
【问题描述】:

关于构建 Sql Server (2008) 查询的任何想法都会告诉我“基于默认值或覆盖存在的项目的日期特定价格”

因此,默认表可能如下所示 - 列价格、开始日期、结束日期(yyyy-M-d):

Default:  $10, 2010-1-1, 2010-2-1

这样的覆盖表:

Override: $12, 2010-1-5, 2010-1-8

查询将返回:

Result:   $10, 2010-1-1, 2010-1-4
          $12, 2010-1-5, 2010-1-8
          $10, 2010-1-9, 2010-2-1

我可能会将其包装在存储过程或函数中,并在特定日期范围内调用它。

【问题讨论】:

  • 如何生成输出?它与顶部的斜体相矛盾

标签: sql sql-server overriding


【解决方案1】:

类似:

SELECT
   D.Price, ISNULL(O.StartDate, D.StartDate), ISNULL(O.EndDate, D.EndDate)
FROM
   Default D
   LEFT JOIN
   Override O ON D.Price= O.Price

【讨论】:

  • 我认为这行不通,因为发布者希望将默认行拆分为位于覆盖两侧的数据(即在示例中:从单个行中分离出三行)。
【解决方案2】:

正确的设计是只有一张桌子。您根本不想要覆盖表。只需将所有内容保存在单个表中 - 受日期范围的限制。查询也变得更加简单。

你的表结构变成了

CREATE TABLE Rates
(ID INT NOT NULL,
 Rate Decimal NOT NULL,
 FromDate NOT NULL,
 ToDate   NOT NULL,
 CONSTRAINT PK_RATES (ID,FromDate,ToDate))

那么查询就变成了

SELECT Rate FROM Rates WHERE ID = @ID AND FromDate = (SELECT MAX(FromDate) FROM Rates WHERE ID = @ID AND FromDate <= @Date) AND ToDate = (SELECT MIN(ToDate) FROM Rates WHERE ID = @ID AND ToDate >=@Date) 

【讨论】:

    【解决方案3】:

    我认为你将不得不做这样的事情(假设你想保持覆盖日期分开,并且你想避免任何程序):

    1. 定义并填充实用程序表,列出可能与您相关的每个日期
    2. 构造一个 SELECT,将这个实用程序表中的每个日期“标记”为 i) 属于覆盖日期或 ii) 属于默认日期
    3. 按日期和“标记”将此 SELECT 的结果分组
    4. 将这些结果加入到相应的价格信息中

    【讨论】:

      【解决方案4】:

      有点晚了,但正在寻找类似问题的解决方案,但没有找到答案,所以试图做一个。

      一种解决方案是尝试调整第一组数据以适应第二组数据,在最后一步中,我们将合并这两组数据,调整后的数据与叠加数据。

      所以我们需要调整默认设置,这需要几个步骤。

      假设:-

      默认期间不重叠。 重叠时期不会与自身重叠。

      重叠数据必须有超过一天的差距,以确保任何调整都不会影响另一个重叠期;我们不会丢失相关的覆盖值,因为我们会在最后一步中取回它们。

      对于部分重叠的默认周期(重叠大于或等于默认周期)

      如果一个期间的开始日期与任何重叠期间重叠,我们会将开始日期更改为重叠期间的结束加上一天

      Default:                                |<-------- P1 ------>|
      Overlap:               |<-----------O1------>|
      =================================================================
      Output:                                       |<-----P1 ---->|         
      

      如果期间结束日期与任何重叠期间重叠,我们会将结束日期更改为重叠期间的开始减去一天

      Default:                |<-------- P1 ------>|
      Overlap:                               |<-----------O1------>|
      ===================================================================
      Output:                 |<--- P1 ---->|
      

      如果默认时间段完全被重叠期覆盖,则删除它

      Default:                 |<--- P1 --->|
      Overlap:              |<--------O1------>|
      ===================================================================
      Output:                       nothing
      

      对于被重叠周期打破的默认周期(重叠小于周期) 获取第一部分(从期初到第一个重叠期) 获取中间部分,即从任何重叠期结束到下一个重叠期开始。 获取最后一部分(从最后重叠到期末)

      Default:      |<---------------------------- P1 ------------------------------>|
      Overlap:              |<---O1--->|         |<---O2-->|        |<--O3->|
      ===================================================================
      Output:       |<-P1->|             |<-P1->|             |<-P1->|        |<-P1->|
      

      最后合并数据,得到结果 我们正在考虑的重叠类型

      Default:                |<---- P1 --->|
      Overlap:                               |<-----------O1------>|
      ===================================================================
      Output:                 |<---- P1 --->||<-----------O1------>|
      

      让我们构建 T-SQL

      With [System] as (
             Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
             Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
             Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
             Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
             Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
             Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
      ),Overrides as (
             Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
             Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
             Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
             Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
             Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
             Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
             Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
      ),PrepareOverridePeriods as (--if override periods have no gabs betwwen we need to merge them
          Select p1.StartDate, p1.EndDate
          from Overrides p1
                    left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
          where p2.StartDate is null
          union all
          select p1.StartDate,p2.EndDate
          from PrepareOverridePeriods p1
          inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
      ),OverridePeriods as (
             select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
             from PrepareOverridePeriods group by StartDate
      ),AdjustedPeriods as (
             select s.RowNum,'Adj.' [type]
             ,isnull(dateadd(day,1,ShiftRight.EndDate),s.StartDate) [StartDate]
             ,isnull(dateadd(day,-1,ShiftLeft.StartDate),s.EndDate) [EndDate]
             ,s.Value
             from System s
                    left outer join OverridePeriods ShiftRight on s.StartDate between ShiftRight.StartDate and ShiftRight.EndDate
                    left outer join OverridePeriods ShiftLeft on s.EndDate between ShiftLeft.StartDate and ShiftLeft.EndDate
                    left outer join OverridePeriods RemovePeriod on s.StartDate between RemovePeriod.StartDate and RemovePeriod.EndDate and s.EndDate between RemovePeriod.StartDate and RemovePeriod.EndDate
             where RemovePeriod.StartDate is null
      ),SmallOverrides as ( --TODO: change SystemCalculated to AdjustSystemCalculatedPeriods
             select ROW_NUMBER() over (partition by s.RowNum order by o.StartDate ) [RowNum],
                     o.RowNum [OverrideRowNum],o.StartDate [OverrideStartDate],o.EndDate [OverrideEndDate],s.Value [Value]
                    ,s.RowNum [SystemRowNum],s.StartDate [SystemStartDate],s.EndDate [SystemEndDate]
             from OverridePeriods o
                    inner join AdjustedPeriods s on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
      )
      --,FirstAndLastParts as (
      --select [SystemRowNum],[type]
      --     ,case when [type]='First' then min([SystemStartDate]) else dateadd(day,1,max(OverrideEndDate)) end [StartDate]
      --     ,case when [type]='First' then dateadd(day,-1,min(OverrideStartDate)) else max([SystemEndDate]) end [EndDate]
      --     ,min(Value) [Value]
      --     from (select *,'First' [type]     from SmallOverrides o union all
      --              select *,'Last' [type] from SmallOverrides o) data
      --     group by [SystemRowNum],[type]
      --)
      ,FirstParts as (
             select [SystemRowNum],'First' [type]
                    ,min([SystemStartDate]) [StartDate]
                    ,dateadd(day,-1,min(OverrideStartDate)) [EndDate]
                    ,min(Value) [Value]
             from SmallOverrides
             group by [SystemRowNum]
      ),LastParts as (
             select [SystemRowNum],'Last' [type]
                    ,dateadd(day,1,max(OverrideEndDate))  [StartDate]
                    ,max([SystemEndDate])  [EndDate]
                    ,min(Value) [Value]
                    from SmallOverrides
             group by [SystemRowNum]
      ),IntermediatParts as (
      select s.SystemRowNum [RowNum],'Inter.' [type]
             ,dateadd(day,1,s.OverrideEndDate) [StartDate]
             ,dateadd(day,-1,e.OverrideStartDate) [EndDate]
             ,s.Value
             from SmallOverrides s
             left outer join SmallOverrides e on e.SystemRowNum=s.SystemRowNum and s.RowNum+1=e.RowNum
             where e.RowNum is not null --remove the first and lasts
      ),AdjustedPeriodsFiltered as (--remove blocks that are broken to smaller pieces
             select s.*
                    from AdjustedPeriods s
                    left outer join OverridePeriods o on o.StartDate between s.StartDate and s.EndDate and o.EndDate between s.StartDate and s.EndDate
                    where o.StartDate is null
      ),AllParts as (
             select * from IntermediatParts union all --order by SystemRowNum,OverrideStartDate
             select * from FirstParts union all
             select * from LastParts union all
             select * from AdjustedPeriodsFiltered
      ),Merged as (
             select [RowNum],[type] [Source],StartDate,EndDate,Value,'System' [RecordType] from AllParts
             union all
             select [RowNum],'override' [Source],StartDate,EndDate,Value,'Override' [RecordType] from Overrides
      )
      select * from Merged order by StartDate
      

      我们能否以不同的方式调整数据集,嗯,是的,另一种方法是从默认时段和覆盖时段的开始日期和结束日期获取所有预期值,然后重建一组新的时段,链接将其设置为默认值,将其与叠加层合并,我们得到了它。 相同的假设和步骤如下:-

      With [System] as (
             Select 1 [RowNum],cast('Jan 01,2017' as date) [StartDate],dateadd(day,-1,cast('Feb 01,2017' as date)) [EndDate],0500 [Value] union all
             Select 2 [RowNum],cast('Feb 01,2017' as date) [StartDate],dateadd(day,-1,cast('Mar 01,2017' as date)) [EndDate],0700 [Value] union all
             Select 3 [RowNum],cast('Mar 01,2017' as date) [StartDate],dateadd(day,-1,cast('Apr 01,2017' as date)) [EndDate],0900 [Value] union all
             Select 4 [RowNum],cast('Apr 01,2017' as date) [StartDate],dateadd(day,-1,cast('May 01,2017' as date)) [EndDate],0700 [Value] union all
             Select 5 [RowNum],cast('May 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jun 01,2017' as date)) [EndDate],0900 [Value] union all
             Select 6 [RowNum],cast('Jun 01,2017' as date) [StartDate],dateadd(day,-1,cast('Jul 01,9999' as date)) [EndDate],1500 [Value]
      ),Overrides as (
             Select 1 [RowNum],cast('Feb 12,2017' as date) [StartDate],cast('Mar 25,2017' as date) [EndDate],1 [Value] union all
             Select 2 [RowNum],cast('Mar 28,2017' as date) [StartDate],cast('May 15,2017' as date) [EndDate],2 [Value] union all
             Select 3 [RowNum],cast('May 18,2017' as date) [StartDate],cast('May 20,2017' as date) [EndDate],3 [Value] union all
             Select 4 [RowNum],cast('Jun 05,2017' as date) [StartDate],cast('Jun 08,2017' as date) [EndDate],4 [Value] union all
             Select 5 [RowNum],cast('Jun 09,2017' as date) [StartDate],cast('Jun 16,2017' as date) [EndDate],5 [Value] union all
             Select 6 [RowNum],cast('Jun 17,2017' as date) [StartDate],cast('Jun 22,2017' as date) [EndDate],6 [Value] union all
             Select 7 [RowNum],cast('Jun 23,2017' as date) [StartDate],cast('Jun 27,2017' as date) [EndDate],7 [Value]
      ),PrepareOverridePeriods as (--if override periods have no gabs between we need to merge them
          Select p1.StartDate, p1.EndDate
          from Overrides p1
                    left join Overrides p2 on p1.StartDate = DATEADD(day,1,p2.EndDate)
          where p2.StartDate is null
          union all
          select p1.StartDate,p2.EndDate
          from PrepareOverridePeriods p1
          inner join Overrides p2 on p1.EndDate = DATEADD(day,-1,p2.StartDate)
      ),OverridePeriods as (
             select ROW_NUMBER() over (order by StartDate) [RowNum],StartDate,MAX(EndDate) as EndDate
             from PrepareOverridePeriods group by StartDate
      )
      ,AllDates as (
             select ROW_NUMBER() over (order by [Date]) [RowNum],data.Date from (
             select dateadd(day,-1,OverridePeriods.StartDate) [Date] from OverridePeriods union all
             select dateadd(day,+1,OverridePeriods.EndDate) [Date] from OverridePeriods union all
             select StartDate [Date] from [System] union all
             select EndDate [Date] from [System] ) as data
      )
      ,NewPeriods as (
      select sy.RowNum, s.[Date] [StartDate],n.[Date] [EndDate] ,sy.Value
             from AllDates s
                    left outer join AllDates n on n.RowNum=s.RowNum+1
                    left outer join OverridePeriods o on s.[Date] between o.StartDate and o.EndDate and n.[Date] between o.StartDate and o.EndDate
                    left outer join [System] sy on s.[Date] between sy.StartDate and sy.EndDate
             where
             s.RowNum % 2 =1 and o.StartDate is null--group it by 2 and remove overriden areas
      )
      ,Merged2 as (
             select [RowNum], StartDate,EndDate,Value,'System' [RecordType] from NewPeriods
             union all
             select [RowNum], StartDate,EndDate,Value,'Override' [RecordType] from Overrides
      )
      select * from Merged2 order by StartDate
      

      我确信可能有另一种方法可以通过某种递归方法实现所需的结果,但现在这对我有用。

      对于最后一步,如果值相同,我们可以尝试合并结果,但我认为没有要求。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-09-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-07-16
        • 2016-07-16
        相关资源
        最近更新 更多