每隔一个小时复制一次

时间:2013-01-30 13:21:25

标签: sql sql-server tsql sql-server-2008-r2

我需要将每行复制的次数与StartTimeEndTime之间的小时数相同。

示例数据:

SQLFIDDLEExample

TimeKey HourKey SensorKey   IdleTimeMinute  StartTime   EndTime
20121017    8   45  110 2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  25  2012.10.17 10:26    2012.10.17 10:51
20121017    12  45  5   2012.10.17 12:21    2012.10.17 12:26
20121017    12  45  60  2012.10.17 12:41    2012.10.17 13:41
20121017    13  45  55  2012.10.17 13:51    2012.10.17 14:46
20121017    15  45  5   2012.10.17 15:11    2012.10.17 15:16
20121017    15  45  35  2012.10.17 15:46    2012.10.17 16:21
20121017    18  45  5   2012.10.17 18:51    2012.10.17 18:56

解释

例如,第一行跨越小时8,9和10.在三个输出行的每一行中,IdleTimeMinute必须是该小时内的分钟持续时间。

预期结果:

TimeKey HourKey SensorKey   IdleTimeMinute  StartTime   EndTime
20121017    8   45  29  2012.10.17 08:31    2012.10.17 10:21
20121017    9   45  60  2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  21  2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  25  2012.10.17 10:26    2012.10.17 10:51
20121017    12  45  5   2012.10.17 12:21    2012.10.17 12:26
20121017    12  45  19  2012.10.17 12:41    2012.10.17 13:41
20121017    13  45  41  2012.10.17 12:41    2012.10.17 13:41
20121017    13  45  9   2012.10.17 13:51    2012.10.17 14:46
20121017    14  45  46  2012.10.17 13:51    2012.10.17 14:46
20121017    15  45  5   2012.10.17 15:11    2012.10.17 15:16
20121017    15  45  14  2012.10.17 15:46    2012.10.17 16:21
20121017    16  45  21  2012.10.17 15:46    2012.10.17 16:21
20121017    18  45  5   2012.10.17 18:51    2012.10.17 18:56

较小的示例

前两行中较小的例子:

TimeKey HourKey SensorKey   IdleTimeMinute  StartTime   EndTime
20121017    8   45  110 2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  25  2012.10.17 10:26    2012.10.17 10:51

对于第一行,我们有时间8:31 - 10:21所以8小时29分钟,9小时60分钟,10小时21分钟。

预期的小结果:

TimeKey HourKey SensorKey   IdleTimeMinute  StartTime   EndTime
20121017    8   45  29  2012.10.17 08:31    2012.10.17 10:21
20121017    9   45  60  2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  21  2012.10.17 08:31    2012.10.17 10:21
20121017    10  45  25  2012.10.17 10:26    2012.10.17 10:51

4 个答案:

答案 0 :(得分:0)

这有点乱,但基于你可以做的SQL小提琴

CREATE TABLE #Temp (RowNumber INT Identity(1,1),[TimeKey] int, [HourKey] int, 

[SensorKey] int, [IdleTimeMinute] int, [StartTime] datetime, [EndTime] datetime)

INSERT INTO #Temp
SELECT *
FROM Table1

DECLARE @i INT = 1
DECLARE @StartTime DATETIME, @EndTime DATETIME
DECLARE @Total INT = (SELECT COUNT(*) FROM #Temp)
DECLARE @x INT = 1
DECLARE @TotalHours INT = 0
WHILE @i <= @Total + 1
BEGIN

SET @StartTime = (SELECT StartTime FROM #temp WHERE RowNumber = @i)
SET @EndTime = (SELECT EndTime FROM #temp WHERE RowNumber = @i)               
SET @x = 1
SET @TotalHours = (SELECT DATEDIFF(Hour,@StartTime,@EndTime))

WHILE @x <= @TotalHours
BEGIN
INSERT INTO Table2 SELECT * FROM #Temp WHERE RowNumber = @i
SET @x = @x + 1
END
SET @i = @i + 1
END

SELECT * FROM Table2

假设您有第二个具有此布局的表

CREATE TABLE Table2
    (RowNumber INT, [TimeKey] int, [HourKey] int, [SensorKey] int, [IdleTimeMinute] int, [StartTime] datetime, [EndTime] datetime)
;

答案 1 :(得分:0)

你可以试试这个。生成序列的限制。如果您的IdleTimeMinute出乎意料,那么您必须使用CTE中的OPTION (MAXRECURSION n)。查询可以从SQL Server 2005+运行。对于旧版本,您必须使用自己的序列生成机制。

;WITH Times
AS
(
  SELECT 1 Id
  UNION ALL
  SELECT Id + 1 
  FROM Times
  WHERE Id < 100
)
SELECT
  R.* ,
  CASE WHEN (IDLETIMEMINUTE - 60 * (T.Id - 1)) / 60 > 0 
      THEN 60
      ELSE (IDLETIMEMINUTE % 60)
   END IDLETIMEMINUTE_New
FROM Table1 R
  JOIN Times T
     ON T.Id <= CEILING(R.[IdleTimeMinute]/60.0)

<强> SQL FIDDLE DEMO

答案 2 :(得分:0)

SQLFiddle

WITH    q(n) AS
        (
        SELECT  0
        UNION ALL
        SELECT  n + 1
        FROM    q
        WHERE   n <23
        )
select TimeKey,
       q.n as HourKey,
       SensorKey,
       (select min(V) from 
         (select ETime-q.n*60 as V
          union all
          select ((q.n+1)*60)-sTime as V
          union all 
          select 60 as V          
         )  x
       ) as IdleTimeMinute,
       StartTime,
       EndTime
from q 
join 
(
select *,
DATEPART(HOUR, starttime)*60
   +DATEPART(MINUTE, starttime) sTime,
DATEPART(HOUR, Endtime)*60
   +DATEPART(MINUTE, Endtime) eTime
from 
table1 
) t1
on q.n*60 between (t1.sTime/60)*60 and t1.eTime
order by TimeKEy,SensorKey,HourKey,StartTime

答案 3 :(得分:0)

你走了。这将处理时间跨度超过午夜或持续数天的情况。它应该表现得很好。

请参阅the SqlFiddle for it

DECLARE @MaxHour int = IsNull((SELECT Max(DateDiff(hour, StartTime, EndTime)) + 1 FROM dbo.Table1), 0);

WITH L0 AS (SELECT 1 N UNION ALL SELECT 1),
L1 AS (SELECT 1 N FROM L0, L0 B),
L2 AS (SELECT 1 N FROM L1, L1 B),
L3 AS (SELECT 1 N FROM L2, L2 B),
L4 AS (SELECT 1 N FROM L3, L3 B),
L5 AS (SELECT 1 N FROM L4, L4 B),
Nums AS (SELECT Num = Row_Number() OVER (ORDER BY (SELECT 1)) FROM L5)
SELECT
   TimeKey = Convert(int, Convert(char(8), S.HourStartTime, 112)),
   HourKey = DatePart(hour, S.HourStartTime),
   T.SensorKey,
   IdleTimeMinute = DateDiff(minute, S.HourStartTime, E.HourEndTime),
   T.StartTime,
   T.EndTime,
   S.HourStartTime,
   E.HourEndTime
FROM
   dbo.Table1 T
   CROSS APPLY (
      SELECT AnchorHour = DateAdd(hour, DateDiff(hour, 0, T.StartTime) + N.Num - 1, 0)
      FROM Nums N
      WHERE
         DateDiff(hour, T.StartTime, T.EndTime) + 1 >= N.Num
         AND N.Num <= @MaxHour
   ) D
   CROSS APPLY (
      SELECT HourStartTime = Max(StartTime)
      FROM (VALUES (D.AnchorHour), (T.StartTime)) S (StartTime)
   ) S
   CROSS APPLY (
      SELECT HourEndTime = Min(EndTime)
      FROM (VALUES (DateAdd(hour, 1, D.AnchorHour)), (T.EndTime)) E (EndTime)
   ) E
ORDER BY
   TimeKey,
   StartTime,
   HourKey;

我包含所选小时的新时间跨度。如果您希望该时间跨度在下一个小时的:59而不是:00结束,则在第三个CROSS APPLY更改DateAdd(hour, 1, D.AnchorHour)DateAdd(minute, 59, D.AnchorHour)并添加{{ 1}}到+ 1子句中DateDiff(minute, S.HourStartTime, E.HourEndTime)的末尾。

在我看来:

  • 使用CTE逐步增加最大差异并不是最佳选择。一旦达到最大数量,我使用的CTE就会接近,最高可达4294967296。
  • 不计算新TimeKey的解决方案是脆弱的,因为取决于未说明午夜无法越过的未声明的假设。
  • 依赖于您更改表格以获得单调增加ID而无间隙的解决方案不太可能适合您的实际数据。

注意:我声明@MaxHour变量的原因是我正在使用的动态数字表CTE想要一个常量。嗯,这不完全准确,但它的长短是没有常量它可以选择一个执行计划,不会限制快速查询所需的行中的行。我选择了SELECT来计算它,但实际上我现在看到这可能会更快,如果你有一个IdleTimeMinute索引肯定会更快:

DateDiff

由于可以跨越两个不同小时的最小时间范围是2分钟(比如说11:59到12:00),所以我们必须增加118分钟,以确保至少有2个小时。