SQL date range query from two columns(start_date and end_date) - sql

I have two columns in my table start_date and end_date and my query inputs two values input_start_date and input_end_date.
I want the data from input_start_date to input_end_date by querying start_date and end_date.
I am able to achieve this by following query:
SELECT * FROM table-name where (
(start_date >= input_start_date and end_date <= input_end_date) or
(start_date <= input_start_date and end_date >= input_end_date) or
(start_date >= input_start_date and start_date <=input_end_date) or
(end_date >= input_start_date and end_date <= input_end_date)
);
Is there any better way of doing it ?

SELECT column-names
FROM table-name
WHERE column-name BETWEEN value1 AND value2
This one is for between one date
SELECT column-names
FROM table-name
WHERE start_date = input_start_date AND end_date = input_end_date

Related

Select record from database, with date "As-On", passed date?

I have a table in database with start_date, end_date and I want to fetch record from database, with date filter, as fetch those records "As on given date".
Eg:
Id start_date end_date
1 1980-01-01 1984-12-31
2 1985-01-01 2009-12-31
3 2010-01-01 2018-12-31
4 2019-01-01 null
5 1940-01-01 null
So now If I pass on date (Desired Output)
**1940-01-01**, it should return #Id=5 (number of records == 1)
**1980-01-02**, it should return #Id=1,5 (number of records == 2)
**1985-01-01**, it should return #Id=1,2,5 (number of records == 3)
**2010-01-01**, it should return #Id=1,2,3,5 (number of records == 4)
**2019-01-01**, it should return #Id=1,2,3,4,5 (number of records == 5)
**2021-01-01**, it should return #Id=1,2,3,4,5 (number of records == 5)
I tried a number of queries, but none seemed to work like
select * from table where (end_date <= '1980-01-02' or end_date is null)
select * from table where '1980-01-01 00:00:00' between start_date and coalesce(end_date, '2999-12-31')
select * from table where (end_date <= '1980-01-01 00:00:00' or end_date is null) and start_date <= '1980-01-01 00:00:00'
Any help will be appriciated.
Use the infinity timestamp literals and between for this:
select *
from "table"
where '1940-01-01' between coalesce(start_date, timestamp '-infinity')
and coalesce(end_date, timestamp 'infinity')
;
The logic that makes sense to me is:
select t.*
from t
where :date >= start_date and
(:date <= end_date or end_date is null);
However, this is not consistent with your stated results. It conforms to my reading of what you want.

How to put Case in Where Statement for Oracle SQL

For the query below, I'm trying to pull a specific date range depending on the current day of the month. If it's the 20th or less (e.g. "2/7/2020") then I want the date range for January. Otherwise, I want the date range for February. Is it possible to be done with a case statement? Or there is a better way?
SELECT
account,
start_date,
amount
FROM
table1
WHERE
CASE
WHEN (
SELECT
CAST(EXTRACT(DAY FROM sysdate) AS NUMBER)
FROM
dual
) <= 20 THEN
start_date
BETWEEN '2020-01-01' AND '2020-01-31'
ELSE start_date BETWEEN '2020-02-01' AND '2020-02-29'
END
You can do this by avoiding the case statement and using truncate the date - 20 to the month, e.g.:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= TRUNC(SYSDATE - 20, 'mm')
AND start_date < add_months(TRUNC(dt - 20, 'mm'), 1);
If you really had to use a CASE expression (you can't use a CASE statement in SQL), you would need to do something like:
SELECT account,
start_date,
amount
FROM table1
WHERE start_date >= CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN add_months(TRUNC(SYSDATE, 'mm'), -1) ELSE TRUNC(SYSDATE, 'mm') END
AND start_date < CASE WHEN to_char(SYSDATE, 'dd') <= '20' THEN TRUNC(SYSDATE, 'mm') ELSE add_months(TRUNC(SYSDATE, 'mm'), 1) END;
N.B. if you're using a function, you don't need to wrap it in a select .. from dual, you can use it directly in the SQL statement.
I've also assumed that you want a dynamic range, e.g. if the day of the month is 20 or less, the range is for the previous month, otherwise the current month.
ETA: You would use the above two queries if there is an index on the start_date column, otherwise you could simply do:
SELECT account,
start_date,
amount
FROM table1
WHERE TRUNC(start_date, 'mm') = TRUNC(SYSDATE - 20, 'mm');
Case statements return single values. As such you should pull out the start date and you'll need two case statements.
select account, start_date, amount
from table1 where
start_date between
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-01'
else '2020-02-01'
end) and
(case
when (select cast(extract(day from sysdate) as number) from dual) <= 20 then '2020-01-31'
else '2020-02-29'
end)
One method subtracts 20 days and then gets the month boundary:
where start_date >= trunc(sysdate - interval '20' day, 'MON') and
start_date < trunc(sysdate - interval '20' day, 'MON') + interval '1' month
This approach is index (and partition) friendly -- an appropriate index on start_date can be used. It is also safe if start_date has time components.
Note: You can use sysdate without having to use a subquery.
You can use or operator with last_day function as following:
Select * from your_table
Where (
start_date <= trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate,'mm') - interval '1' month and trunc(sysdate,'mm') - 1
)
Or
(
start_date > trunc(sysdate,'mm') + 20
and start_date between trunc(sysdate, 'mm') and last_day(sysdate)
)
This approach will use index on start_date, if any.
Cheers!!
select account, amount, start_date
from table1
where ( ( (select cast (extract (day from sysdate) as number) from dual) <= 20
and start_date between date '2020-01-01' and date '2020-01-31')
or ( (select cast (extract (day from sysdate) as number) from dual) > 20
and start_date between date '2020-02-01' and date '2020-02-29')
);
Using CASE expressions as BETWEEN operands:
SELECT account
, start_date
, amount
FROM table1
WHERE start_date BETWEEN CASE
WHEN extract(day from sysdate) <= 20
THEN trunc(sysdate -interval '1' month, 'month')
ELSE trunc(sysdate, 'month')
END
AND CASE
WHEN extract(day from sysdate) <= 20
THEN last_day(sysdate -interval '1' month)
ELSE last_day(sysdate)
END

Postgres query to fetch results between two dates

This is example:
create table test (
id int,
name varchar(6),
start_date date,
end_date date
);
insert into test (id,name, start_date, end_date) values (1,'aaa', '2014-07-01', '2014-07-30');
insert into test (id,name, start_date, end_date) values (2,'bbb', '2014-07-01', '2014-08-30');
insert into test (id,name, start_date, end_date) values (3,'ccc', '2014-08-01', '2014-08-30');
insert into test (id,name, start_date, end_date) values (4,'ddd', '2014-08-16', '2014-08-30');
insert into test (id,name, start_date) values (5,'eee', '2014-07-01');
insert into test (id,name, start_date) values (6,'fff', '2014-08-16');
I need write query where result will be:
2;"bbb";"2014-07-01";"2014-08-30"
3;"ccc";"2014-08-01";"2014-08-30"
4;"ddd";"2014-08-16";"2014-08-30"
5;"eee";"2014-07-01";""
6;"fff";"2014-08-16";""
I've written this:
select * from test
where (start_date >= '2014-08-15' and (end_date <= current_Date or end_date is null)) or
(start_date <= '2014-08-15' and end_date is null) ;
But I see only ddd,eee and fff records.
Instead of the compare operators, you can actually use PostgreSQL's own detection of date (& time) overlaping. There are actually 2 constructs:
1) The SQL compatible OVERLAPS operator:
(start1, end1) OVERLAPS (start2, end2)
(start1, length1) OVERLAPS (start2, length2)
This one does not handle well an open-ended date-period, but you can use the the special infinity date for that.
select *
from test
where (date '2014-08-15', current_date) overlaps
(start_date, coalesce(end_date, date 'infinity'));
As far as I know, you can't use any index to speed up the query above.
Also:
Each time period is considered to represent the half-open interval start <= time < end, unless start and end are equal in which case it represents that single time instant.
2) Using the daterange type:
Ranges can be unbounded, and you can explicitly set how to handle range bounds (to include them or not).
select *
from test
where daterange(start_date, end_date, '[]') &&
daterange(date '2014-08-15', current_date, '[]');
Also, you can speed up this query, by applying a gist index on the daterange expression:
create index on test using gist ((daterange(start_date, end_date, '[]')));
SQLFiddle
I am guessing (intelligently) that you want any period that overlaps with 2014-08-15 to the present date. If so, this logic returns what you want:
select *
from test
where (start_date >= '2014-08-15' and (end_date <= current_Date or end_date is null)) or
(start_date <= '2014-08-15' and (end_date > '2014-08-15' or end_date is null));
Here is a SQL Fiddle.

sql query to select from and to date

select PERIOD_NAME
from gl_periods
where :l_date_from between start_date and end_date
and :l_date_to between start_date and end_date
This query is not working.
I need to select from date and to date between those two columns.And should display all period names between those two dates.
Help me with this query.
Try this:
select PERIOD_NAME
from gl_periods
where :l_date_from <= end_date
and :l_date_to >= start_date

Date Range in PL/SQL

If I have table with a Date column (Date field) called created_date, with values like "9/2/2010 5:25:42 PM".
I want to select all rows from a start_date to a end_date. However, the end_date may be null. In this case, I want to select all rows where created_date is greater than end_date.
Since toDate (which can be null) is a host variable, it's easier than the solutions already given (which are all wrong in that regard, btw)
select * from mytable
where created_date between v_fromdate
and nvl(v_todate, to_date('31.12.9999','dd.mm.yyyy'));
select *
from TABLE
where created_date >= '2010-09-02' and (created_date is NULL or created_date <= '2010-09-03')
Why just use a simple SQL query for that, like this one:
select xxx from table_names where created_date is null or (created_date >= to_date("02/09/2010", "dd/mm/yyyy") and created_date <= to_date("03/09/2010", "dd/mm/yyyy"));
Edit
You can define a query like the one defined by ammoQ, i.e. something like that:
select xxx from table_names where created_date is null or created_date >= start_date and created_date <= nvl(end_date, to_date("31/12/9999", "dd/mm/yyyy"));
However, as you are using PL/SQL, you can check the nullability of end_date parameter:
IF end_date IS NULL THEN
select xxx from table_names where created_date is null or created_date >= start_date;
ELSIF
select xxx from table_names where created_date is null or created_date >= start_date and created_date <= end_date;
END IF;
Note that you can remove the created_date is null condition if created_date is not a nullable column...
select * from yourtable
where created_date >= #StartDate AND created_date <=ISNULL(#EndDate,created_date)
SELECT *
FROM A_TABLE
WHERE CREATED_DATE >= &START_DATE AND
(CREATED_DATE <= &END_DATE OR
&END_DATE IS NULL)
If i took it right from your question
this should work:
SELECT *
FROM yourTable
WHERE created_date >= to_date('01.09.2010', 'dd.mm.yyyy')
AND (end_date <= to_date('02.09.2010', 'dd.mm.yyyy')
OR end_date IS NULL);