I need to sum the mileage ran each month per vehicle - sql-server-2008-r2

I have a series of vehicles that are fueled every day, entering the total fuel and the odometer reading. I need to sum the mileage ran each month per vehicle.
My table is fuel, vehicle is bus, date is service_date, odometer reading is mileage, fuel entered is quantity.
So I would take the mileage from the first of every month and subtract that from the first of the previous month per vehicle, in this case 3237. To make matters worse, if the vehicle was not fueled on the first, I would want the mileage from the last day it was fueled on the previous month.
service_date bus mileage quantity
7/1/2018 202 149654 34
7/3/2018 202 150256 40.5
7/4/2018 202 150562 42
7/6/2018 202 150853 41
7/7/2018 202 151191 37
7/8/2018 202 151323 23.6
7/15/2018 202 151502 39
7/13/2018 202 151806 45
8/1/2018 202 152891 37 3237

Not sure I understand all of your specific requirements, but here is how I would write a query to report on mileage for a fleet of buses.
First I would get the ending mileage for each month using GROUP BY and MAX like this:
SELECT bus_id
, MAX(bus_mileage) AS 'ending_mileage'
, DATEPART(YEAR, service_date) AS 'year'
, DATEPART(MONTH, service_date) AS 'month'
, MAX(service_date) AS 'last_service_date'
FROM #mileage
GROUP BY bus_id
, DATEPART(YEAR, service_date)
, DATEPART(MONTH, service_date)
Then I would use that as a subquery to pull the previous month's ending mileage. This would give you the starting mileage for the month.
SELECT bus_id
, ending_mileage
, (
SELECT MAX(bus_mileage)
FROM #mileage m
WHERE DATEPART(year, m.service_date) = DATEPART(YEAR, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND DATEPART(MONTH, m.service_date) = DATEPART(MONTH, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND m.bus_id = bus_mileage.bus_id
) AS 'starting_mileage'
, mileage_year
, mileage_month
FROM (
SELECT bus_id
, MAX(bus_mileage) AS 'ending_mileage'
, DATEPART(YEAR, service_date) AS 'mileage_year'
, DATEPART(MONTH, service_date) AS 'mileage_month'
, MAX(service_date) AS 'last_service_date'
FROM #mileage
GROUP BY bus_id
, DATEPART(YEAR, service_date)
, DATEPART(MONTH, service_date)
) bus_mileage
Then I would wrap the whole thing in an outer query that subtracts the starting mileage from the ending mileage.
SELECT bus_id
, ending_mileage
, starting_mileage
, ending_mileage - starting_mileage AS 'mileage_for_the_month'
, mileage_year
, mileage_month
FROM (
SELECT bus_id
, ending_mileage
, (
SELECT MAX(bus_mileage)
FROM #mileage m
WHERE DATEPART(year, m.service_date) = DATEPART(YEAR, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND DATEPART(MONTH, m.service_date) = DATEPART(MONTH, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND m.bus_id = bus_mileage.bus_id
) AS 'starting_mileage'
, mileage_year
, mileage_month
FROM (
SELECT bus_id
, MAX(bus_mileage) AS 'ending_mileage'
, DATEPART(YEAR, service_date) AS 'mileage_year'
, DATEPART(MONTH, service_date) AS 'mileage_month'
, MAX(service_date) AS 'last_service_date'
FROM #mileage
GROUP BY bus_id
, DATEPART(YEAR, service_date)
, DATEPART(MONTH, service_date)
) bus_mileage
) monthly_mileage
Here is the whole thing with demo data so you can see how it all works together.
DECLARE #mileage TABLE(
service_date DATE NOT NULL
, bus_id INT NOT NULL
, bus_mileage INT NOT NULL
)
INSERT INTO #mileage (service_date, bus_id, bus_mileage)
VALUES ('7/1/2018', 202, 149654)
, ('7/15/2018', 202, 151502)
, ('8/1/2018', 202, 152891)
, ('8/15/2018', 202, 153502)
, ('9/3/2018', 202, 154891)
, ('10/15/2018', 202, 155502)
, ('11/3/2018', 202, 157891)
, ('7/5/2018', 302, 155502)
, ('8/3/2018', 302, 157691)
SELECT bus_id
, ending_mileage
, starting_mileage
, ending_mileage - starting_mileage AS 'mileage_for_the_month'
, mileage_year
, mileage_month
FROM (
SELECT bus_id
, ending_mileage
, (
SELECT MAX(bus_mileage)
FROM #mileage m
WHERE DATEPART(year, m.service_date) = DATEPART(YEAR, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND DATEPART(MONTH, m.service_date) = DATEPART(MONTH, DATEADD(MONTH, -1, bus_mileage.last_service_date))
AND m.bus_id = bus_mileage.bus_id
) AS 'starting_mileage'
, mileage_year
, mileage_month
FROM (
SELECT bus_id
, MAX(bus_mileage) AS 'ending_mileage'
, DATEPART(YEAR, service_date) AS 'mileage_year'
, DATEPART(MONTH, service_date) AS 'mileage_month'
, MAX(service_date) AS 'last_service_date'
FROM #mileage
GROUP BY bus_id
, DATEPART(YEAR, service_date)
, DATEPART(MONTH, service_date)
) bus_mileage
) monthly_mileage
Not sure if this meets your requirements exactly, but this is how I would report on mileage based on the information you've given me.

Related

Multiple cases that allows different conversion of column that is being checked in WHERE CASE WHEN clause

SELECT Clmn
FROM Tbl T
INNER JOIN SomeotherTbl
WHERE CONVERT(varchar(10), T.[Date], 120) =
CASE #SortOrder
WHEN '1' THEN CONVERT(varchar(10), GETDATE(), 120)
WHEN '2' THEN CONVERT(varchar(7), GETDATE(), 120)
WHEN '3' THEN CONVERT(varchar(4), GETDATE(), 120)
END
So i have a Date column that i converted to varchar , but i would like to convert it to different length according to #SortOrder , what's the best solution ?
Use Like instead of =:
WHERE CONVERT(varchar(10),D.[Date],120) LIKE
CASE #SortOrder
WHEN '1' THEN CONVERT(varchar(10),GETDATE(),120)
WHEN '2' THEN CONVERT(varchar(7),GETDATE(),120) +'%'
WHEN '3' THEN CONVERT(varchar(4),GETDATE(),120) +'%'
END
Another option (without using case) is this:
WHERE (#SortOrder > '3' OR YEAR(D.[Date]) = YEAR(GETDATE()))
AND (#SortOrder > '2' OR MONTH(D.[Date]) = MONTH(GETDATE()))
AND (#SortOrder > '1' OR DAY(D.[Date]) = DAY(GETDATE()))
And yet another option, that will work for versions 2012 or higher, and is a little more complicated, but if you have an index on the [Date] column will allow SQL Server to use it:
DECLARE #Date date = GETDATE()
DECLARE #ThisMonth date = DATEFROMPARTS(YEAR(#Date), MONTH(#Date), 1)
DECLARE #ThisYear date = DATEFROMPARTS(YEAR(#Date), 1, 1)
SELECT ...
FROM ...
WHERE
(#SortOrder = '1' AND D.[Date] = #Date)
OR (#SortOrder = '2' AND D.[Date] >= #ThisMonth AND D.[Date] < DATEADD(MONTH, 1, #ThisMonth))
OR (#SortOrder = '3' AND D.[Date] >= #ThisYear AND D.[Date] < DATEADD(YEAR, 1, #ThisYear))
You can see a live demo on rextester.
If you want to compare year, month, and day there are better ways
select CONVERT(varchar(10), GETDATE(), 120), CONVERT(varchar(7), GETDATE(), 120), CONVERT(varchar(4), GETDATE(), 120)
, DATEPART(year, getdate()), DATEPART(month, getdate()), DATEPART(day, getdate())
2018-02-22 2018-02 2018 2018 2 22

Build the calendar table for current year including weeknum divided to semester

for the below query i need divide the semesters starting from 1 to 26 .. first semester as 1 to 26 weeks and first semester should start from again 1 to 26
Any help is greating appreciated
DECLARE #StartDate DATETIME
DECLARE #CutoffDate DATETIME
SET #StartDate = Dateadd(yy, Datediff(yy, 0, Getdate()), 0)
SET #CutoffDate = Dateadd(yy, Datediff(yy, 0, Getdate()) + 1, -1)
SELECT day,
[week nu],
semester,
[semester week nu]
INTO #currentdates
FROM (SELECT Day = Dateadd(day, rn - 1, #StartDate),
Datepart(week, Dateadd(day, rn - 1, #StartDate)) [Week nu],
CASE
WHEN Month(Dateadd(day, rn - 1, #StartDate)) <= 6 THEN
'First Semester'
ELSE 'Second Semester'
END AS Semester,
Datepart(week, Dateadd(day, rn - 1, #StartDate)) AS
[Semester Week nu]
FROM (SELECT TOP (Datediff(day, #StartDate, #CutoffDate)) rn =
Row_number()
OVER (
ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]) AS x) AS y;
I've got a couple of changes, first, you need to add 1 to your last datediff, your last date was December 30, not 31!
Second of all, I just get the date and week number in the first subselect, and then use these ine case statements for the select
The semester number is if week nu<=26,
The semester week nu is week nu if week nu<=26 else wwek nu -26
DECLARE #StartDate datetime DECLARE #CutoffDate datetime
SET #StartDate = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0)
SET #CutoffDate = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()) + 1, -1)
SELECT DAY,
[Week nu],
case when [Week nu]<=26 then 'First Semester' Else 'Second Semester' end Semester,
case when [Week nu]<=26 then [Week nu] Else [Week nu]-26 end[Semester Week nu]
INTO #currentDates
FROM
(SELECT DAY = DATEADD(DAY, rn - 1, #StartDate),
DATEPART(WEEK,DATEADD(DAY, rn - 1, #StartDate)) [Week nu]
FROM
(SELECT TOP (DATEDIFF(DAY, #StartDate, #CutoffDate)+1) rn = ROW_NUMBER() OVER (ORDER BY s1.[object_id])
FROM sys.all_objects AS s1
CROSS JOIN sys.all_objects AS s2
ORDER BY s1.[object_id]) AS x) AS y;

TSQL SELECT data within range of year and month

I'm trying to query data within a range of start year and month and end year and month. But SQL returnes onty the year and the month chosen. Can anyone identify the problem with my approach.
Thanks!
ALTER PROCEDURE xxx
(#JaarBegin AS int
, #JaarEind AS int
, #MaandBegin AS int
, #MaandEind AS int)
AS
BEGIN
WITH
CTE AS
(
SELECT [D_Medewerker_ID]
,[Gebruikersnaam]
,[Naam]
,[Afdelingscode]
,CONVERT(date, [Datum_uit_dienst]) AS DatumIn
,CONVERT(date, [Datum_in_dienst]) AS DatumUit
FROM [DM].[dm].[D_Medewerker] AS M
),
CTE2 AS(
SELECT F.[D_Functie_ID]
,[Generieke_Functie]
,[Specifieke_Functie]
,Fo.[D_Medewerker_ID]
FROM [DM].[dm].[D_Functie] AS F
JOIN dm.dm.F_FormatieBezetting AS Fo
ON F.D_Functie_ID = Fo.D_Functie_ID
)
SELECT DISTINCT CTE.[Gebruikersnaam]
, CTE.Naam
, CTE.Afdelingscode
, CTE.DatumIn
, CTE.DatumUit
, CTE2.Generieke_Functie
, CTE2.Specifieke_Functie
FROM CTE
JOIN CTE2
ON CTE.D_Medewerker_ID = CTE2.D_Medewerker_ID
WHERE DATEPART(year,CTE.DatumUit) BETWEEN #JaarBegin AND #JaarEind
AND DATEPART(MONTH, CTE.DatumUit) >= #MaandBegin AND DATEPART(MONTH, CTE.DatumUit) <= #MaandEind
ORDER BY CTE.DatumUit DESC;
END
You need to convert the int values you get to a date value.
In Sql server 2012 or later, you can use the built-in function DATEFROMPARTS to do this:
WHERE CTE.DatumUit >= DATEFROMPARTS ( #JaarBegin , #MaandBegin , 1 )
AND CTE.DatumUit < DATEADD(MONTH, 1, DATEFROMPARTS ( #JaarEind , #MaandBegin , 1 ))
If you are working with an earlier version of sql server, you need to build a string that represents the date (using iso format yyyy-mm-dd) and then cast it to date:
WHERE CTE.DatumUit >= CAST(RIGHT('0000' + CAST(#JaarBegin as varchar(4)), 4) + '-' + RIGHT('00' + CAST(#MaandBegin as varchar(2)), 2) +'-01' as datetime)
AND CTE.DatumUit < DATEADD(MONTH, 1, CAST(RIGHT('0000' +CAST(#JaarEind as varchar(4)), 4) + '-' + RIGHT('00' + CAST(#MaandBegin as varchar(2)), 2) +'-01' as datetime))

IIF Conditional StartDate EndDate int/date collision

I am trying to use the IIF statements in SS-2012
I have this which works fine
SELECT
NULLIF(IIF( a.EndDate is null
, DATEDIFF(MONTH, a.StartDate, getdate()) ,
IIF( a.EndDate is not null
, DATEDIFF(MONTH, a.StartDate, a.EndDate) , '')),'')
AS Months
,NULLIF(IIF( a.EndDate is null
, DATEDIFF(Day, a.StartDate, getdate()) ,
IIF( a.EndDate is not null
, DATEDIFF(Day, a.StartDate, a.EndDate) , '')),'')
AS DateDays
FROM
TableDates a
The problem I came across is if the start date and enddate are on the same date(I am trying to default it to 1 month and 30 or 31 days whatever month it is)
I am gettiing
int is incompatible with date
When trying this below
,NULLIF(
IIF ( (a.EndDate is null) , DATEDIFF(Day, a.StartDate, getdate()) ,
IIF ( (a.StartDate = a.EndDate)
, DATEADD(Day, DATEDIFF(Day, a.StartDate, a.EndDate), a.EndDate),
IIF ( (a.EndDate is not null) , DATEDIFF(Day, a.StartDate, a.EndDate),'')
)),'')
How can I default the days to 30-31 and month to 1 if the dates are the same?
If you need to show the number of days of the month as default where start date and enddate are on the same date then i hope the below query will work for you
IIF ( (a.StartDate = a.EndDate),
(datediff(day, a.StartDate, dateadd(month, 1, a.StartDate))),...
and to show the month as default where start date and enddate are on the same date
IIF ( (a.StartDate = a.EndDate),Month(a.StartDate),...

SQL Server 2012: Round to NEAREST(!) start of month (in timestamp format) from a timestamp column)

I need to round to NEAREST start of month (in timestamp format) from a timestamp column.
How do one accomplish this?
Examples:
TimestampColumn A: Rounded to these values
2012-01-07 18:18:29.923 2012-01-01 00:00:00.000
2012-01-14 12:58:13.122 2012-01-01 00:00:00.000
2012-06-09 17:10:30.787 2012-06-01 00:00:00.000
2012-05-31 09:29:43.870 2012-06-01 00:00:00.000
2012-10-22 12:09:47.067 2012-11-01 00:00:00.000
2012-10-15 04:35:11.013 2012-10-01 00:00:00.000
Consider converting to date first
DECLARE #d DATETIME
set #d = CONVERT(DATE, '2012-02-14 12:58:13.122')
SET #d = DATEADD(DAY, 1-datepart(day, #d), #d)
SELECT #d
Here is one way to do it - just subtract all the parts of the date which you don't care about:
DECLARE #d DATETIME
set #d = '2012-02-14 12:58:13.122'
SET #d = DATEADD(DAY, 1-datepart(day, #d), #d)
SET #d = DATEADD(hour, -datepart(hour, #d), #d)
SET #d = DATEADD(minute, -datepart(minute, #d), #d)
SET #d = DATEADD(second, -datepart(second, #d), #d)
SET #d = DATEADD(millisecond, -datepart(millisecond, #d), #d)
SELECT #d
Calculate the length of the applicable month in seconds and then decide whether you are past the middle of the month. Go forward or back as needed.
declare #Foo as DateTime = '2012-10-15 12:35:11.013'
select
DateAdd( month, DateDiff( m, 0, #Foo ), 0 ) as 'Year/Month',
DateDiff( s, DateAdd( month, DateDiff( m, 0, #Foo ), 0), #Foo ) as 'Seconds Into Month',
DateDiff( s, DateAdd( month, DateDiff( m, 0, #Foo ), 0 ), DateAdd( month, DateDiff( m, 0, #Foo ) + 1, 0 ) ) as 'Seconds In Month',
DateDiff( s, DateAdd( month, DateDiff( m, 0, #Foo ), 0 ), DateAdd( month, DateDiff( m, 0, #Foo ) + 1, 0 ) ) / 2 as 'Seconds In Half Month',
DateAdd( month, DateDiff( m, 0, #Foo ) + Round( 1.0 * DateDiff( s, DateAdd( month, DateDiff( m, 0, #Foo ), 0), #Foo ) / DateDiff( s, DateAdd( month, DateDiff( m, 0, #Foo ), 0 ), DateAdd( month, DateDiff( m, 0, #Foo ) + 1, 0 ) ), 0 ), 0 ) as 'Rounded Date'
Thanks for contributing. But I'm going for this one; its good enough.
SELECT CASE
WHEN DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0)) >
ABS(DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)))
THEN DATEADD(dd, datediff(dd, 0, DATEADD(DAY, DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()), 0)), GETDATE() ) )+0, 0)
ELSE dateadd(dd, datediff(dd, 0, DATEADD(DAY, DATEDIFF(DAY, GETDATE(), DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) + 1, 0)), GETDATE() ) )+0, 0)
END
Here is an example. You have 5 fields: the date you want to convert, the converted date as you wanted, the first day of the month, the last day of the month, and the first day of the next month.
Just choose what you need in it:
SELECT
BED_Meeting_When,
CASE WHEN DAY(BED_Meeting_When) < 15 THEN (DATEADD(DAY, (-DAY(BED_Meeting_When) + 1), BED_Meeting_When)) ELSE DATEADD(DAY, (-DAY(BED_Meeting_When) + 1), DATEADD(MONTH, 1, BED_Meeting_When)) END,
DATEADD(DAY, (-DAY(BED_Meeting_When) + 1), BED_Meeting_When) AS 'Arrondi au premier du mois',
DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, BED_Meeting_When) + 1, 0)) AS 'Arrondi au dernier du mois',
DATEADD(DAY, (-DAY(BED_Meeting_When) + 1), DATEADD(MONTH, 1, BED_Meeting_When)) AS 'Arrondi au premier du mois suivant',
BEMR_Titre
FROM bpri_entretien_detail
INNER JOIN bpri_entretien_motif ON (BED_BEMR_Idx = BEMR_Idx)
Hi I know this is late to the party a little simple offering can never offend I hope
Creating a mini temp table I pop'd the dates offered in the original post
and then selected from it as below
create table #DT(
TS_A datetime, TS_B datetime)
insert into #DT (ts_a) values ('2012-01-07 18:18:29.923'),
('2012-01-14 12:58:13.122'),
('2012-06-09 17:10:30.787'),
('2012-05-31 09:29:43.870'),
('2012-10-22 12:09:47.067'),
('2012-10-15 04:35:11.013')
select TS_A, DATEADD(MONTH, DATEDIFF(MONTH, 0,ts_a), 0) TS_B from #DT
Results - I hope this is clear
RESULTS
Amazing how times they are a changing :-)
TX
I hope this turns out alright haven't posted any where for over a decade