Цикл по строкам и взрывному диапазону дат - PullRequest
0 голосов
/ 17 сентября 2018

У меня есть таблица задач, где я хочу создать представление, которое может выполнять следующие действия: 1. Итерация по каждой строке 2. Разнесите каждый день между начальной и конечной датой в новую строку 3. Вставьте среднее задание работы в новый столбец

Это таблица:

CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
)

INSERT INTO #InputTABLE VALUES('2298aas','2018-06-06','2018-06-12',200);

Я дошел до того, чтобы решить 2 и 3, но я действительно борюсь с частью итерации. Код ниже потерпит неудачу, если в InputTABLE присутствует более одной строки:

CREATE TABLE #OutputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
)

DECLARE @taskid varchar (200)
DECLARE @cnt int
DECLARE @startDate datetime
DECLARE @endDate datetime
DECLARE @incr int
DECLARE @tempDate datetime 
DECLARE @work int
DECLARE @averagework int

SET @taskid=(Select TaskID from #InputTABLE)
SET @startDate=(Select startdate from #InputTABLE)
SET @endDate=(Select enddate from #InputTABLE)
SET @cnt=DATEDIFF(dy,@startDate,@endDate)
SET @incr=0

SET @tempDate=DATEADD(dy,@incr,Cast(@startDate As datetime))
SET @work=(Select TaskWork from #InputTABLE)
SET @averagework= @work/@cnt

WHILE @cnt>=0
BEGIN

   IF @cnt = 0 
      BEGIN
         INSERT INTO #OutputTABLE VALUES(@taskid,@tempDate,@endDate,@averagework);
      END
   ELSE
      BEGIN
         insert into #OutputTABLE values(@taskid,@tempDate,DATEADD(dy, DATEDIFF(dy,0,@tempDate)+1, -1),@averagework);
      END
   SET @tempDate=DATEADD(dy,@incr+1,DATEADD(dy,DATEDIFF(dy,0,@startDate),0))

   SET @cnt=@cnt-1
   SET @incr=@incr+1

   END

Я думал о реализации решения с использованием курсора из this , но я не уверен, как это сделать? Я также обеспокоен комментариями о том, что циклический переход по строкам плохо влияет на производительность, поэтому любые советы по этому поводу очень ценятся!

Таблица содержит около 15 000 строк, а средний диапазон дат - около 31 дня, поэтому представление будет примерно 465 000 строк. Не большое число, но таблица постоянно растет, поэтому мне также может потребоваться ограничить просмотр только последними двумя годами.

1 Ответ

0 голосов
/ 17 сентября 2018

Ваш вопрос не очень ясен, но мой магический шарик-кристалл говорит мне, что вы, возможно, ищете это:

SET DATEFORMAT ymd;
CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
);

INSERT INTO #InputTABLE VALUES('six days','2018-06-06','2018-06-12',200)
                             ,('one day','2018-06-06','2018-06-07',200);

SELECT TaskID
      ,DATEADD(DAY,B.Numbr,startdate) AS ExplodingDate
      ,CAST(TaskWork AS DECIMAL(10,4))/A.DayDiff
FROM #InputTABLE
CROSS APPLY(SELECT DATEDIFF(DAY,startdate,enddate) AS DayDiff) A
CROSS APPLY(SELECT TOP (A.DayDiff) ROW_NUMBER() OVER(ORDER BY (SELECT NULL))-1 AS Numbr FROM master..spt_values) B

DROP TABLE #InputTABLE;

результат

TaskID      ExplodingDate           (Kein Spaltenname)
six days    2018-06-06 00:00:00.000 33.333333333333333
six days    2018-06-07 00:00:00.000 33.333333333333333
six days    2018-06-08 00:00:00.000 33.333333333333333
six days    2018-06-09 00:00:00.000 33.333333333333333
six days    2018-06-10 00:00:00.000 33.333333333333333
six days    2018-06-11 00:00:00.000 33.333333333333333
one day     2018-06-06 00:00:00.000 200.000000000000000

Краткое объяснение
Первая APPLY вычисляет разницу в днях между двумя датами.
Вторая APPLY использует трюк с TOP и ROW_NUMBER для создания таблицы подсчета на лету.
Это создаст столько строк на входную строку, сколько будет дней между начальной и конечной датой.
Остальное - простое вычисление ...

ОБНОВЛЕНИЕ Полный пример с постоянной таблицей

CREATE DATABASE TestDB;
GO
USE TestDB;
GO
CREATE TABLE dbo.RunningNumbers(Number INT NOT NULL
                               ,CalendarDate DATE NOT NULL
                               ,CalendarYear INT NOT NULL
                               ,CalendarMonth INT NOT NULL
                               ,CalendarDay INT NOT NULL
                               ,CalendarWeek INT NOT NULL
                               ,CalendarYearDay INT NOT NULL
                               ,CalendarWeekDay INT NOT NULL);

DECLARE @CountEntries INT = 100000;
DECLARE @StartNumber INT = 0;


WITH E1(N) AS(SELECT 1 FROM(VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1))t(N)), --10 ^ 1
    E2(N) AS(SELECT 1 FROM E1 a CROSS JOIN E1 b), -- 10 ^ 2 = 100 rows
    E4(N) AS(SELECT 1 FROM E2 a CROSS JOIN E2 b), -- 10 ^ 4 = 10,000 rows
    E8(N) AS(SELECT 1 FROM E4 a CROSS JOIN E4 b), -- 10 ^ 8 = 10,000,000 rows
    CteTally AS
    (
        SELECT TOP(ISNULL(@CountEntries,1000000)) ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1 + ISNULL(@StartNumber,0) As Nmbr
        FROM E8
    )
INSERT INTO dbo.RunningNumbers
SELECT CteTally.Nmbr,CalendarDate.d,CalendarExt.*
FROM CteTally
CROSS APPLY
(
    SELECT DATEADD(DAY,CteTally.Nmbr,{ts'1900-01-01 00:00:00'})
) AS CalendarDate(d)
CROSS APPLY
(
    SELECT YEAR(CalendarDate.d) AS CalendarYear
          ,MONTH(CalendarDate.d) AS CalendarMonth
          ,DAY(CalendarDate.d) AS CalendarDay
          ,DATEPART(WEEK,CalendarDate.d) AS CalendarWeek
          ,DATEPART(DAYOFYEAR,CalendarDate.d) AS CalendarYearDay
          ,DATEPART(WEEKDAY,CalendarDate.d) AS CalendarWeekDay
) AS CalendarExt;
GO

SET DATEFORMAT ymd;
CREATE TABLE #InputTABLE
(
TaskID varchar (200),
startdate DATETIME,
enddate DATETIME,
TaskWork int 
);

INSERT INTO #InputTABLE VALUES('six days','2018-06-06','2018-06-12',200)
                             ,('one day','2018-06-06','2018-06-07',200);

SELECT TaskID
      ,B.CalendarDate
      ,CAST(TaskWork AS DECIMAL(10,4))/(A.DayDiff+1)
FROM #InputTABLE
CROSS APPLY(SELECT DATEDIFF(DAY,startdate,enddate) AS DayDiff) A
CROSS APPLY(SELECT * FROM dbo.RunningNumbers WHERE CalendarDate BETWEEN startdate AND enddate) B

DROP TABLE #InputTABLE;
GO

USE master;
GO

DROP DATABASE TestDB;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...