Checking for date range conflicts with Access - sql

I'm having trouble writing a query that will tell me if a given car is booked for a specified date period. My table is called Bookings and has CarID, StartDate and EndDate columns. I want to return a row if the car is booked for the period that the user enters. At the moment I have this query, which I got from the internet and some tinkering:
SELECT *
FROM Bookings
WHERE BookingID NOT IN
(SELECT BookingID
FROM Bookings
WHERE
(StartDate <= user_start_date AND EndDate >= user_start_date) OR
(StartDate <= user_end_date AND EndDate >= user_end_date) OR
(StartDate >= user_start_date AND EndDate <= user_end_date)) AND
(CarID = 7)
Here, I'm using user_start_date as my start date from the user and user_end_date as the end date from the user, and 7 for the car. However, the logic doesn't seem to work. Even when I verify there's absolutely no clashes in the dates it always returns rows.
How can I ammend this query so it works?
Thanks

Assuming that none of the dates can be null, and the start dates are less than the end dates, the following query should list any bookings for car 7 that overlap the given date range:
SELECT *
FROM Bookings
WHERE CarID = 7
AND StartDate < user_end_date
AND EndDate > user_start_date

Related

Generate List of dates between 2 dates for each Id

I have a table with PersonId's that each have a FirstSubscription date and LastSubscriptionDate.
What I need to do is between those 2 dates, generate 1 date for each month. This is for reporting purposes on the front end, as this data will end up inside PowerBI and I need these dates to join to a ReportingCalendar.
This Calendar is accessible by SQL so it can be used in this calculation. I am using it to generate the dates (using first of the month) between the First and LastSubDate but I need to find a way to join this with the rest of the ID's that way I get a list of date for each ID.
Here is my code to generate the dates.
DECLARE #MinDate DATE
DECLARE #MaxDate DATE
SET #MinDate = '2020-08-31'
SET #MaxDate = '2022-08-30'
SELECT DATEADD(month, DATEDIFF(month, 0, date), 0)
FROM dbo.ReportingCalendar
WHERE Date >= #MinDate
AND Date < #MaxDate
GROUP BY
DATEADD(month, DATEDIFF(month, 0, date), 0)
My PersonSubscription table looks like this
|PersonId|FirstSubDate|LastSubDate|
|--------|------------|-----------|
|1186 |8/31/2020 |8/30/2022 |
|2189 |7/30/2019 |7/31/2021 |
So I would want to end up with an output where each PersonId has 1 entry for each month between those 2 dates. So PersonId has 25 entries from 8/2020 until 8/2022. We don't care about the actual date of the sub since this data is looked at monthly and will primarily be looked at using a Distinct Count each month, so we only care if they were subbed at any time in that month.
I just needed to do a Cross Apply.
I took my code that got me all of the PersonId's and their FirstSubDate and LastSubDate and then did a cross apply to the code I listed above, referencing the MinDate and MaxDate with the FirstSubDate and LastSubDate.

To find from a table if there is any nearer date values for the same key

I have an sql table (t_accountdetails) with an account column called AccountId and effective date column for that account. An account can have multiple effective date. I have a requirement to get all the entries for the accounts which has very close effective date entries.(an offset of +/-14 days)
Say for eg:
AccountId: 12345 has got 2 entries with effective date 12/11/2017 and 12/18/2017
So my query should return above case where we have an entry of effective dates within offset of +/-14days
Please note I am actually not looking for date +-14 from today. I am looking for effective date which +/- 14 days of another effective date for the same account
You want all records where exists another effective date within 14 days, so use WHERE EXISTS:
select *
from t_accountdetails t
where exists
(
select *
from t_accountdetails other
where other.accountid = t.accountid
and other.id <> t.id
and abs(datediff(day, other.effective_date, t.effective_date)) <= 14
)
order by accountid, effective_date;
You can use the DATEADD function to make it work
select * from t_accountdetails where AccountId = 12345 and effectiveDate >= DATEADD(day, -14, getdate()) and effectiveDate <= DATEADD(day, 14, getdate())
This will return all records with AccountID = 12345 and an effective date between today - 14 days and today + 14 days.
Note: if more than one record match the criteria then all matching records will be returned.
I would be inclined to use lag() and lead():
select ad.*
from (select ad.*,
lag(effective_date) over (partition by accountid order by effective_date) as prev_ed,
lead(effective_date) over (partition by accountid order by effective_date) as next_ed
from t_accountdetails ad
) ad
where effective_date <= dateadd(day, 14, prev_ed) or
effective_date >= dateadd(day, -14, next_ed);
It would be interesting to compare the performance of this version to the exists version with an index on t_accountdetails(accountid, effective_date).

Get employees that were active during specific date range

How do I get list of active employees who were active in a specific date range (1-JAN-2014 TO 31-MAR-2014)
My table is like
Table:
employeeheader
empid
firstname
lastname
emphistory
empid
begindate
enddate
If enddate is null that means employee is still active.
SELECT * FROM employeeheader
JOIN emphistory ON employeeheader.empid = emphistory.empid
WHERE begindate <= <range_start> AND (enddate is null OR enddate >= <range_end>)
This gives all the employees which worked completely during a specific range.
Something like this might work - I didn't actually test it.
where begindate < the day after your end date
and nvl(enddate, sysdate) >= your start date
Since oracle dates include the time of day, you want to account for it, just in case.
This query can help
SELECT * FROM employeeheader
WHERE empid IN
(SELECT empid FROM emphistory
WHERE (enddate IS NULL OR enddate>= EndDateParameter)
AND begindate<=BeginDateParameter)

Decide if an employee is new or leavingemployee

I have employees with contracts in the database. I want to know if an employee is a new employee at a location. I have the following datastructure:
EmployeeId Index BeginDate Enddate HoursToWork LocationId
12133 1 2013-01-01 2014-01-01 10 1
12133 2 2013-06-01 2014-01-01 20 1
12133 3 2012-01-01 2014-01-01 5 1
As you can see, an employee can have more than 1 contract on a location. Then Endate can be null.
Per location and per Month or Quarter I want to see how many employees are started. I want to use #Startdate and #Enddate parameters for the period I want have the data.
There are to many cases I should take into account. Like, the Index field is not Always increased together with the Begindate, like you can see at Index = 3.
Example:
I want to know how many employees are started in january 2013.
In this case nothing because the first contract was started on 2012-01-01. There are two new contracts but this employee is not new for the location. But if index 3 was not existing then whis should be a new employee.
It can be that a employee have two contract that starts on the same date and if he doesnt have before a contract then it is 1 new employee.
I already tried the following, which works when an employee just has 1 contract. But if there are more than 1 contracts then it is hard to decide if the employee is new:
declare #Startdate datetime set #Startdate = '2013-01-01'
declare #Enddate datetime set #Enddate = '2013-12-31'
select EmployeeId, Index, BeginDate, Enddate, HoursToWork, LocationId
,(case
when BeginDate between #Startdate and #Enddate then 1
end) as NewEmployee
,(case
when Enddate between #Startdate and #Enddate then 1
end) as LeavingEmployee
from Contracts
Given the 3 records, this employee is not an new employee. I would like to have a output like:
LocationId NewEmployee
1 0
When I just have the first 2 records and I want know new employees in Janury 2013 then I expect:
LocationId NewEmployee
1 1
How about something like this for starting employee?
SELECT EmployeeID, LocationID, Min(StartDate)
FROM Contracts
GROUP BY EmployeeID, LocationID
HAVING Min(StartDate) between #Startdate and #Enddate
I would suggest something similar for Leavingemployee, but I would not spend much effort trying to get those into one query. It seems they are functionally different.
edit: es, it should be Min, not max. As for what is needed, i read "I want to see how many employees are started", and i didn't see much reason to complicate matters.
If additional data is needed, it's always possible to move this to a subquery and select * where ... in subquery, but if it's not needed...
In order to only deal with active contract (contracts that are active during the time interval for the query) we can set the following rules:
A contract is active somewhere during the period we are looking at if its startdate < the parameter enddate and its enddate > the parameter startdate.
Adding that to our query, we get
SELECT EmployeeID, LocationID, Min(StartDate)
FROM Contracts
WHERE Startdate <= #Enddate
AND Enddate >= #Startdate
GROUP BY EmployeeID, LocationID
HAVING Min(StartDate) between #Startdate and #Enddate

MySQL - Reporting using a week to represent all rows with a datestamp in that week

I am trying to build a table of data where each row represents a week (i.e., 09/23-09/30). Each row is built from data from several different rows that have datestamps within that week period. The rest of the columns are subselects that typically give averages of all the data in all the rows in the week period.
Logically, what is the best way to go about this? I've managed to create a query that gives me one row with a startDate and endDate and all the data for the startDate and endDate.
What I need is to have the query return each consequent weeks single row of data, basically giving me the entire report as opposed to one line of the report.
Unfortunately, I can't simply loop through the query in PHP. This has to be in a stored procedure when all is said and done.
Edit:
Here is the SQL I am currently using
BEGIN
if (NOT fromDate) then
SET fromDate = "2007-9-25";
end if;
if (NOT toDate) then
SET toDate = CURDATE();
end if;
SELECT
(SELECT
CONCAT(
MONTH(fromDate),
'/',
DAY(fromDate),
' - ',
MONTH(toDate),
'/',
DAY(toDate)
)
) AS dateRange
,
(SELECT
COUNT(DISTINCT kioskID)
FROM tbl_rpt_kiosksessions
WHERE startDate
BETWEEN fromDate AND toDate
) AS NumberOfStores
,
(SELECT
COUNT(sessionID)
FROM tbl_rpt_kiosksessions
WHERE startDate
BETWEEN fromDate AND toDate
) AS NumberOfSessions
,
(
(
(
(SELECT
COUNT(sessionID)
FROM tbl_rpt_kiosksessions
WHERE startDate
BETWEEN fromDate AND toDate
)
/
(SELECT
COUNT(DISTINCT kioskID)
FROM tbl_rpt_kiosksessions
WHERE startDate
BETWEEN fromDate AND toDate
)
)
/7)
) AS SessionsPerDay
,
(SELECT
(AVG(duration)/60)
FROM tbl_rpt_kiosksessions
WHERE startDate
BETWEEN fromDate AND toDate
) AS AvgSessionLength
FROM
tbl_rpt_kiosksessions
WHERE
startDate
BETWEEN "2010-02-09" AND CURDATE()
GROUP BY DATE_FORMAT(startDate, '%Y-%V');
END
The result of this supposing the following parameters ('2010-02-07','2010-02-14') is:
Image of the record returned
Which is obviously exactly what you'd expect. What I need to figure out is how to get this automated using just a startDate. So, for instance:
startDate = '2010-01-24'
And then getting result sets returned for:
01/24 - 01/30
01/31 - 02/06
02/07 - 02/14
and so on until the endDate is given or is simply CURDATE().
Thoughts?
SELECT data columns / aggregate functions FROM table GROUP BY DATE_FORMAT(date_column, '%Y-%V')
(%V assumes weeks start on Sunday - switch to %v for Monday)