Related
Is there any way to calculate length of service of an Employee when there is a gap in his tenure. I have a period1 from '08/09/2003' to '06/25/2009', period 2 is from '06/10/2015' to '03/31/2022' or GETDATE(). can someone explain me how can I calculate the whole period(Length of service) when there is a gap like this for that Employee ?
All dates above are in 'MM/DD/YYYY' format.
So, the Period1 service is - 5 Years, 10 Months, 16 Days
Period2 service is - 6 Years, 9 Months, 21 Days
The requirements is
total service should be - 12 Years, 8 Months, 7 Days
I know to calculate one period length but having issues while I try to calculate length when there is a gap
This soluction works pretty good. Although, counting days this way is kind off weird. If you start work at June 22nd and end July 22nd, have you then worked 1 month? or 30 days?
DECLARE #ServiceTbl TABLE (StartDate Date, EndDate Date)
DECLARE #StartDate date, #EndDate date
DECLARE #Sum TABLE (Years int, months int, days int)
INSERT INTO #ServiceTbl
SELECT '08/09/2003', '06/25/2009'
UNION SELECT '06/10/2015', null
DECLARE cur CURSOR LOCAL STATIC FORWARD_ONLY FOR
SELECT StartDate, ISNULL(EndDate, GetDate())
FROM #ServiceTbl
OPEN cur
WHILE 1=1
BEGIN
FETCH NEXT FROM cur INTO #StartDate, #EndDate
if ##FETCH_STATUS<>0 break
;WITH CTE AS(
SELECT
0 as countYear
, 0 as countMonth
, DATEDIFF(Day, #StartDate, EOMONTH(#StartDate, 0)) as CountDays
, EOMONTH(#StartDate, 1) as nextDate
, #EndDate as EndDate
, 0 as id
UNION ALL SELECT
CASE
WHEN nextDate < EndDate
AND countMonth + 1 = 12
THEN countYear + 1
ELSE countYear END
, CASE
WHEN nextDate < EndDate
AND countMonth + 1 < 12
THEN countMonth + 1
WHEN nextDate < EndDate
THEN 0
ELSE countMonth END
, CountDays + CASE
WHEN EndDate < EOMONTH(nextDate, 1)
THEN DATEDIFF(day, nextDate, EndDate)
ELSE 0 END
, EOMONTH(nextDate, 1)
, EndDate
, id + 1
FROM CTE
WHERE nextDate < EndDate
)
INSERT INTO #Sum
SELECT TOP 1 countYear, countMonth, CountDays
FROM CTE
ORDER BY id Desc
END
CLOSE cur DEALLOCATE cur
SELECT SUM(Years) + FLOOR((SUM(months) + FLOOR(SUM(Days) / 31)) / 12) as Years
, (SUM(months) + FLOOR(SUM(Days) / 31)) % 11 as Months
, SUM(days) % 30 as Days
FROM #Sum
You cannot calculate the years, months and days for each period and then sum them, because you don't know how to sum the days (31, 30, 29 or 28)
So, maybe if we do sum up all these values, and then add them to the enddate of the first period then we can calculate the values from the startdate and this extended endate ?
The outcome will never be exact, because of the difference in months length, but this maybe is as close that you can get
I tested it in this DBFiddle
But, I don't get your expected result because I still don't see how you get 9 months and 21 days from your second period.
Take a look at the dBFiddle, if it is not what you are looking for then maybe it helps inspire you in the right direction
select t2.period,
t2.startdate,
t2.enddate,
datediff(month, t2.startdate, t2.enddate) / 12 as years,
datediff(month, t2.startdate, t2.enddate) - ((datediff(month, t2.startdate, t2.enddate) / 12) * 12) as months,
datediff(day,
datefromparts(2022, datepart(month, t2.enddate), datepart(day, t2.startdate)),
datefromparts(2022, datepart(month, t2.enddate), datepart(day, t2.enddate))
) as days
from (select temp.period,
min(temp.startdate) as startdate,
dateadd(day, sum(temp.days), dateadd(month, sum(temp.months), dateadd(year, sum(temp.years), min(temp.startdate)))) as enddate
from ( select t.period,
t.startdate,
t.enddate,
datediff(month, t.startdate, t.enddate) / 12 as years,
datediff(month, t.startdate, t.enddate) - ((datediff(month, t.startdate, t.enddate) / 12) * 12) as months,
datediff(day,
datefromparts(2022, datepart(month, t.enddate), datepart(day, t.startdate)),
datefromparts(2022, datepart(month, t.enddate), datepart(day, t.enddate))
) as days
from table1 t
) temp
group by temp.period
) t2
I need to calculate using SQL Query, how many days within a given range fall into each calendar month.
I have given 2 dates, which define a date range; for example 2020-01-01 to 2020-08-03. I need to find how many days in that range fall in to each month i.e. how many fall into July, and how many into August.
In the example given, the expected result is 31 days in July and 3 days in August.
One approach uses a recusive query. Using date artithmetics, we can build the query so it performs one iteration per month rather than one per day, so this should be a rather efficient approach:
with cte as (
select
datefromparts(year(#dt_start), month(#dt_start), 1) month_start,
1 - day(#dt_start) + day(
case when #dt_end > eomonth(#dt_start)
then eomonth(#dt_start)
else #dt_end
end
) as no_days
union all
select
dateadd(month, 1, month_start),
case when #dt_end > dateadd(month, 2, month_start)
then day(eomonth(dateadd(month, 1, month_start)))
else day(#dt_end)
end
from cte
where dateadd(month, 1, month_start) <= #dt_end
)
select * from cte
Demo on DB Fiddle.
If we set the boundaries as follows:
declare #dt_start date = '2020-07-10';
declare #dt_end date = '2020-09-10';
Then the query returns:
month_start | no_days
:---------- | ------:
2020-07-01 | 22
2020-08-01 | 31
2020-09-01 | 10
You can refer this
;with dates(thedate) as (
select dateadd(yy,years.number,0)+days.number
from master..spt_values years
join master..spt_values days
on days.type='p' and days.number < datepart(dy,dateadd(yy,years.number+1,0)-1)
where years.type='p' and years.number between 100 and 150
-- note: 100-150 creates dates in the year range 2000-2050
-- adjust as required
)
select dateadd(m,datediff(m, 0, d.thedate),0) themonth, count(1)
from dates d
where d.thedate between '2020-01-01' and '2020-08-03'
group by datediff(m, 0, d.thedate)
order by themonth;
Please refer the link below where RichardTheKiwi user given a clear example for your scenario.
SQL Server query for total number of days for a month between date ranges
You can do all the work at the month level rather than the day level -- which should be a bit faster. Here is a method using a recursive CTE:
with cte as (
select #startdate as startdate, #enddate as enddate,
datefromparts(year(#startdate), month(#startdate), 1) as month
union all
select startdate, enddate, dateadd(month, 1, month)
from cte
where dateadd(month, 1, month) < #enddate
)
select month,
(case when month <= startdate and dateadd(month, 1, month) >= enddate
then day(enddate) - day(startdate) + 1
when month <= startdate
then day(eomonth(month)) - day(startdate) + 1
when dateadd(month, 1, month) < enddate
then day(eomonth(month))
when dateadd(month, 1, month) >= enddate
then day(enddate)
end)
from cte;
And the db<>fiddle.
The logic is simpler at the day level:
with cte as (
select #startdate as dte, #enddate as enddate
union all
select dateadd(day, 1, dte), enddate
from cte
where dte < enddate
)
select datefromparts(year(dte), month(dte), 1) as yyyymm, count(*)
from cte
group by datefromparts(year(dte), month(dte), 1)
order by yyyymm
option (maxrecursion 0)
Here is a solution with recursive CTE.
declare #startDate date = '2020-07-01'
declare #endDate date = '2020-08-03'
; WITH cte (n, year, month, daycnt)
AS (
SELECT
0
, DATEPART(year, #startDate)
, DATENAME(MONTH, #startDate)
, DATEPART(day, EOMONTH( #startDate ) ) - DATEPART(day, #startDate ) + 1
UNION ALL
SELECT
n + 1
, DATEPART(year, DATEADD(month, n + 1, #startDate) )
, DATENAME(MONTH, DATEADD(month, n + 1, #startDate) )
, IIF(
n = ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
, DATEPART(day, #endDate )
, DATEPART(day, EOMONTH( DATEADD(month, n + 1, #startDate) ) )
)
FROM
cte
WHERE
n <= ( DATEPART(month, #endDate) - DATEPART(month, #startDate) ) + ( DATEPART(year, #endDate) - DATEPART(year, #startDate) ) * 12 - 1
)
SELECT *
FROM cte
ORDER BY n
OPTION (maxrecursion 0)
This could be further simplified with a number function but that would also be essentially be a recursive CTE, though it would definitely look cleaner. But it requires defining a function on top of this SELECT statement.
I am trying to get the remaining number of working units for each month, of a sum between a bought number of working unit, and a consumed number of working unit.
I tried two possibilities, but both have flaws :
In the first test, I created a "Months" table that contains every month and every year, in order to show all months in the final matrix I wish to create with these data. With this one, I get the closing whenever there is a consumed working unit, but when there is not, the column is "empty", because it does not get the last closing.
USE OTRS_Revised
SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]
UNION ALL
SELECT '' AS Customer, CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], '' AS Total, '' AS Sum_bought
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month
I also tried to do it "month by month", with the below query. It works for one month, but I can't find any way to use this to get the results for each month of the year 2016.
SELECT
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Bought_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer)
+
(SELECT SUM(Closing) AS Expr1
FROM OTRS_Revised.dbo.WU_Consumed_View
WHERE (Customer LIKE 'SomeCustomer') AND (DATEADD(Year, Year - 1900, DATEADD(Month, Month - 1, DATEADD(day, 0, 0))) <= DATEADD(Year, 2016 - 1900, DATEADD(Month, 5 - 1, DATEADD(day, 0, 0))))
GROUP BY Customer) AS Expr1,
[Month]
FROM OTRS_Revised.dbo.Months
GROUP BY [Month]
SELECT b.*
FROM
( SELECT CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month]
FROM [dbo].Months
WHERE [Year] <= 2016
GROUP BY Year, Month
ORDER BY Customer, Year, Month ) AS a
LEFT OUTER JOIN
(SELECT [Customer], CASE WHEN [Year] < 2016 THEN 1 ELSE [Year] END AS [Year], CASE WHEN [Year] < 2016 THEN 0 ELSE [Month] END AS [Month], [Closing] AS Total, SUM([Closing])
OVER (PARTITION BY [Customer] ORDER BY [Year], [Month] ROWS UNBOUNDED PRECEDING) AS Closing
FROM [dbo].[WU_Closing_View]
WHERE [Customer] IN ('CustomerList')
GROUP BY [Customer], [Year], [Month], [Closing]) AS b
ON a.Month = b.Month )
In your approach when you do union the rows that don't have matching months is getting removed. Since you want the months that does not have closing match as well, you need to use the left outer join
Something like this perhaps
DECLARE #T TABLE (ID INT, ProductID INT, TrDate DATE,InOut VARCHAR(10),Amount INT)
INSERT INTO #T VALUES
(1 ,1, '2016-01-01', 'I', 100),
(2 ,2, '2016-01-01', 'I', 100),
(3 ,3, '2016-02-01', 'I', 100),
(4 ,4, '2016-03-01', 'I', 100),
(5 ,1, '2016-03-01', 'I', 100),
(6 ,2, '2016-04-01', 'O', 10),
(7 ,3, '2016-05-01', 'I', 100),
(8 ,5, '2016-05-01', 'I', 100),
(9 ,5, '2016-05-01', 'O', 100),
(10 ,6, '2016-05-01', 'I', 100)
declare #m table (id int, menddate date)
insert #m values
(1,'2015-12-31'),(2,'2016-01-31'),(3,'2016-02-29'),(4,'2016-03-31'),
(5,'2016-04-30'),(6,'2016-05-31'),(7,'2016-06-30'),(4,'2016-07-31')
Select *
from
(
select -- t.*
x.xproductid , x.xyyyymm,
SUM(t.total) OVER (partition by x.xproductid
ORDER BY x.xyyyymm
ROWS UNBOUNDED PRECEDING) AS CumulativeTotal
from
(
SELECT t.ProductID tproductid, year(t.trdate) * 100 + month(t.trdate) tyyyymm,
sum(case when t.Inout = 'I' then t.Amount else t.amount * -1 end) as total
FROM #T t
group by ProductID, year(t.trdate) * 100 + month(t.trdate)
) t
right outer join
(select distinct productid as xproductid,year(m.menddate) * 100 + month(m.menddate) xyyyymm from #t t, #m m) x on x.xproductid = t.tproductid and x.xyyyymm = t.tyyyymm
) z
where z.xyyyymm >= 201601
order by z.xProductID,z.xyyyymm
Note the use of a right outer join to get all the month ends for all products
I am trying to group every 7 days into a week but my issue is that the the hours for the first week is always off by a lot. What i mean by this is that we work 40 hours a week but ALWAYS the hours for first week is way off and the remaining weeks are okay. For example:
WeekBeginDate TotalHours
7/6/2015 10
7/13/2015 40
7/20/2015 40
I know the first week should be 40 but it shows less than 40 hours. Here is my sql.
Select x.UID,
Convert(VarChar, x.WeekBeginDate, 101) As WeekBeginDate,
x.TOTAL_HOURS
From
(
Select UID,
DateAdd(DD, Convert(Int, (DateDiff(DD, '1/1/1900', t.DT) / 7)) * 7, '1/1/1900') [WeekBeginDate],
Sum(HOURS) As TOTAL_HOURS
From myTable t
Where UID = 'mike01'
And DT >= DateAdd(Week, -3, GetDate())
Group By UID, Convert(Int, DateDiff(DD, '1/1/1900', t.DT) / 7)
)x;
Can you use the datepart function?
SELECT DATEPART(WEEK,GETDATE())
** EDIT **
I see what you're trying to do. Just convert all dates to beginning of the week and group by it.
SELECT UID,
DD - DATEPART(dw,DD) + 1 AS [WeekBeginDate],
SUM(HOURS) AS [HOURS]
FROM dbo.mytable
WHERE UID = 'mike01'
GROUP BY UID, DD - DATEPART(dw,DD) + 1
Here is the setup script I used to test it.
CREATE TABLE mytable (
[UID] varchar(100),
DD DATETIME,
[HOURS] DECIMAL(18,2)
)
INSERT INTO dbo.mytable
( UID, DD, HOURS )
VALUES ('mike01', '2015-07-30',1),
('mike01', '2015-07-29',6),
('mike01', '2015-07-23',2),
('mike01', '2015-07-16',3)
I have the following query
-- DELETE Current month and Month - 1 records from MonthlyTbl
DELETE FROM dbo.tbl_SDW_MONTHLY_AS
WHERE [Month Number] > MONTH(GETDATE()) - 2
AND [Year] = YEAR(GETDATE())
The query does not appear to be deleting the records from December 2013, can you help me fix this?
You can do it this way.
declare #dt datetime
set #dt = getdate()
DELETE FROM dbo.tbl_SDW_MONTHLY_AS
WHERE
100 * [Year] + [Month Number] >=
(
100 * datepart(YEAR, dateadd(DAY, - datepart(DAY, #dt) - 1, #dt)) +
datepart(MONTH, dateadd(DAY, - datepart(DAY, #dt) - 1, #dt))
)
Don't use MONTH number and YEAR number.
Use the dates instead.
DELETE FROM dbo.tbl_SDW_MONTHLY_AS
WHERE [Date] > DATEADD(MONTH,-2,GETDATE())