2016-11-10 85 views
0

我在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時和白天從07:00至19:00。

分鐘必須四捨五入到四分之一小時。 還有一些需要照顧的地方,例如最低限度通知Periode(@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 = @[email protected] 
    set @EndDatumZeit = @[email protected] 
    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 <[email protected]) --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 [email protected] --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 

感謝您的幫助! 邁克爾

+0

你爲什麼用'datetime'而不是'time'? –

+0

@PanagiotisKanavos在做了類似的事情之後,'time'無法表達24小時(或更多),使得午夜時間跨越噩夢處理的範圍。檢查RangeStart <= Target <= RangeEnd'變成類似'(RangeStart <= Target和Target <= RangeEnd)或(RangeStart> RangeEnd和(RangeStart <= Target或Target <= RangeEnd))'。如果測試並隱藏在UDF中,則不會太糟糕,但散佈時會出現笨拙和容易出錯的情況。 – HABO

+0

@HABO不在這種情況下。這只是一天的幾個小時。儘管如此,這裏的問題還是比較嚴重,包括試圖循環而不是寫一個適當的查詢 –

回答

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 
; 

所期望的結果。這可以用一個很多清理和優化。 它也只適用於開始和結束時間在同一天內,以及其他錯誤和有趣的東西。

+0

thx年努力!看到我的答案,這讓我有能力計算超過一天。 – mak

0

感謝每個人的貢獻,我可以找到我的方式,糾正我的持續時間和兩個更新。

Duration: 
set @Dauer=datediff(hh, DateAdd(hh, DateDiff(hh, 0, @[email protected]), 0),DateAdd(hh, DateDiff(hh, 0, @[email protected]), 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) 

現在一切正常。 Thx給大家。 Michael