【问题标题】:Find the minimum date in a range of dates with sql用sql查找日期范围内的最小日期
【发布时间】:2011-11-10 17:38:43
【问题描述】:

所以,我有一些具有开始日期和结束日期的历史记录表。问题是,该表中有几条记录指向同一事物,但它们的开始日期和结束日期并不准确。所以,我正在尝试统一他们的开始和结束日期。

因此,每组记录的开始日期和结束日期都很接近(大约 7 秒内)。然后会有另一个集群,具有相同的键(在本例中为 VoyageID),但有一组不同的关闭开始日期和结束日期。那有意义吗?如果没有,我可以发布一些示例数据。

无论如何,我现在的目标是找到每个集群的最短开始日期。我现在拥有的让我获得了每个 VoyageID 的最低要求。任何帮助,将不胜感激。谢谢!

这是我所拥有的:

DECLARE @7S DATETIME
SET @7S = '0:0:07'

PRINT @7S

SELECT MAX(T1.BeginDate), T1.VoyageID FROM
hist.VoyageProfitLossValues T1 INNER JOIN
hist.VoyageProfitLossValues T2 ON
T1.VoyageID = T2.VoyageID AND
T1.BeginDate BETWEEN (T2.BeginDate - @7S) and (T2.BeginDate + @7S)
GROUP BY T1.VoyageID

编辑:示例数据:

BeginDate                   EndDate                    VoyageID
2011-07-05 07:02:50.713     2011-07-05 07:25:53.007    6312
2011-07-05 07:02:50.870     2011-07-05 07:25:53.693    6312
2011-07-05 07:02:51.027     2011-07-05 07:25:54.387    6312
2011-07-08 14:22:21.147     NULL                       6312
2011-07-08 14:22:21.163     NULL                       6312
2011-07-08 14:22:21.177     NULL                       6312

注意:每个航次的真实数据多于3个,BeginDates可以相差更远。

我想摆脱这个:

BeginDate                   VoyageID
2011-07-05 07:02:50.713     6312
2011-07-08 14:22:21.147     6312

我所拥有的只会给我第一行。

我最终也会使用结束日期来执行此操作,但我可以轻松地将一个转换为另一个。

【问题讨论】:

  • 样本数据和预期输出会有所帮助。
  • 您是说要GROUP BY 7 秒范围内的日期吗?
  • 我不确定我有这个。您的列名为 BeginDate,但实际上有一半是结束日期,现在您想将它们配对吗?
  • 如果开始日期在范围内,但结束日期超出范围,会发生什么?如果有两组不同的开始,但(实际上)只有一个结束范围呢?
  • 我对其进行了编辑,希望使其更清晰。

标签: sql sql-server-2008


【解决方案1】:

此解决方案的想法是为每个VoyageID 在BeginDate 上排序您的行。从顶部开始,选择与上一行的时间差异超过 7 秒的行。

@Voy 代替 hist.VoyageProfitLossValues。首先,我创建一个临时表#T,它将用每个VoyageID 的有序值填充ID 列。 C 是一个递归 CTE,它从 ID = 1 开始,遍历所有行,将当前行与前一行进行比较,并将结果存储在 FirstDate 列中。我在示例数据中添加了第二个VoyageID,只是为了证明它也适用。

declare @Voy table
(
  BeginDate datetime,
  EndDate datetime,
  VoyageID int
)

insert into @Voy values  
('2011-07-05 07:02:50.713',     '2011-07-05 07:25:53.007',    6312),
('2011-07-05 07:02:50.870',     '2011-07-05 07:25:53.693',    6312),
('2011-07-05 07:02:51.027',     '2011-07-05 07:25:54.387',    6312),
('2011-07-08 14:22:21.147',      NULL                    ,    6312),
('2011-07-08 14:22:21.163',      NULL                    ,    6312),
('2011-07-08 14:22:21.177',      NULL                    ,    6312),
('2011-07-05 07:02:50.713',     '2011-07-05 07:25:53.007',    6313),
('2011-07-05 07:02:50.870',     '2011-07-05 07:25:53.693',    6313),
('2011-07-05 07:02:51.027',     '2011-07-05 07:25:54.387',    6313),
('2011-07-08 14:22:21.147',      NULL                    ,    6313),
('2011-07-08 14:22:21.163',      NULL                    ,    6313),
('2011-07-08 14:22:21.177',      NULL                    ,    6313)


create table #T
(
  ID int,
  VoyageID int,
  BeginDate datetime
  primary key (ID, VoyageID)
)

insert into #T (ID, VoyageID, BeginDate)
select row_number() over(partition by VoyageID order by BeginDate),
       VoyageID,
       BeginDate
from @Voy     


;with C as
(
  select T.ID,
         T.VoyageID,
         T.BeginDate,
         1 as FirstDate
  from #T as T
  where T.ID = 1
  union all
  select T.ID,
         T.VoyageID,
         T.BeginDate,
         case when datediff(second, C.BeginDate, T.BeginDate) > 7 then 1 else 0 end
  from #T as T
    inner join C
      on T.ID = C.ID + 1 and
         T.VoyageID = C.VoyageID
)
select C.BeginDate,
       C.VoyageID
from C
where C.FirstDate = 1
order by C.VoyageID,
         C.BeginDate
option (maxrecursion 0)


drop table #T

结果:

BeginDate               VoyageID
----------------------- -----------
2011-07-05 07:02:50.713 6312
2011-07-08 14:22:21.147 6312
2011-07-05 07:02:50.713 6313
2011-07-08 14:22:21.147 6313

【讨论】:

  • 其实,没关系。由于递归过多,这在实际数据上失败。 ("在语句完成之前,最大递归 100 已经用完。")
  • 注意:这个表格有 >400000 行
  • @ColinDeClue 在查询末尾添加option (maxrecursion 0)。
  • 我在玩最大递归。不知道为什么我没有意识到可以有一个无限制的选项。
【解决方案2】:

这种方法使用光标。我不知道这对您是否有效:

create table #datacluster ( 
    dateCluster datetime, 
    dateV datetime primary key)

DECLARE @7S DATETIME
DECLARE @base DATETIME
DECLARE @begindate DATETIME

SELECT @base = SYSDATETIME()
SET @7S = '0:0:07'

DECLARE cursor1 CURSOR 
FAST_FORWARD READ_ONLY FOR    
SELECT distinct T1.BeginDate 
FROM
  hist.VoyageProfitLossValues T1 
ORDER BY  T1.BeginDate DESC

FETCH NEXT FROM cursor1 
INTO @begindate;    

WHILE @@FETCH_STATUS = 0
BEGIN

  IF @base - @7S > @begindate
  BEGIN
    set @base = @begindate
  END
  insert into #datacluster ( dateCluster, dateV) 
  values (@base,  @begindate)

  FETCH NEXT FROM cursor1 
  INTO @begindate;    
END

从#dataCluster 更新 VoyageProfitLossValues 表:

UPDATE hist.VoyageProfitLossValues 
SET BeginDate = (
   SELECT C.BeginDate 
   FROM #datacluster C 
   WHERE 
      C.dateV = hist.VoyageProfitLossValues.BeginDate 
  )

注 1:未经测试!!

优化:

临时表的主键。 快进只读光标。

【讨论】:

  • 我所拥有的并没有给我想要的。它只给了我每个 VoyageID 的所有日期集群的最大值,而不是每个集群的最大值。
  • 如果您想在没有 VoyageID 的情况下进行集群,为什么要在查询中包含 VoyageID?我会修改您的查询,只是为了知道我是否理解正确。如果没有,我将删除我的答案。
  • 每个 VoyageID 都有单独的 BeginDate 集群。 (每次航行的几个历史记录)。我需要 VoyageID 来(最终)编写更新部分(看起来和你的很相似)
  • 让我知道这是否适合您。如果没有,让我删除我的帖子。
  • 除了速度很慢(因为它必须为列表中的每一项工作,而不是作为一组工作),我认为它应该工作。我会测试它并告诉你。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-28
  • 1970-01-01
  • 2016-09-22
  • 2016-10-08
  • 1970-01-01
  • 2014-12-06
相关资源
最近更新 更多