Check that the dates are not overlapping - laravel-6

Check that the date doesn't overlap.
I'm creating a site for an employee leave management, where an employee wants to apply for a leave and the leaves can not overlap.
programming lang: laravel 6, jQuery

(StartA <= EndB) and (EndA >= StartB)
Proof:
Let ConditionA Mean that DateRange A Completely After DateRange B
_ |---- DateRange A ------|
|---Date Range B -----| _
(True if StartA > EndB)
Let ConditionB Mean that DateRange A is Completely Before DateRange B
|---- DateRange A -----| _
_ |---Date Range B ----|
(True if EndA < StartB)
Then Overlap exists if Neither A Nor B is true -
(If one range is neither completely after the other,
nor completely before the other, then they must overlap.)
Now one of De Morgan's laws says that:
Not (A Or B) <=> Not A And Not B
Which translates to: (StartA <= EndB) and (EndA >= StartB)

Related

SQL filter if given date range exist [duplicate]

This table is used to store sessions (events):
CREATE TABLE session (
id int(11) NOT NULL AUTO_INCREMENT
, start_date date
, end_date date
);
INSERT INTO session
(start_date, end_date)
VALUES
("2010-01-01", "2010-01-10")
, ("2010-01-20", "2010-01-30")
, ("2010-02-01", "2010-02-15")
;
We don't want to have conflict between ranges.
Let's say we need to insert a new session from 2010-01-05 to 2010-01-25.
We would like to know the conflicting session(s).
Here is my query:
SELECT *
FROM session
WHERE "2010-01-05" BETWEEN start_date AND end_date
OR "2010-01-25" BETWEEN start_date AND end_date
OR "2010-01-05" >= start_date AND "2010-01-25" <= end_date
;
Here is the result:
+----+------------+------------+
| id | start_date | end_date |
+----+------------+------------+
| 1 | 2010-01-01 | 2010-01-10 |
| 2 | 2010-01-20 | 2010-01-30 |
+----+------------+------------+
Is there a better way to get that?
fiddle
I had such a query with a calendar application I once wrote. I think I used something like this:
... WHERE new_start < existing_end
AND new_end > existing_start;
UPDATE This should definitely work ((ns, ne, es, ee) = (new_start, new_end, existing_start, existing_end)):
ns - ne - es - ee: doesn't overlap and doesn't match (because ne < es)
ns - es - ne - ee: overlaps and matches
es - ns - ee - ne: overlaps and matches
es - ee - ns - ne: doesn't overlap and doesn't match (because ns > ee)
es - ns - ne - ee: overlaps and matches
ns - es - ee - ne: overlaps and matches
Here is a fiddle
SELECT * FROM tbl WHERE
existing_start BETWEEN $newStart AND $newEnd OR
existing_end BETWEEN $newStart AND $newEnd OR
$newStart BETWEEN existing_start AND existing_end
if (!empty($result))
throw new Exception('We have overlapping')
These 3 lines of sql clauses cover the 4 cases of overlapping required.
Lamy's answer is good, but you can optimize it a little more.
SELECT * FROM tbl WHERE
existing_start BETWEEN $newSTart AND $newEnd OR
$newStart BETWEEN existing_start AND existing_end
This will catch all four scenarios where the ranges overlap and exclude the two where they don't.
I had faced the similar problem. My problem was to stop booking between a range of blocked dates. For example booking is blocked for a property between 2nd may to 7th may. I needed to find any kind of overlapping date to detect and stop the booking. My solution is similar to LordJavac.
SELECT * FROM ib_master_blocked_dates WHERE venue_id=$venue_id AND
(
(mbd_from_date BETWEEN '$from_date' AND '$to_date')
OR
(mbd_to_date BETWEEN '$from_date' AND '$to_date')
OR
('$from_date' BETWEEN mbd_from_date AND mbd_to_date)
OR
('$to_date' BETWEEN mbd_from_date AND mbd_to_date)
)
*mbd=master_blocked_dates
Let me know if it doesn't work.
Given two intervals like (s1, e1) and (s2, e2) with s1<e1 and s2<e2
You can calculate overlapping like this:
SELECT
s1, e1, s2, e2,
ABS(e1-s1) as len1,
ABS(e2-s2) as len2,
GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0)>0 as overlaps,
GREATEST(LEAST(e1, e2) - GREATEST(s1, s2), 0) as overlap_length
FROM test_intervals
Will also work if one interval is within the other one.
Recently I was struggling with the same issue and came to end with this one easy step (This may not be a good approach or memory consuming)-
SELECT * FROM duty_register WHERE employee = '2' AND (
(
duty_start_date BETWEEN {$start_date} AND {$end_date}
OR
duty_end_date BETWEEN {$start_date} AND {$end_date}
)
OR
(
{$start_date} BETWEEN duty_start_date AND duty_end_date
OR
{$end_date} BETWEEN duty_start_date AND duty_end_date)
);
This helped me find the entries with overlapping date ranges.
Hope this helps someone.
You can cover all date overlapping cases even when to-date in database can possibly be null as follows:
SELECT * FROM `tableName` t
WHERE t.`startDate` <= $toDate
AND (t.`endDate` IS NULL OR t.`endDate` >= $startDate);
This will return all records that overlaps with the new start/end dates in anyway.
Mackraken's answer above is better from a performance perspective as it doesn't require several OR's in order to evaluate if two dates overlap. Nice solution!
However I found that in MySQL you need to use DATEDIFF instead of the minus operator -
SELECT o.orderStart, o.orderEnd, s.startDate, s.endDate
, GREATEST(LEAST(orderEnd, endDate) - GREATEST(orderStart, startDate), 0)>0 as overlaps
, DATEDIFF(LEAST(orderEnd, endDate), GREATEST(orderStart, startDate)) as overlap_length
FROM orders o
JOIN dates s USING (customerId)
WHERE 1
AND DATEDIFF(LEAST(orderEnd, endDate),GREATEST(orderStart, startDate)) > 0;

Writing SQL where clause to exclude rows based on two conditions

Hi there I have a database where I would like Select all rows, but excluding records where two conditions are both met. I can't work out how to write the where clause that does this.
To give an example the table is:
Name | Date | Country
I want to select all rows except rows where the Name is Dave and the Date is > 20180101. So rows where the Name is Dave but the Date is <= 20180201 should be included as should rows where the date is > 20180101 and the name is anything but Dave.
This is where De Morgan's Law is helpful. It states !(A && B) can also be expressed !A || !B and vice versa: !A || !B can be !(A && B).
Also that !(A || B) is !A && !B and !A && !B is !(A || B).
For this question you want: NOT ('Dave' AND >'20180101'), which matches the first case above. Applying De Morgan's Law would give you NOT 'Dave' OR <='20180101'.
Use it in an SQL query like this:
SELECT *
FROM [table]
WHERE [Name] <> 'Dave' OR [Date] <= '20180101'
select *
from table
where name <> 'Dave' OR date <= '2018-01-01'
get all the rows that the name is not dave or the date is not X. if both conditions are fail (both dave and X) then that row won't be returned.

Equality check for two boolean expressions (TSQL)

I have two tables and I want to join two tables based on the sign of the related columns
I'm joining on the condition a<0 = b<0 but the equality sign gives me a syntax error. I don't want to do (a<0 AND b<0) OR (Not a<0 AND Not b<0) because it doesn't look clean
You know what the answer is:
where (a < 0 and b < 0) or (a >= 0 and b >= 0)
SQL Server doesn't treat boolean values as bona fide values, so you cannot treat them as regular values in other types of expressions.
You can express this using bitwise or ('^') if you really want:
where (case when a < 0 and b < 0 then 1 else 0 end) ^ (case when a >= 0 and b >= 0 then 1 else 0 end) = 1
However, I find that rather inscrutable.
As Arvo suggested, you can use Sign(). Assuming that a and b are integer types:
where Sign( Sign( a ) + 1 ) = Sign( Sign( b ) + 1 )
Explanation: The inner Sign() calls convert the input values to -1, 0 or 1. Adding 1 shifts those values to 0, 1 or 2. The outer Sign() calls collapse that back to 0 or 1 representing negative and non-negative inputs.
This kind of code is occassionally useful, but should be accompanied by a comment explaining the intent. If the technique is truly impenetrable then it should be explained in the comment or a citation provided.

SQL plan compilation and truth tables

If I have NOT ( 1 <> 1 AND NULL <> 1 )
I can see SQL turning this into in the execution plan XML: ( 1 = 1 OR NULL = 1)
If you would literally evaluate the former expression, the True AND Null would be Null and would eliminate the row. However, the compiled expression can return a row due to the OR.
Can I assume that this type of compilation is guaranteed to always happen? SQL Server would never attempt to bring the convoluted logic forward into the compiled plan? Is there some documentation on this?
This article was pretty helpful, but I am just missing a piece of the puzzle:
https://www.simple-talk.com/sql/learn-sql-server/sql-and-the-snare-of-three-valued-logic/
Here is a SQL example
SELECT 1
FROM T T
LEFT JOIN T2 T2 --t2 has zero rows
ON T.id = t2.t_id
WHERE NOT ( T.id <> 99 AND T2.id <> 99 )
From my experience with SQL, I know that under normal circumstances (without short circuit evaluation) T2.id <> 99 effectively turns the left join into an inner join. That was the behavior I was initially expecting. I was surprised when this filter actually worked.
TL;DR The "compiled result" is not a helpful concept. What matters is the "specified result"--specified by the language definition. A DBMS must make the statement act the way you wrote it.
The truth [sic] table for AND in your link is wrong. AND with False is always False and OR with True is always True in SQL.
Comparisons in SQL return True, False or Unknown. Unknown can arise from a comparison to NULL or a 3VL logic connective (AND/OR/NOT etc) on Unknown. "NULL" is not a literal. True, False & Unknown are values with (assorted) literals in the SQL standard, but not in most DBMSs. (And Unknown can be returned as NULL.) IS is not a comparison; IS NULL and IS NOT NULL are unary 3Vl logic connectives and so are the similar ones named with TRUE, FALSE & UNKNOWN. They always return True or False.
True AND Null would be Null and would eliminate the row. However, the
compiled expression can return a row due to the OR.
No. The truth [sic] table for AND in your link is wrong. AND with False is always False and OR with True is always True in SQL. So your AND is always False from the NOT of False from the AND of False from 1 <> 1 and your OR is always True from 1 = 1. No matter what the other comparisons return (True, False or Unknown). If you work through these two expressions using the (correct) SQL truth tables), they both always give the same result, True.
One has to be very careful about rewriting conditions in SQL. One can interchange NOT (E1 *comparison* E2) by E1 *NOT-comparison* E2 or NOT (E IS ?) and E IS NOT ?. One can safely rewrite an expression using standard logic identities/rules if no value ever IS NULL. One can also safely apply rewrite rules to
(E1 *comparison* E2)
AND E1 IS NOT NULL AND E2 IS NOT NULL
Also beware that you must properly use an Unknown final result, which includes not matching for a WHERE but not failing for a constraint.
SELECT 1
FROM T T
LEFT JOIN T2 T2 --t2 has zero rows
ON T.id = t2.t_id
WHERE NOT ( T.id <> 99 AND T2.id <> 99 )
LEFT JOIN returns the rows of INNER JOIN plus unmatched rows of T extended by T2 columns NULL. (With T2 empty, the INNER JOIN is empty and all rows of T are unmatched.) All the extended rows have T2.id <> 99 Unknown since T2.id is NULL. For T.id = 99 the AND is False and the NOT is True; the WHERE returns all rows. For T1.id any other integer or NULL, the AND will be Unknown, the NOT will be Unknown; the WHERE returns no rows.
(There is no "short ciruit" evaluation of conditions in SQL. Every argument of a connective must be defined.)
If you would literally evaluate the former expression, the True AND Null would be Null and would eliminate the row.
No. You are evaluating the expression. NOT ( 1 <> 1 AND NULL <> 1 ) is NOT (FALSE AND UNKNOWN) is NOT FALSE is TRUE.
( 1 = 1 OR NULL = 1) is TRUE OR UNKNOWN is TRUE. They are both equivalent.
NOT ( 1 <> 1 AND NULL <> 1 ) can be rewritten as NOT ((NOT (1=1)) AND (NOT (NULL = 1))). In regular two value logic, by De Morgan's Laws that can be rewritten as NOT (NOT ((1 = 1) OR (NULL = 1))) and then (1=1) OR (NULL = 1). As it turns out De Morgan's Laws also hold in the three value logic of SQL. This can be demonstrated by creating exhaustive truth tables for the two laws.
The truth table showing that one of De Morgan's Laws, (NOT A) OR (NOT B) is equivalent to NOT (A AND B), holds in SQL's three value logic:
A B | (NOT A) OR (NOT B) | equiv? | NOT (A AND B)
========================================================
T T | F T F F T | T | F T T T
T F | F T T T F | T | T T F F
T U | F T U U U | T | U T U U
-------------------------------------------------------
F T | T F T F T | T | T F F T
F F | T F T T F | T | T F F F
F U | T F T U U | T | T F F U
-------------------------------------------------------
U T | U U U F T | T | U U U T
U F | U U T T F | T | T U F F
U U | U U U U U | T | U U U U
The other law, (NOT A) AND (NOT B) is equivalent to NOT (A OR B) can similarly be demonstrated.
Can I assume that this type of compilation is guaranteed to always happen?
No, specific compilations are never (hardly ever) guaranteed. Barring bugs in SQL Server, the query plans chosen, the transformations applied, will return the results specified by a query.
Edited to add: Let T.id be 99 and T2.id be NULL. Then:
WHERE NOT ( T.id <> 99 AND T2.id <> 99 )
WHERE NOT (99 <> 99 AND NULL <> 99)
WHERE NOT (FALSE AND UNKNOWN)
WHERE NOT (FALSE)
WHERE TRUE

finding range by comparing two tables

I have a table in database as "EXPERIENCE RANGE" with rows as (I can also edit this table according to my need)
0
0.5
1
2
3
5
10
20
I have total experience as integer. I need to display the range in which it lies.
Example - for experience of 8, Range will be 5 - 10
I need to write a sql query. Any ideas will be quite helpful as I am new to SQL.
I cannot hard code it..need to take values from tables only.
Assuming that you are using Oracle, the following query works fine with your existing table:
SELECT
( SELECT MAX( value ) FROM experience_range WHERE value <= :search_value ) AS range_start,
( SELECT MIN( value ) FROM experience_range WHERE value > :search_value ) AS range_end
FROM dual;
No need to hardcode the values, and no need to store the lower and upper bounds redundantly.
you can do it with CASE Expression, the syntax is:
SELECT
CASE
WHEN experience >= 0 and experience <= 4 THEN '0-4'
WHEN experience >= 5 and experience <= 10 THEN '5-10'
.....
ELSE 'No Range'
END as Range
FROM Table_Name
If you do need to store the ranges in a table, I would personally suggest altering the structure of the range table (Assuming you are able to), maybe something like:
|--------------------------------------|
|ID|DESCRIPTION|LOWER_LIMIT|UPPER_LIMIT|
|1 | 0 - 0.5 | 0 | 0.5 |
|2 | 0.5 - 1 | 0.5 | 1 |
...
Then you could get your range by running something like:
SELECT DESCRIPTION FROM [RANGES] WHERE <VALUE> >= LOWER_LIMIT AND <VALUE> < UPPER_LIMIT
EDIT - Mikhail's answer also works, defining the ranges within the query itself is also an option and probably simpler providing you don't need to get these ranges from several reports. (That would require editing every report/query individually)
EDIT 2 - I see you are not able to hardcode the ranges, in which case the above would be best. Can I ask why you are unable to hardcode them?