计算价目表中的时间

时间:2016-11-10 15:46:51

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

我在SQL Server 2008R2中有一个价目表,并希望根据服务的制作时间来计算服务的价格。

timefrom           |timeto             |Price
--------           |------             |-----
1900-01-01 00:00:00|1900-01-01 07:00:00|20.00
1900-01-01 07:00:00|1900-01-01 19:00:00|15.00
1900-01-01 19:00:00|1900-01-02 00:00:00|20.00

此价目表显示夜间开始于19:00和持续至07:00以及白天07:00至19:00的不同价格。

分钟必须四舍五入到四分之一小时。 还有一些需要注意的事项,例如最小通知周期(@Vorlaufzeit)以及周末的工作日。满足这两个条件而不是问题。我的问题是第一个也是最后一个记录,我必须向上舍入和/或向下舍入,这是不正确的。这两行在循环中都有1,并且应该在两次更新中都正确纠正,但不是。

所以,例如2016-11-04 10:50的服务(向下到10:45,这是0.25小时)到2016-11-04 19:25(向上舍入到19:30,这是0.5小时)是0.25 + 8 + 0.5 = 8.75小时,费用8.25 * 15 + 0.5 * 20 = 133.75。

我尝试使用此代码,但它没有给我带来正确的结果。这只是我必须向上或向下舍入的第一个也是最后一个记录。这是正确的,当有整整几个小时。

        DECLARE @Dauer int
    DECLARE @X int  --Loopcounter für Stunden
    declare @Y int  --Loopcounter für Tageszahler
    declare @Anfangszeit datetime
    declare @Anfangsstunde datetime
    declare @Endzeit datetime
    declare @Vorlaufzeit int  --in Minuten
    declare @ErsteZeitvon datetime
    declare @SummeAnzStunden decimal(8,2)
    declare @MinimumZeit int
    declare @ZeitvonVolleStunde int  -- aus 07:25 mach 7 Uhr
    declare @ZeitbisVolleStunde int 

    declare @AnfangsDatumZeit as datetime
    declare @EndDatumZeit as datetime 
    declare @AnfangsDatumZeitLoop as datetime
    declare @AnfangsZeitLoop as datetime
    declare @TagesZaehler int

    set @AnfangsDatumZeit = @Datumvon+@Zeitvon
    set @EndDatumZeit = @Datumbis+@Zeitbis
    set @Tageszaehler=datediff(day,@AnfangsDatumZeit, @EndDatumZeit)

    declare @t1 table ( PreisID int, AnzStunden decimal(5,2), Preis decimal(8,2), Anfangszeit datetime, Prüfzeit datetime, startzeit datetime, endezeit datetime, Vorlaufzeit int, Dauer int, PreisFT decimal(8,2), DatZeitvon datetime, DatZeitbis datetime, Tageszaehler int )

    -- Insert statements for procedure here


    set @ZeitvonVolleStunde=Datediff(hour, '00:00:00', @Zeitvon)

    set @ZeitbisVolleStunde=Datediff(minute, '00:00:00', @Zeitbis)


    set @Dauer=ceiling(Datediff(minute, @AnfangsDatumZeit, @EndDatumZeit)/60.00)  

set @Vorlaufzeit=datediff(minute,@Bestelldatum, @AnfangsDatumZeit)     


    SET @X = 0

if Datediff(minute, @AnfangsDatumZeit, @EndDatumZeit) > 360
    begin
            WHILE (@X <=@Dauer)  --z.b. 13

            begin   


                    --set @Y = datediff(day,@AnfangsDatumZeit,@AnfangsDatumZeitLoop)
                    set @Y = datediff(day,@AnfangsDatumZeit,dateadd(hour,@X, @AnfangsDatumZeit))

                    set @AnfangsDatumZeitLoop=dateadd(hour,@X, @AnfangsDatumZeit)

                    set @AnfangsZeitLoop=dateadd(hour,@X, @Zeitvon)



                            insert into @t1 ( PreisID, AnzStunden, Preis , Anfangszeit, Prüfzeit, DatZeitvon , DatZeitbis  )



                            SELECT top 1 preisID, 1, Preis, @AnfangsZeitLoop, @AnfangsDatumZeitLoop, Zeitvon,  Zeitbis

                                 FROM dbo.Mypricetable 
                                 where  SdlID=@Leistungsart   --SdlID
                                        and Wochentag=case when DATEPART(dw,@AnfangsDatumZeitLoop) < 6 then 'W' else 'S' end  --Wochentag 
                                        and @Vorlaufzeit BETWEEN Vorlaufzeitvon and Vorlaufzeitbis  --Vorlaufzeit in Minuten
                                        AND @Dauer*60 BETWEEN Dauervon AND Dauerbis   --DauerInMinuten 
                                        and @AnfangsZeitLoop between Zeitvon and  Zeitbis --sucht die von/bis Zeitgruppe
                                 order by zeitvon

            SET @X = @X + 1
            end

    --check and udate of the first record in @t1 rounding down to 15 minutes
    update @t1 set Anzstunden= Anzstunden + CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](@AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](@AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25)
    from @t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from @t1 order by Prüfzeit) 

    --check and udate of the last record in @t1 rounding up to 15 minutes
    update @t1 set Anzstunden= round(convert(decimal(5,2),datepart(minute,@EndDatumZeit)+7)/60/25,2)*25
    from @t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from @t1 order by Prüfzeit DESC) 


    end

    select * from @t1 order by Prüfzeit

谢谢你的帮助! 迈克尔

2 个答案:

答案 0 :(得分:0)

此代码有点冗长,但希望与您分享,因为它会产生 133.75

的预期结果
DECLARE @x table (
   timefrom datetime
 , timeto   datetime
 , price    decimal(14,4)
);

INSERT INTO @x (timefrom, timeto, price)
  VALUES ('1900-01-01T00:00:00', '1900-01-01T07:00:00', 20.00)
       , ('1900-01-01T07:00:00', '1900-01-01T19:00:00', 15.00)
       , ('1900-01-01T19:00:00', '1900-01-02T00:00:00', 20.00)
;

-- You should have your own, physical tally table!
DECLARE @numbers table (
   number tinyint
);
INSERT INTO @numbers (number)
SELECT DISTINCT
       number
FROM   master.dbo.spt_values
WHERE  number BETWEEN 0 AND 255
;

DECLARE @start datetime = '2016-11-04T10:50:00'
      , @end   datetime = '2016-11-04T19:25:00'
;

-- first, let's do some rounding of our inputs
DECLARE @rounded_start_time time
      , @rounded_end_time   time
;


-- Illustrate the steps to round the time to quarters... this might not be the simplest method; but it works!
/*
SELECT @start AS start
     , DateAdd(hh, DateDiff(hh, 0, @start), 0) AS truncate_hour
     , Round(DatePart(mi, @start) / 15.0, 0) * 15 AS rounded_mins
     , DateAdd(mi, Round(DatePart(mi, @start) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, @start), 0)) AS truncate_hour_then_add_mins
;
*/

SET @rounded_start_time = DateAdd(mi, Round(DatePart(mi, @start) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, @start), 0));
SET @rounded_end_time   = DateAdd(mi, Round(DatePart(mi, @end  ) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, @end  ), 0));

PRINT 'Start: ' + Format(@rounded_start_time, 'HH:mm');
PRINT 'End:   ' + Format(@rounded_end_time  , 'HH:mm');

--SELECT *
--FROM   @x
--;

; WITH intervals AS (
  SELECT number * 15 AS minute_increments
       , DateAdd(mi,  number *      15, 0) AS interval_start
       , DateAdd(mi, (number + 1) * 15, 0) AS interval_end
  FROM   @numbers
  WHERE  number >= 0
  AND    number <  24 * 4 --number of 15 minute increments in a day
)
, costed_intervals AS (
  SELECT intervals.interval_start
       , intervals.interval_end
       , Cast(intervals.interval_start AS time) As interval_start_time
       , Cast(intervals.interval_end   AS time) As interval_end_time
       , x.price / 4.0 AS interval_price
  FROM   @x AS x
   INNER
    JOIN intervals
      ON intervals.interval_end   <= x.timeto
     AND intervals.interval_start >=  x.timefrom
)
, applicable_intervals AS (
  SELECT interval_start
       , interval_end
       , interval_start_time
       , interval_end_time
       , interval_price
  FROM   costed_intervals
  WHERE  interval_start_time < @rounded_end_time
  AND    interval_end_time   > @rounded_start_time
)
SELECT Sum(interval_price) AS total_price
FROM   applicable_intervals
;

这可以使用清理和优化的 批次 。 它只适用于开始和结束时间在同一天内,以及其他错误和有趣的东西。

答案 1 :(得分:0)

感谢每个人的贡献,我可以找到自己的方式并纠正我的持续时间和两次更新。

Duration:
set @Dauer=datediff(hh, DateAdd(hh, DateDiff(hh, 0, @Datumvon+@Zeitvon), 0),DateAdd(hh, DateDiff(hh, 0, @datumbis+@Zeitbis), 0)) 

Update the first record
update @t1 set Anzstunden =Anzstunden + (datediff(mi,DateAdd(mi, Round((DatePart(mi, @Zeitvon)-7) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, @Zeitvon), 0)),DateAdd(hh, DateDiff(hh, 0, @Zeitvon), 0))/60.00) 
    -- case when CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](@AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](@AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25) *-1 > 0 then 
    -- CONVERT(DECIMAL(6, 2), ROUND(((datediff(minute,[dbo].[sfRoundToHourParts](@AnfangsDatumZeit,1), [dbo].[sfRoundToHourParts](@AnfangsDatumZeit,4)) + 7) / 60.00) / 25, 2) * 25) *-1 else Anzstunden end
from @t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from @t1 order by Prüfzeit) 

Update the last record

--Prüft und korrigiert den LETZTEN Datensatz der @t1 auf 15 Minuten-Takt
update @t1 set Anzstunden= Anzstunden + ((datediff(mi,DateAdd(mi, Round((DatePart(mi, @Zeitbis)+7) / 15.0, 0) * 15, DateAdd(hh, DateDiff(hh, 0, @Zeitbis), 0)),DateAdd(hh, DateDiff(hh, 0, @Zeitbis), 0))/60.00)*-1)
from @t1 c where c.Prüfzeit=(SELECT Top 1 Prüfzeit from @t1 order by Prüfzeit DESC) 

现在一切都是正确的。 感谢大家。 迈克尔