我在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
谢谢你的帮助! 迈克尔
答案 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)
现在一切都是正确的。 感谢大家。 迈克尔