Create a timestamp range between two different columns - sql

I am trying to create a query and in the where clause I want to have a timestamp range between two different timestamp columns(starttime & endtime). I tried this but it fetched 0 results:
select *
from db.db_users
where ('<user_start_date>' >= starttime and '<user_end_date>' <= endtime)
and username ilike '%thodoris%'
limit 5;
user_start_date & user_end_date supposed to be user's values and different between them:
e.g.(2020-07-10 00:00:00 & 2020-07-10 23:59:59).
I want to ask if there is any other way to do this.

First, you should be using parameters. Second, the logic for a complete overlap is:
where start_date < :user_start_date and
end_date > :user_end_date
And for a partial overlap:
where start_date < :user_send_date and
end_date > :user_start_date

Related

Postgresql query between time range

Let's say i have a table with below data
id shifttime name
-----+-----------------------+---------------------
1 | 18:50:00+05:30 | a
Here i want to get the row based on time range, the problem here is that if the end range goes past 24 hrs, i am unable to fetch the row. can any one help me out with the query.
SELECT * FROM TABLE WHERE shifttime>='17:20:00+05:30' AND shifttime<='01:20:00+05:30'
You can do the comparison as:
where (:start < :end and shifttime >= :start AND shifttime <= :end) or
(:end < :start and not (shifttime >= :start AND shifttime <= :end) )
Basically, you need to compare the starting and ending values to determine if you are looking between the values or outside those values. Note: I'm not sure if both end points should be included or not, but that is easily adjusted.

oracle query to extract all the dates within given periods and range

Consider if there is a table with two column
TABLE TIME_FRAME
------------------------
|FROM |TO |
|2013-12-13 |2014-01-06|
|2011-12-05 |2011-12-31|
|2014-01-23 |2014-02-22|
|2011-11-21 |2011-12-17|
........
FROM and TO from each row defines a period of time. Also there can be overlap between the periods (here row 2 and row 4) or cover multiple periods
if give a start_date and end_date as parameters here the requirement is about return all the dates falls within the parameters and also within any of the periods in the columns
for example
if start_date is 2013-12-25 and end_date is 2014-02-10
so from above data it should return all dates between
`2013-12-25` and `2014-01-06`
plus
`2014-01-23` and `2014-02-10`
is it possible to create a query for above requirement (not by PL/SQL)?
It's possible by creating set of days using LEVEL recursion operator and then filtering this set by comparing it to your table data. Here is the working Oracle SQL query for you:
select day
from (select to_date('25-DEC-2013', 'dd-mon-yyyy') - 1 + level day
from dual
connect by level <= to_date('10-FEB-2014', 'dd-mon-yyyy') -
to_date('25-DEC-2013', 'dd-mon-yyyy') + 1)
where exists
(select 1 from TIME_FRAME p where day between p.FROM and p.TO);
Hope this helps!

Postgres SQL script with loop

I need to write a Postgres script to loop over all the rows I have in a particular table, get the date column in that row, find all rows in a different table that have that date and assign that row the ID of the first row in its foreign key column. Explaining further, my data (simplified) looks like:
first_table:
id INT, run_date DATE
second table:
id INT, target_date TIMESTAMP, first_table_id INT
pseudocode:
first_table_array = SELECT * FROM first_table;
i, val = range first_table_array {
UPDATE second_table SET first_table_id={val.id} WHERE date={val.run_date}
}
But it's not actually that simple, because I'm comparing a date against a timestamp (which I presume will cause some problems?), and I need to refine the time period as well.
So instead of doing where date={val.run_date} I actually need to check whether the date is run_date starting at 4am, and going into 4am the following day.
So say run_date is 01/01/2015, I need to convert that value into a timestamp of 01/01/2015 04:00:00, and then create another timestamp of 01/02/2015 04:00:00, and do:
where date > first_date AND date < second_date.
So, something like this:
pseudocode:
first_table_array = SELECT * FROM first_table;
i, val = range first_table_array {
first_date = timestamp(val.run_date) + 4 hrs
second_date = timestamp(val.run_date + 1 day) + 4 hrs
UPDATE second_table SET first_table_id={val.id} WHERE date > first_date AND date < second_date
}
Is postgres capable of this sort of thing, or am I better off writing a script to do this? Do I need to use loops, or could I accomplish this stuff just using joins and standard SQL queries?
I did the best I could. A data sample would be great.
UPDATE second_table ST
SET ST.id = FT.id
FROM first_table FT
WHERE ST.date BETWEEN FT.run_date + interval '4 hour'
AND FT.run_date + interval '4 hour' + interval '1 day'
How to do an update + join in PostgreSQL?
http://www.postgresql.org/docs/9.1/static/functions-datetime.html
NOTE:
BETWEEN is equivalent to use <= and >=

SQL, Selecting between date/times

This is for (MS SQL Server 2008) It allows the user to schedule other users for a start and end date/time without overlaps.
I'm addressing the business rule of not being able to overlap individuals during a schedule. E.G, If user 1 is scheduled from 1/1/2012 to 1/4/2012, user 2 should not be able to submit a request for 1/2/2012 to 1/4/2012. This part is already taken care of, and here is my SQL.
SELECT * FROM fooTable
WHERE
Prim = 1
AND
Group = 10
AND
(('2012-06-01 08:01' between startdate and enddate
OR '2012-06-03 08:01' between startdate and enddate)
OR
('2012-06-01 08:01' < startdate) AND ('2012-06-03 8:01' > enddate))
I now have a requirement to allow a schedule to overlap in the sense that the new shift can begin as the last one ends. E.G, My end date is 1/1/2012 at 8:00pm - I should be able to schedule someone to start at 1/1/2012 and 8:00pm.
Need some help thinking this one through.
First off, don't use Group as a column name. It is a reserved word in every SQL standard. I renamed it to grp for the purpose of my answer.
Trying to schedule a new shift from '2012-06-01 08:00' to '2012-06-03 08:00' ...
INSERT INTO tbl (prim, grp, startdate, enddate)
SELECT 1, 10, '2012-06-01 08:00', '2012-06-03 08:00'
WHERE NOT EXISTS (
SELECT *
FROM tbl
WHERE prim = 1
AND grp = 10
AND '2012-06-03 08:00' > startdate -- not >= to allow sharing a border
AND '2012-06-01 08:00' < enddate -- and not BETWEEN ... AND either
)
Note that I compare:
new_end &gt old_start
new_start &lt old_end
If you use BETWEEN .. AND .. you include the borders of a shift in your test. It's the same as using >= and <=. You need to use > and < to allow borders to overlap.
Well, and try my largely simplified syntax. Not sure about what you had there originally.
Here is a working demo on sqlfiddle.com to play with.

Getting results between two dates in PostgreSQL

I have the following table:
+-----------+-----------+------------+----------+
| id | user_id | start_date | end_date |
| (integer) | (integer) | (date) | (date) |
+-----------+-----------+------------+----------+
Fields start_date and end_date are holding date values like YYYY-MM-DD.
An entry from this table can look like this: (1, 120, 2012-04-09, 2012-04-13).
I have to write a query that can fetch all the results matching a certain period.
The problem is that if I want to fetch results from 2012-01-01 to 2012-04-12, I get 0 results even though there is an entry with start_date = "2012-04-09" and end_date = "2012-04-13".
SELECT *
FROM mytable
WHERE (start_date, end_date) OVERLAPS ('2012-01-01'::DATE, '2012-04-12'::DATE);
Datetime functions is the relevant section in the docs.
Assuming you want all "overlapping" time periods, i.e. all that have at least one day in common.
Try to envision time periods on a straight time line and move them around before your eyes and you will see the necessary conditions.
SELECT *
FROM tbl
WHERE start_date <= '2012-04-12'::date
AND end_date >= '2012-01-01'::date;
This is sometimes faster for me than OVERLAPS - which is the other good way to do it (as #Marco already provided).
Note the subtle difference. The manual:
OVERLAPS automatically takes the earlier value of the pair as the
start. 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. This means for instance
that two time periods with only an endpoint in common do not overlap.
Bold emphasis mine.
Performance
For big tables the right index can help performance (a lot).
CREATE INDEX tbl_date_inverse_idx ON tbl(start_date, end_date DESC);
Possibly with another (leading) index column if you have additional selective conditions.
Note the inverse order of the two columns. See:
Optimizing queries on a range of timestamps (two columns)
just had the same question, and answered this way, if this could help.
select *
from table
where start_date between '2012-01-01' and '2012-04-13'
or end_date between '2012-01-01' and '2012-04-13'
To have a query working in any locale settings, consider formatting the date yourself:
SELECT *
FROM testbed
WHERE start_date >= to_date('2012-01-01','YYYY-MM-DD')
AND end_date <= to_date('2012-04-13','YYYY-MM-DD');
Looking at the dates for which it doesn't work -- those where the day is less than or equal to 12 -- I'm wondering whether it's parsing the dates as being in YYYY-DD-MM format?
You have to use the date part fetching method:
SELECT * FROM testbed WHERE start_date ::date >= to_date('2012-09-08' ,'YYYY-MM-DD') and date::date <= to_date('2012-10-09' ,'YYYY-MM-DD')
No offense but to check for performance of sql I executed some of the above mentioned solutiona pgsql.
Let me share you Statistics of top 3 solution approaches that I come across.
1) Took : 1.58 MS Avg
2) Took : 2.87 MS Avg
3) Took : 3.95 MS Avg
Now try this :
SELECT * FROM table WHERE DATE_TRUNC('day', date ) >= Start Date AND DATE_TRUNC('day', date ) <= End Date
Now this solution took : 1.61 Avg.
And best solution is 1st that suggested by marco-mariani
SELECT *
FROM ecs_table
WHERE (start_date, end_date) OVERLAPS ('2012-01-01'::DATE, '2012-04-12'::DATE + interval '1');
Let's try range data type.
--sample data.
begin;
create temp table tbl(id serial, user_id integer, start_date date, end_date date);
insert into tbl(user_id, start_date, end_date) values(1, '2012-04-09', '2012-04-13');
insert into tbl(user_id, start_date, end_date) values(1, '2012-01-09', '2012-04-12');
insert into tbl(user_id, start_date, end_date) values(1, '2012-02-09', '2012-04-10');
insert into tbl(user_id, start_date, end_date) values(1, '2012-04-09', '2012-04-10');
commit;
add a new daterange column.
begin;
alter table tbl add column tbl_period daterange ;
update tbl set tbl_period = daterange(start_date,end_date);
commit;
--now test time.
select * from tbl
where tbl_period && daterange('2012-04-10' ::date, '2012-04-12'::date);
returns:
id | user_id | start_date | end_date | tbl_period
----+---------+------------+------------+-------------------------
1 | 1 | 2012-04-09 | 2012-04-13 | [2012-04-09,2012-04-13)
2 | 1 | 2012-01-09 | 2012-04-12 | [2012-01-09,2012-04-12)
further reference: https://www.postgresql.org/docs/current/functions-range.html#RANGE-OPERATORS-TABLE