SQL determine schedule availability - sql-server

I have a tbl_availability that determines when a resource is available. The table structure is:
id - running id
startdate - when this availability starts
enddate - when this availability ends
dayofweek - weekday of availability
fromtime - start time
totime - end time
There can be multiple records for the same dayofweek, for example one record for Sundays 1000-1200 and another record for Sundays 1300-1400.
I am trying to figure out how to get two things:
Check when entering a new record that there is no conflict (overlap)
with an existing record
Given a startdate and enddate, find all of
the available periods that apply.

To determine if there's a conflict this query will return any overlapping time ranges:
SELECT * FROM tbl_availability
WHERE (startDate > #CheckEnd AND startDate <= #CheckStart)
OR (endDate < #CheckStart AND endDate >= #CheckEnd)
OR (startDate >= #CheckStart AND endDate <= #CheckEnd)
The first part of the where clause checks for anything that overlaps the start time. The second part check for anything that overlaps the end time. The third part check for anything within the range.
To check for available time ranges for a specified duration use this:
SELECT * FROM
(SELECT endDate AS PeriodStart, (SELECT TOP 1 startDate
FROM tbl_availability as sub
WHERE sub.startDate > tbl_availability.endDate
ORDER by sub.startDate) AS PeriodEnd
FROM tbl_availability) AS OpenPeriods
WHERE DateDiff(MINUTE, PeriodStart, PeriodEnd) >= DateDiff(MINUTE, #RangeStart, #RangeEnd)
I haven't tested these queries, so there may have to be some tweaking going on here.

Related

Partition and group by query

I have table containing the columns:
1. ClockifyId,
2. StartTime EndTime of every Task
3. Date.
4. Duration
The image is attached below
My goal is to write query to calculate the total duration of every user(which is ClockifyId) of every date.
As One User can have multiple task in one day, I wanted to sum duration of all those task. In short,
I wanted to have total task duration of every user(which is clockifyid) of every date.
enter image description here
There are a couple of details missing here, but this should get you close enough.
The first thing you need to do is convert the StartTime and EndTime to datetime fields if they aren't already. Doing a DATEDIFF on them allows you to figure out per record what the difference in minutes is. You can change the unit of measure as needed.
Once you do that, you use the SUM() which is an aggregate function. This makes it necessary to use the GROUP BY. You then group by which ever fields, in this case the ClockifyId and the StartTime as a date. You have to do it as a date without the datetime or you will get multiple rows back for a single Clockify record in a day.
SELECT
ClockifyId
, SUM(DATEDIFF(mi, CAST(StartTime AS datetime), CAST(EndTime AS datetime))) AS DurationInMinutes
, CAST(StartTime AS date)
FROM TableName
GROUP BY
ClockifyId
, CAST(StartTime AS date)
It's worth noting that this assumes there is always a valid StartTime and EndTime. This will throw some errors if those fields have nulls.

How to do WHERE <before> an aggregate function (Postgres)

It's hard to explain from the title, but this is my SQL:
SELECT
SUM("payments"."amount"),
"invoices"."property_id"
FROM "payments"
JOIN "invoices"
ON "payments"."invoice_id" = "invoices"."id"
GROUP BY "property_id"
It returns the sum of all Payment records (amount column) for a particular Property (which is connected through it's invoices).
In other words:
Property has_many: :invoices
Invoice has_one: :payment
I'm trying to select payments between a particular date range though, but it has to happen "before" the aggregate function (so do the exact query above, but only for 2017-01-01 through 2017-02-01). The field would be generated_at on Payment
You are looking for a WHERE clause. (WHERE is executed before aggregation; HAVING is executed after.) Suggested date literals in PostgreSQL are ANSI standard DATE 'YYYY-MM-DD'. Date ranges are usually checked with >= start day and < end day + 1 (in order to deal properly with the time part if any).
SELECT
SUM(p.amount),
i.property_id
FROM payments p
JOIN invoices i ON p.invoice_id = i.id
WHERE p.generated_at >= DATE '2017-01-01'
AND p.generated_at < DATE '2017-02-02'
GROUP BY i.property_id;

Factoring public holidays in to a SQL code

Apologies if this is a simple one. I'm looking for some help with the following:
SELECT *
FROM (
SELECT TOP 7
RIGHT (CONVERT (VARCHAR, CompletedDate, 108), 8) AS Time,
WorkType
FROM Table
WHERE WorkType = 'WorkType1'
OR DATEPART (DW, CompletedDate) IN ('7','1')
AND WorkType = 'WorkType2'
ORDER BY CompletedDate DESC) Table
ORDER BY CompletedDate ASC
Multiple events run every day, and the above searches for the last one scheduled to run each day, and pulls the time from it for the past 7 days. This time marks the end of the day's events, and is the value I'm after.
Events run at a different order on weekends, so I search for a different WorkType. WorkType 1 is unique to weekdays. WorkType2 is run both at weekdays and weekends, however it is not the final event on a weekday, so I don't search for it then.
However, this kind of falls apart when public holidays such as bank holidays come into play, as they use the weekend timings. I still need to capture these times, but the above skips over them. If I were to remove or expand the DATEPART, I would end up with duplicate values for each day that don't mark the final job of the day.
What changes can I make to this to capture these lost holiday timings, without manually going through and checking every holiday? Is there a way that I can return a value for JobType2, if JobType1 does not appear on a day?
I suggest a materialized calendar table with one row per date along with the desired WorkType for that day. That will allow you to simply join on to the calendar table to determine the proper WorkType value without embedding the logic in the query itself.
With this table loaded with all dates for your reporting domain:
CREATE TABLE dbo.WorkTypeCalendar(
CalendarDate date NOT NULL
CONSTRAINT PK_Calendar PRIMARY KEY CLUSTERED
, WorkType varchar(10) NOT NULL
);
GO
The query can be refactored as below:
SELECT *
FROM ( SELECT TOP 7
RIGHT(CONVERT (varchar, CompletedDate, 108), 8) AS Time
, WorkType
FROM Table1 AS t
JOIN WorkTypeCalendar AS c ON t.WorkType = c.WorkType
AND t.CompletedDate >= c.CalendarDate
AND t.CompletedDate < DATEADD(DAY,
1,
c.CalendarDate)
ORDER BY CompletedDate DESC
) Table1
ORDER BY CompletedDate ASC
You also might consider making this a generalized utility calendar table. See http://www.dbdelta.com/calendar-table-and-datetime-functions/ for an complete example of such a table and script to load US holidays you can adjust for your needs and locale.

Filtering columns based on a date in a field for current week, past weeks, and future weeks

Example Table:
(Column Heading) Total Interviews of everyone --- (Row Heading) Interview Requested
(Column Heading) Number of interviews this week -- (Row Value) Count all the dates scheduled this week that have the status: "Interview Requested" e.g 4
(Column Heading) Number of interviews next week -- (Row Value) Count all the dates scheduled next week that have the status: "Interview Requested" e.g 6
First of all i'll like to aploigise about my makeshift table, it seems that i cant use pictures since im a new member.
I am also new to using reportbuilder and sql and have been trying to figure out how i can count the amount of dates that fall between certain date ranges in a report.
Like ive stated in the makeshift field called "Number of interviews this week" I wish to count all the dates scheduled for the current week, the week after, the week before and so on based on a date field. I then plan to break this table down to show all the interviews a single person has schedueled.
The issue I am having is that there will be many dates spanning different months/days. So Im not sure how i can represent this with DateDiff or other date features, because the table is meant to be a live representation of the current interviews.
Is what i'm trying to do even possible using report builder?? If so any tips would be great, if not then thanks for taking the time to look at my question.
As Requested Dataset Fields:
[UserName] (User table), [InterviewStatus] (Interview table), [DateUpdated] (Interview Table)
Those are the main ones that will be used in the report
If you just want to count the number of columns of each field, you can use the sql statement COUNT() see COUNT().
You can do the common query like this:
SELECT COUNT(fieldName1), COUNT(fieldName2), ... , COUNT(fieldNamen) FROM <tableName>
Also try to show your codes so that your question will be more clarified.
You can use the generic select statement from your interview table filtering in the where clause for dates between the beginning of the current week and the end of the next.
Then the two fields would be a sum of 1 if the date falls within the current week or next.
EDIT:
Example below will give the number of dates within this week. Taken from another question:
Select InterviewStatus,
Sum(Case when DateUpdated >= DATEADD(dd, -(DATEPART(dw, GETDATE()) -1), GETDATE())
AND DateUpdated < CAST(CONVERT(VARCHAR(10), DATEADD(dd, (8 - DATEPART(dw, GETDATE())), GETDATE()), 120) AS DATETIME)
Then 1
Else 0 end)
as NumberInCurrentWeek
From InterviewTable
Where DateUpdated >= DATEADD(dd, -(DATEPART(dw, GETDATE()) -1), GETDATE())
AND DateUpdated < CAST(CONVERT(VARCHAR(10), DATEADD(dd, (8 - DATEPART(dw, GETDATE())), GETDATE()), 120) AS DATETIME)
Group by InterviewStatus
This just needs to be tweaked to your exact circumstance. For example alter the 8 by 7 will capture the current fortnight.
Then produce a simple table based on the query.

SQL Server Retrieving Recurring Appointments By Date

I'm working on a system to store appointments and recurring appointments. My schema looks like this
Appointment
-----------
ID
Start
End
Title
RecurringType
RecurringEnd
RecurringTypes
---------------
Id
Name
I've keeped the Recurring Types simple and only support
Week Days,
Weekly,
4 Weekly,
52 Weekly
If RecurringType is null then that appointment does not recur, RecurringEnd is also nullable and if its null but RecurringType is a value then it will recur indefinatly. I'm trying to write a stored procedure to return all appointments and their dates for a given date range.
I've got the stored procedure working for non recurring meetings but am struggling to work out the best way to return the recurrences this is what I have so far
ALTER PROCEDURE GetAppointments
(
#StartDate DATETIME,
#EndDate DATETIME
)
AS
SELECT
appointment.id,
appointment.title,
appointment.recurringType,
appointment.recurringEnd,
appointment.start,
appointment.[end]
FROM
mrm_booking
WHERE
(
Start >= #StartDate AND
[End] <= #EndDate
)
I now need to add in the where clauses to also pick up the recurrences and alter what is returned in the select to return the Start and End Dates for normal meetings and the calculated start/end dates for the recurrences.
Any pointers on the best way to handle this would be great. I'm using SQL Server 2005
you need to store the recurring dates as each individual row in the schedule. that is, you need to expand the recurring dates on the initial save. Without doing this it is impossible to (or extremely difficult) to expand them on the fly when you need to see them, check for conflicts, etc. this will make all appointments work the same, since they will all actually have a row in the table to load, etc. I would suggest that when a user specifies their recurring date, you make them pick an actual number of recurring occurrences. When you go to save that recurring appointment, expand them all out as individual rows in the table. You could use a FK to a parent appointment row and link them like a linked list:
Appointment
-----------
ID
Start
End
Title
RecurringParentID FK to ID
sample data:
ID .... RecurringParentID
1 .... null
2 .... 1
3 .... 2
4 .... 3
5 .... 4
if in the middle of the recurring appointments schedule run, say ID=3, they decide to cancel them, you can follow the chain and delete the remaining ID=3,4,5.
as for expanding the dates, you could use a CTE, numbers table, while loop, etc. if you need help doing that, just ask. the key is to save them as regular rows in the table so you don't need to expand them on the fly every time you need to display or evaluate them.
I ended up doing this by creating a temp table of everyday between the start and end date along with their respective day of the week. I limited the recurrence intervals to weekdays and a set amount of weeks and added where clauses like this
--Check Week Days Reoccurrence
(
mrm_booking.repeat_type_id = 1 AND
#ValidWeeklyDayOfWeeks.dow IN (1,2,3,4,5)
) OR
--Check Weekly Reoccurrence
(
mrm_booking.repeat_type_id = 2 AND
DATEPART(WEEKDAY, mrm_booking.start_date) = #ValidWeeklyDayOfWeeks.dow
) OR
--Check 4 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 3 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*4) = 0
) OR
--Check 52 Weekly Reoccurences
(
mrm_booking.repeat_type_id = 4 AND
DATEDIFF(d,#ValidWeeklyDayOfWeeks.[Date],mrm_booking.start_date) % (7*52) = 0
)
In case your interested I built up a table of the days between the start and end date using this
INSERT INTO #ValidWeeklyDayOfWeeks
--Get Valid Reoccurence Dates For Week Day Reoccurences
SELECT
DATEADD(d, offset - 1, #StartDate) AS [Date],
DATEPART(WEEKDAY,DATEADD(d, offset - 1, #StartDate)) AS Dow
FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY s1.id) AS offset
FROM syscolumns s1, syscolumns s2
) a WHERE offset <= DATEDIFF(d, #StartDate, DATEADD(d,1,#EndDate))
Its not very elegant and probably very specific to my needs but it does the job I needed it to do.

Resources