2016-04-26 2 views
1

Мне было предложено создать хранимую процедуру, которая отображает количество единиц аренды в диапазоне переменных дат. У меня есть таблица со следующей схемой:Как я могу подсчитать, что общее количество факторов и даты и времени?

  --Note that this is condensed, and in reality has proper constraints 
     --and more columns. Many dates from this table are tied to a single 
     --ContractDetail (separate table) by ContractDetailId. 
     CREATE TABLE RentalContractDates 
     (
      RentalDateId    INT IDENTITY(1,1) NOT NULL, --PK 
      ContractDetailId   INT     NOT NULL, --FK 
      RentalDate     DATETIME    NOT NULL, 
      Quantity     DECIMAL(20,8)  NULL 
     ); 

     INSERT INTO RentalContractDates (ContractDetailId, RentalDate, Quantity) 
     VALUES (1, '04/01/2016 3:00 PM', 10), 
       (1, '04/10/2016 1:00 PM', 2), 
       (1, '04/15/2016 11:00 AM', -5), 
       (1, '04/15/2016 11:30 AM', -2), 
       (1, '04/27/2016 2:00 PM', -5);  

пользователей будут вводить диапазон дат для поиска, и эта процедура должна найти все даты, которые находятся в пределах этого диапазона, а затем также фактор в то время отсечки, где клиент будет взиматься еще один день с их аренды.

Пример Сценарий: Общее время среза устанавливается до 12:00 PM. У меня есть 10 виджетах, снятых в 4/01/2016 в 15:00. Это в основном я подразумеваю, что я действительно арендовал их 4/02/2016, так как он прошел Cutoff Time 04/01. Я сдаю в аренду еще 2 на 4/10/2016 в 1:00 вечера, так что существенно 4/11/2016. Я возвращаю 5 виджетов 15.04.2016 в 11:00 и еще 2 в 11:30. Я хочу вернуть все виджеты на 4/27/2016, но я прохожу мимо Cutoff Time до 12:00 PM, поэтому вместо того, чтобы взимать плату за 4/02-4/27, на самом деле я буду взимать плату за 4/02 -4/28.

ВАЖНОЕ ЗАМЕЧАНИЕ: Если у меня были ранее арендованные количества до 04/01, что является началом диапазона отчетов, мне нужно будет включить их в отчет. Например, если бы у меня было 12 арендных ставок на 3/31, 4/1 и на добавление 12 к их итогам. Другими словами, любые предыдущие величины необходимо было вычислить в сумму, которая втягивается с введенными параметрами отчета @BeginDate и @EndDate. Итак, 04/01 будет читать 12, 04/02 будет читать 22 и т. Д.

Как вы можете видеть, мне не нужно, чтобы пользователи каждый день вводили свою аренду, я просто установил дату и время начала их аренду с количеством, и в следующий раз, когда они вступят в комбинацию даты/времени, он будет повторно суммирован.

Текущий код: Я хочу присоединиться к этому запросу со списком дат календаря на весь месяц и соответственно установить их количественные показатели.

 DECLARE @BeginDate DATETIME = '04/01/2016', 
       @EndDate DATETIME = '04/28/2016'; 

     DECLARE 
       @CutoffTime TIME = '12:00 PM'; 

     SET @BeginDate = @BeginDate + @CutoffTime; 
     SET @EndDate = @EndDate + @CutoffTime; 

     SELECT gbd.ContractDetailId, 
       gbd.RentalDate, 
       gbd.Cutoff, 
       gbd.Quantity, 
       'Running Total' = SUM(Quantity) OVER (PARTITION BY ContractDetailId, RentalDate, Cutoff ORDER BY RentalDate) 
     FROM (

    SELECT 
      r.ContractDetailId, 
      'RentalDate' = CONVERT(Date, RentalDate), 
      r2.Cutoff, 
      r.Quantity 
    FROM RentalContractDates r 
    INNER JOIN 
     (
      SELECT 
       rcd.ContractDetailId, 
       'Cutoff' = CASE WHEN CONVERT(TIME, RentalDate) >= @CutoffTime THEN 'AFTER CUTOFF' ELSE 'BEFORE CUTOFF' END 
      FROM 
       RentalContractDates rcd 
     ) r2 
     ON r2.ContractDetailId = r.ContractDetailId 
    WHERE 
      r.RentalDate Between @BeginDate and @EndDate 
    GROUP BY r.ContractDetailId, CONVERT(DATE, RentalDate), r2.Cutoff, Quantity 
     ) gbd 
     ORDER BY RentalDate, Cutoff DESC 

Я хочу присоединиться к данным с этим КТРОМ и установить количество для каждой даты:

 ;WITH T([Date]) AS 
    ( 
     SELECT @StartDate 
     UNION ALL 
     SELECT DATEADD(DAY,1,T.[Date]) FROM T WHERE T.[Date] < @EndDate 
    ) 
    SELECT * FROM T 

Ожидаемых конечный вывод: После завершения отчета будет в конечном итоге выглядеть примерно так , хотя она будет повернута и включает в себя день недели в названии:

ContractDetailId RentalDate  Quantity 
     ---------------------------------------------------------------- 
     1    04/01/2016  0 -- 0, because rentals were input after cutoff. 
     1    04/02/2016  10 
     1    04/03/2016  10 -- Continues until 4/10 
     1    04/10/2016  10 
     1    04/11/2016  12 -- Continues until 4/15 
     1    04/15/2016  5 -- I returned 5 and then 2, so this should sum since both were before the cutoff time. 
              -- Continues until 4/27. 
     1    04/27/2016  5 -- 5, because -5 was entered past cutoff on 4/27. 
     1    04/28/2016  0     

у меня есть код поворота вдоль с динамическим sql, необходимым для окончательного вывода, уже завершенным (я могу опубликовать его, если это запрошено), но я теряюсь на том, как правильно группировать эти данные путем предварительной обрезки/пост-отсечения и соответственно изменить день. Как я должен справиться с этой ситуацией? Спасибо за любой совет/помощь!

Редактировать 1: Исправлены неверные данные образца.

+1

Данные вашего образца показывают, что отображено 12 виджетах и ​​14 возвращено. Хотя это может быть прибыльной бизнес-моделью, кажется, это немного не так. – HABO

+0

Чтобы отрегулировать время отсечки, просто вычтите '12 * 60 = 720' минут от каждого оригинала' datetime' и после этого проигнорируйте временную часть: 'CAST (DATEADD (минута, -720, RentalDate) AS date)'. –

+0

@ ХАБО Вы правы, не знаете, как я это пропустил. 04/15 должно быть 5. Я его отредактировал. –

ответ

0
--Inputs for your function 
    DECLARE @BeginDate DATE = '04/01/2016', 
      @EndDate DATE = '04/28/2016', 
      @ContractDetailID INT = 1; 

    --Defined in the function 
    DECLARE @CutoffTime TIME = '12:00 PM'; 

    DECLARE @PriorSum DECIMAL(20,8) = 0; 

    DECLARE @RowCount INT = DATEDIFF(dd,@BeginDate,@Enddate) +1; 

    --Get Any quantities before Begin Date 
    SELECT @PriorSum=COALESCE(SUM(rcd.Quantity),0) 
    from RentalContractDates rcd 
    WHERE CAST(CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate end as date) < @BeginDate 
     AND @ContractDetailID = rcd.ContractDetailId 

    --Create the Days for the report 
    ;WITH RecursiveRowGenerator (Row#, Iteration) AS (
      SELECT 1, 1 
      UNION ALL 
      SELECT Row# + Iteration, Iteration * 2 
      FROM RecursiveRowGenerator 
      WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1)) 
      UNION ALL 
      SELECT Row# + (Iteration * 2), Iteration * 2 
      FROM RecursiveRowGenerator 
      WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1)) 
     ) 
     , SqrtNRows AS (
      SELECT * 
      FROM RecursiveRowGenerator 
      UNION ALL 
      SELECT 0, 0 
     ) 
     , Rowtbl as ( 
      SELECT top (@RowCount+1) A.Row# * POWER(2,CEILING(LOG(SQRT(@RowCount+1))/LOG(2))) + B.Row# as RowNum 
      FROM SqrtNRows A, SqrtNRows B 
      ORDER BY A.Row#, B.Row# 
     ) 
     , 
    DateTable as (

     select top (@RowCount) DATEADD(dd,RowNum,@BeginDate) AS ReportDate 
     from Rowtbl 
     where RowNum <= @RowCount 

    ) 
    , 
    --Merge the days for the report with the actual rental data 
    GBD AS 
    (SELECT 
       @ContractDetailID as ContractDetailID, 
       DT.ReportDate AS 'RentalDate', 
       CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN 'AFTER CUTOFF' ELSE 'BEFORE CUTOFF' END AS 'Cutoff', 
       COALESCE(rcd.Quantity,0) AS Quantity 
     FROM DateTable DT 
     LEFT JOIN RentalContractDates rcd on 
       DT.ReportDate = CAST(CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate END as DATE) 
       AND @ContractDetailID = rcd.ContractDetailId 
     WHERE DT.ReportDate Between @BeginDate and @EndDate 


    ) 
--Final Select  
SELECT gbd1.ContractDetailId, 
       gbd1.RentalDate, 
       (select SUM(gbd2.Quantity) from GBD GBD2 where GBD1.rentaldate >= GBD2.RentalDate) + @PriorSum AS RunningTotal    
     FROM GBD gbd1  
     GROUP BY gbd1.ContractDetailId,gbd1.RentalDate 
     ORDER BY gbd1.RentalDate asc 

Edit: Генератор рекурсивный строка Аарон Фрил и мое движение к решению для создания строк t-sql select get all Months within a range of years. Он генерирует строки для всех дат, поэтому мы можем оставить их либо количеством, либо 0, если для этой даты нет записи. Сроки разработки с 2000 по 2016 год очень дешевы с использованием этого генератора. Присоединение всех величин и предыдущей суммы к этим датам является довольно дорогостоящим.Агрегация их, поэтому у вас есть общая сумма даже для дат без записи в RentalContractDates, это дорогая часть.

Та часть, которая обрабатывает отрезан вопрос времени:

gbd.RentalDate = CAST(CASE WHEN CAST(rcd.RentalDate AS TIME) > @CutoffTime THEN DATEADD(dd, 1, rcd.RentalDate) 
      ELSE rcd.RentalDate END AS DATE) 

Он преобразует rentaldate только один раз, можно сравнить с cutoffTime, добавляет день, если прошлое и затем преобразует только дату.

+0

Я проверил ваш код, и, когда он работает правильно, он значительно замедляется при расширении диапазона дат, чтобы сказать, год или больше. При старте в 2000 году для начала, для обработки потребовалось более 1 минуты. Проблема в том, что у меня может быть довольно много ContractDetailIds (100+), каждый со многими датами, до одного контракта, на который я буду запускать отчет. Я изменяю его, чтобы увидеть, могу ли я взять выбор объединить GBD и переместить его в временную таблицу, чтобы ускорить процесс. Я считаю, что рекурсия с CTE и этот выбор - это то, что замедляет работу. Когда я закончу, я обновлю свой оригинальный пост. Благодаря! –

+0

Полностью согласен. Я должен был быть более ясным, что это решение не предназначалось для исполнения на более крупных диапазонах дат. Я думаю, что ваш подход хорош. – TheWildMan

Смежные вопросы