T-SQL absence by month from start date end date - sql

I have an interesting query to do and am trying to find the best way to do it. Basically I have an absence table in our personnel database this records the staff id and then a start date and end date for the absence. End date being null if not yet entered (not returned). I cannot change the design.
They would like a report by month on number of absences (12 month trend). With staff being off over the month change it obviously may be difficult to calculate.
e.g. Staff off 25/11/08 to 05/12/08 (dd/MM/yy) I would want the days in November to go into the November count and the ones in December in the December count.
I am currently thinking in order to count the number of days I need to separate the start and end date into a record for each day of the absence, assigning it to the month it is in. then group the data for reporting. As for the ones without an end date I would assume null is the current date as they are presently still absent.
What would be the best way to do this?
Any better ways?
Edit: This is SQL 2000 server currently. Hoping for an upgrade soon.

I have had a similar issue where there has been a table of start/end dates designed for data storage but not for reporting.
I sought out the "fastest executing" solution and found that it was to create a 2nd table with the monthly values in there. I populated it with the months from Jan 2000 to Jan 2070. I'm expecting it will suffice or that I get a large pay cheque in 2070 to come and update it...
DECLARE TABLE months (start DATETIME)
-- Populate with all month start dates that may ever be needed
-- And I would recommend indexing / primary keying by start
SELECT
months.start,
data.id,
SUM(CASE WHEN data.start < months.start
THEN DATEDIFF(DAY, months.start, data.end)
ELSE DATEDIFF(DAY, data.start, DATEADD(month, 1, months.start))
END) AS days
FROM
data
INNER JOIN
months
ON data.start < DATEADD(month, 1, months.start)
AND data.end > months.start
GROUP BY
months.start,
data.id
That join can be quite slow for various reasons, I'll search out another answer to another question to show why and how to optimise the join.
EDIT:
Here is another answer relating to overlapping date ranges and how to speed up the joins...
Query max number of simultaneous events

Related

SQL - Count month difference in non-consecutive date period

I am trying to extract how many months of membership a member gets up to date. As the picture shows, this member got four years of subscription since 2018. However, she stopped the subscription for a year ending in 2019. And then, restart the membership in 2020 again.
Each membership lasts for 12 months. if we look at the last membership starting from 2022-05-08, it will end up on 2023-05-08. However, I only want to get the total month count up to date(getdate - 2022-09-14).
Please advise how I could approach this matter. Thanks!
enter image description here
Assuming you were planning to apply sum(monthcount), you could wrap the monthcount within a CASE statement to checks if the vip_end is greater than today's date:
sum(case when vip_end > getdate() then ... else monthcount end)
What you do within that ... depends on whether you wanted to just count the different number of months within the date range (e.g. 31st Jan -> 01st Feb is counted as a whole month because it just considers Jan -> Feb):
datediff(month, datecreated, getdate())
or perhaps calculate the number of months based on the average days in a month:
datediff(day, datecreated, getdate())*12.0/365.25
or maybe something else... it really depends on what level of detail you want to achieve.

how to automate the date change in a query using transact sql

I work for a company where everyday I modify a query by changing the date of the day before, because the report is always from the previous day.
I want to automate the date change. I have made a table with two columns, one with all dates from this year and another with bits where if 0 is a working day and 1 if is a holiday.
I have successfully automated a little bit by telling if the day before is a working day then subtract 1 from the date (This is what happens everyday). But the problem is, that if is Monday appears as Friday, because Saturday and Sunday are not billable. And let's also say, that if today is Thursday and Wednesday and Tuesday we're holidays, then the report will run on Monday. I will leave you a picture, that shows how the table is made with dates.
Remembering, that if there is no holidays in the middle of the week, always will be subtract one.
The way to do this sort of thing is close to what you have done, but just extend it further. Create a BusinessDate table that has every date, and then every rule you have implemented inside it. You can go so far as to include a column such as ReportDate which will return,for every date, what date the report should be run for.
Do this once, and it will work forever more. You may have to update for future holidays once a year, but better than once a day!
It will also allow you to update things specific for your business, like quarter dates, company holidays, etc.
If you want to know more on the subject, look up topics around creating a date dimension in a data warehouse. Its the same general issue you are facing.
Too complicated for a comment and it involves a lot of guessing.
So everyday, your process starts by first determining if "today" is a work day. So you would do something like:
if exists (select * from <calendar> where date = cast (getdate() as date) and IsWorkday = 1")
begin
<do stuff>
end;
The "do stuff" section would then run a report or your query (or something that isn't very clear) using the most recent work day prior to the current date. You find that date using something like:
declare #targetdate date;
set #targetdate = (select max(date) from <calendar>
where date < cast (getdate() as date)
and IsWorkday = 1);
if #targetdate is not null
<run your query using #targetdate>
That can be condensed into less code but it is easier to understand when the logic is written step-by-step.

WHERE statement to choose record previous day but choose Friday record when current day is Monday Microsoft SQL

I need a WHERE statement where the date of the record is the previous day. I have the below code which will do this
WHERE DOC_DATE = dateadd(day,datediff(day,1,GETDATE()),0)
However I need this statement to get Friday's record when the current day is Monday. I have the below code but it will not work for me. No errors come back on SQL although no records results come back either. I have the below code for this
WHERE DOC_DATE = DATEADD(day, CASE WHEN datepart(dw,(GETDATE())) IN (2) then -3 ELSE -1 END ,0)
Important to add that this needs to be in a WHERE clause. This is for a Docuware administrative view I am creating. I have no control on how to write the SELECT statement, I only have access to edit the WHERE clause:
Here's a slightly "magical" way to compute the value that doesn't depend on any particular server settings such as datefirst. It's probably not immediately obvious how it works:
WHERE DOC_DATE = dateadd(day,datediff(day,'20150316',getdate()),
CASE WHEN DATEPART(weekday,getdate()) = DATEPART(weekday,'20150330')
THEN '20150313'
ELSE '20150315' END)
In the first line, we're computing the number of days which have elapsed since some arbitrary date. I picked a day in March 2015 to use as my base date.
The second line asks what today's day of the week is and if it's the same as some arbitrary "Known good" Monday. Just taking one value and comparing it to 2 depends on what your DATEFIRST setting is so I prefer not to write that.
In the third line, we decide what to do if it's a monday - I give a date that is 3 days before my arbitrary date above. If it wasn't a monday, we pick the day before.
Adding it all together, when we add the days difference from the arbitrary date back to one of these two dates from lines 3 and 4, it has the effect of shifting the date backwards 1 or 3 days.
It's can be an odd structure to see if you're not familiar with it - but combining dateadd/datediff and exploiting relationships between an arbitrary date and other dates computed from it can be useful for performing all kinds of calculations. A similar structure can be used for computing e.g. the last day of the month 15 months ago using just dateadd/datediff, an arbitrary date and another date with the right offset from the first:
SELECT DATEADD(month,DATEDIFF(month,'20010101',GETDATE()),'19991031')
As I said in a comment though, usually doing this sort of thing is only a short step away from needing to properly model your organisation's business days, at which point you'd typically want to introduce a calendar table. At one row per day, 20 years worth of pre-calculated calendar (adjusted as necessary as the business changes) is still less than 10000 rows.
You can try this.
WHERE DOC_DATE = DATEADD(DAY, CASE WHEN datepart(dw, GETDATE()) = 2 THEN -3 ELSE -1 END, CAST(GETDATE() AS DATE))

Get the month and year now then count the number of rows that are older then 12 months in SQL/Classic ASP

I know this one is pretty easy but I've always had a nightmare when it comes to comparing dates in SQL please can someone help me out with this, thanks.
I need to get the month and year of now then compare it to a date stored in a DB.
Time Format in the DB:
2015-08-17 11:10:14.000
I need to compare the month and year with now and if its > 12 months old I will increment a count. I just need the number of rows where this argument is true.
I assume you have a datetime field.
You can use the DATEDIFF function, which takes the kind of "crossed boundaries", the start date and the end date.
Your boundary is the month because you are only interested in year and month, not days, so you can use the month macro.
Your start time is the value stored in the table's row.
Your end time is now. You can get system time selecting SYSDATETIME function.
So, assuming your table is called mtable and the datetime object is stored in its date field, you simply have to query:
SELECT COUNT(*) FROM mtable where DATEDIFF(month, mtable.date, (SELECT SYSDATETIME())) > 12

SQL Query to Count Number of Days, Excluding Holidays/Weekends

I have a "workDate" field and a "receivedDate" field in table "tblExceptions." I need to count the number of days beteen the two. workDate always comes first - so, in effect, it's kind of like workDate is "begin date" and receivedDate is "end date". Some exclusions make it tricky to me though:
First, I need to exclude holidays. i do have a table called "tblHolidays", with one field - holidayDate. I have holidays stored up through next year, so maybe I can incorporate that into the query?
Also, most flummoxing is that work dates can never occur on weekends, but received dates can. So, i somehow need to exclude weekends when i'm counting, but allow for a weekend if the received date happens to fall on a saturday or sunday. so, if the work date is June 3rd, 2011, and received date is June 11th, 2011, the count of valid days would be 7, not 9.
Any help on this is much appreciated. Thanks!
Something like this should give you the number of days with the holidays subtracted:
select
days = datediff(day, workDate, receivedDate)
- (select count(*) from tblHolidays where holidayDate >= workDate and holidayDate < receivedDate)
from
tblExceptions
Note that the date functions differ between database systems. This is based on MS SQL Server, so it may need adapting if you are using some other database.
If you have a table full of dates to include (non-weekends, non-holidays, etc.) and you knew when the 'begin' date and the 'end' date is, then you can do something roughly like this:
select count(*) from tblIncludedDates where beginDateValue <= dateField and endDateValue >= dateField
to get the number of valid days between those dates.