2016-11-10 1 views
0

Ich habe eine Preisliste Tabelle in SQL Server 2008R2 und möchte den Preis für einen Service nach der Zeit berechnen, wo der Service gemacht wurde.Berechnung der Zeit in einer Preisliste

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 

Diese Preisliste Zeigt verschiedene Preise während der Nachtzeit um 19:00 Uhr beginnt und dauert bis 07.00 Uhr und tagsüber von 7.00 bis 19.00 Uhr.

Minuten müssen auf Viertel Stunden aufgerundet werden. Es gibt noch einige Dinge zu beachten, wie zB Mindestvorlaufzeit (@Vorlaufzeit) und ob Wochentag am Wochenende. Beide Bedingungen sind erfüllt und nicht das Problem. Mein Problem ist der erste und der letzte Datensatz, bei dem ich aufrunden und/oder abrunden muss, was inkorrekt ist. Beide Zeilen haben 1 in der Schleife und sollten in den beiden Updates korrekt korrigiert werden, tun dies aber nicht.

Also zum Beispiel ein Service vom 2016-11-04 10:50 (Abrunden auf 10:45, was 0,25 Stunden ist) bis 2016-11-04 19:25 (aufgerundet auf 19:30, was 0,5 ist Stunden) beträgt 0,25 + 8 + 0,5 = 8,75 Stunden und kostet 8,25 * 15 + 0,5 * 20 = 133,75.

Ich versuchte mit diesem Code, aber es bringt mir nicht das richtige Ergebnis. Es ist nur die erste und die letzte Aufzeichnung, in der ich auf- oder abrunden muss. Es ist nur richtig, wenn es volle Stunden gibt.

 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 

Danke Ihre Hilfe! Michael

+0

Warum verwenden Sie 'datetime' anstelle von' time'? –

+0

@PanagiotisKanavos Nachdem man ähnliche Dinge getan hat, macht die Unfähigkeit der "Zeit", 24 Stunden (oder mehr) auszudrücken, Bereiche, die Mitternacht zu einem Alptraum werden lassen. Die Suche nach 'RangeStart <= Target <= RangeEnd' wird zu etwas wie' (RangeStart <= Target und Target <= RangeEnd) oder (RangeStart> RangeEnd und (RangeStart <= Target oder Target <= RangeEnd)) '. Nicht so schlecht, wenn es in einer UDF getestet und versteckt wird, aber ungeschickt und fehleranfällig, wenn es verstreut wird. – HABO

+0

@HABO nicht in diesem Fall. Das sind nur Stunden an einem Tag. Es gibt jedoch weit schlimmere Probleme, einschließlich des Versuches, eine richtige Abfrage zu schreiben, anstatt eine richtige Abfrage zu schreiben –

Antwort

0

Dieser Code ein bisschen ausführlicher, aber wollte es mit Ihnen teilen, wie es das gewünschte Ergebnis der 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 
; 

produziert Dies könnte viel zur Reinigung verwenden und optimieren. Es funktioniert auch nur, wenn die Start- und Endzeiten am selben Tag sind, unter anderem Bugs und Spaß Sachen.

+0

thx yr Bemühungen! Sehen Sie meine Antwort, die mir die Möglichkeit gibt, über einen Tag zu berechnen. – mak

0

Danke an everyones Beitrag Ich konnte meinen Weg finden und korrigierte meine Dauer und die beiden Updates.

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) 

jetzt ist alles korrekt. Danke an alle. Michael

Verwandte Themen