Considering the model Property which has_many :property_events. PropertyEvent has a start_date and an end_date as a date.
I am interested in querying for vacancies or for all properties that do NOT have PropertyEvent within a selected time period.
I tried the following, but the first PropertyEvent that exists within the time period invalidates all properties from being returned.
#properties.where('NOT EXISTS (:property_event)',
property_event: PropertyEvent.where("property_events.start_date <= ?",end_date).
where("property_events.end_date >= ?",start_date)
)
Any ideas?
I think what you're looking for is:
#properties.where(
"NOT EXISTS(
SELECT 1 FROM property_events pe
WHERE (pe.start_date, pe.end_date) OVERLAPS (?, ?)
)",
start_date,
end_date
)
Related
I am working with a PostgreSQL database of archaeological sites that stores their year of foundation (start_date) and the year where they were abandoned (end_date) as date fields. These years can be Before the Common Era (BCE) or Common Era (CE) and they range between BCE 900 to nowadays, since some of the sites were abandoned in the past, but many of them are still inhabited. Here is a snapshot of the data I am using to give you a better idea of what I am dealing with.
My aim is to build a query to define the sites that were occupied between two specific years (BCE 27 and CE 235) using this dataset. This is a graphic showing this objective, which should result in the selection of Sites A, B, C and D, but not E. For this purpose, I have tried several solutions:
select site_id, name, type, start_date, end_date
from site_date
where start_date between '0027-01-01 BC' and '0235-01-01';
...shows sites starting between these two years, but not those active between them.
select site_id, name, type, start_date, end_date
from site_date
where end_date >= '0235-01-01';
...shows sites ending before CE 235.
...as well as variations of the previous.
I realized that these applications were actually defining the sites whose start and end dates fall within BCE 27 and CE 235, but not those (still active in this period) whose foundation was before the start date and whose abandonment was after the end date. How to solve this issue?
It sounds like what you want is anything that has a start_date <='0235-01-01' AND end_date >='0027-01-01 BC'.
Pseudo code...
Where (start_date <= 27BCE and end_date >= 27BCE)
OR
(start_date between 27BCE and 235CE)
AS per graphic shown, I understood sample data is here, you wanted A, B, C and D but not E, these site were having end date > '0027-01-01 BC'
and these site exists between BCE 27 and CE 235,
select * from site_date
where end_date > '0027-01-01 BC'
Postgres fully understands BCE dates and therefore all date functions work properly with them. Unfortunately some instances do not display them correctly and will display '2021-01-01 BC' and '2021-01-01' exactly the same. You may actually need to use to_char(date_column, 'FMyyyy-mm-dd bc') to get the proper display.
The following converts your date sets (start_date, end_date) and the desired date start, end dates into a daterange then uses the overlaps operator (&&) to determine where any part of the (start_date, end_date) falls within the specified range. (See demo here. It also adds a row with a null end date to show how still inhabited can work.)
with target_range(founded_date,abandoned_date) as
( values (date '0027-01-01 BC', date '0235-01-01') )
select s.*
from sites s
join target_range tr
on ( daterange(s.start_date, s.end_date, '[]') &&
daterange(tr.founded_date,tr.abandoned_date, '[]')
) ;
Additional Ref: make_date function
I am having trouble doing calculations in one table using conditional statements. I have a table 'df' with the following column names:
id - int
time - timestamp
correctness - boolean
subject - text
Every student (id) completes tasks on particular subject (subject). The system assigns "True" value in column "correctness" if the assignment is completed correctly and "False" if not. The time (time) when the student completes the task is also saved by the system.
I need to write an optimal sql query that counts all students who completed 20 tasks successfully within an hour during March 2020.
Thanks in advance!
You can do this with no subqueries using:
select distinct s.id
from t
where t.timestamp >= '2020-03-01' and t.timestamp < '2020-04-01'
group by s.id, date_trunc('hour', timestamp)
having count(*) >= 20;
Note: You may want that the tasks are completed successfully, but that is not actually mentioned in your question.
For performance, you want an index on (timestamp).
You need to look at each 'correct' task and see if there are 20 previous tasks, delivered within one hour, that are correct.
That means you have to inner join task unto itself and then count them.
select distinct on(tasks.id) tasks.id, tasks.time, sum(previous_tasks.id)
from tasks
inner join tasks previous_tasks
on tasks.id = previous_tasks.id
and (previous_tasks.time - tasks.time) < interval '1 hour'
and previous_tasks.correctness
and tasks.time >= '2020-03-01' and tasks.time < '2020-04-01'
and previous_tasks.time >= '2020-03-01' and previous_tasks.time < '2020-04-01'
group by 1, 2
having sum(previous_tasks.id) >= 20
I have a table:
table 1
carId (INT)
Check Out date (DATE)
Checek In date (DATE)
EXPLANATION: This table has a data for a rental car which shows a particular car is checked out on a particular date and checked in on a particular date
I want to find how many cars are on rent on a particular date
OUTPUT
date (DATE)
NO_Of_Cars
Is there a way to solve this problem in python/SQL?
EDIT : I may have posted this question wrongly.
In output, I want date and no of cars for all the dates of a year.
Apologies
select count(distinct carId) NO_Of_Cars
from table1
where checkOut <= particular_date
and (particular_date < checkIn or checkIn is null)
Note that check out and check in are points of custody change. Hence checkOut <= means from and including the moment of check out and < checkIn means up to but not including the moment of check in. If the check in moment was inclusive <= checkIn then the car could, in principle, have 2 custodians for that exact checkIn moment (maybe the customer changed cars half way through the rental period).
Use this code in SQL
SELECT date, NO_Of_Cars FROM table1
WHERE
particular_date <=check_out and
(particular_date >= check_in OR check_in IS NULL);
The date comparison logic is needed over here.
You may refer Date comparison in SQL
Can anyone help me create a query which will populate a list of DJs who are not already booked in.
My user will select a start date (and time), and an end date (and time) - and then click a button to select a DJ.
I only want those DJs which are available between those time slots to appear in the list.
Here are the two tables which are involved
all I need in the listbox is the DJ Number, and the DJ Name
So far I have this... but it isn't working:
SELECT tblDJ.DJ_No AS [DJ ID], tblDJ.DJ_Name AS Name FROM tblDJ
WHERE (((tblDJ.[DJ_No]) Not In
(SELECT tblBooking.[FK_DJ_No]
FROM tblBooking
WHERE ( (tblBooking.End_Date) >= 01-04-2020 19:30:00 )))) ....etc....
I'm just entering a date in here for now, but obviously it will be stored in a variable once implemented.
Thanks
Implementing OVERLAPS of two intervals would look like:
1st_start_date <= 2nd_end_date and 1st_end_date >= 2nd_start_date
where 1st and 2nd values are markers of different events.
You could use that logic in combination with NOT EXISTS to discard those djs that are unavailable at a given time:
select dj_no, dj_name
from tbldj d
where not exists (
select 1
from tblbooking b
where b.fk_dj_no = d.dj_no
and b.start_date <= #END DATE#
and b.end_date >= #START DATE#
)
You just need to replace #START DATE# and #END DATE# with your values.
This does work because there are following assumptions:
Start date of the first event is prior to end date of that event
Start date of the second event is prior to end date of that event
Which seems logical, right?
The date in the SQL needs to be wrapped between two # in order for MS-Access to recognize it as a date:
select *
from tblDJ
where DJ_No not in
(
select FK_DJ_No
from tblBooking
where End_Date >= #2020-04-01 19:30:00#
)
Other than that you query will work.
I have a table called users where I have two columns: name and created_at. created_at column column is of type datetime and it stores the datetime when this user was created.
I need to know the number of users created for a given date range. Let's say I ask give me user report between 1-nov-2010 and 30-nov-2010 . I need something like this
1-nov-2010: 2
2-nov-2010: 5
The problem I am running into is that created_at data has value upto second. How do I check if a created_at date falls within a given date.
Any help in solving this problem is appreciated.
I am using mysql5.
select date_format(created_at, '%e-%b-%Y'), count(*)
from users
where created_at >= '2010-11-01' and created_at < '2010-12-01'
group by date(created_at);
MySQL lets you do lots of date-ish things even with datetimes.
An alternative if computing the day after the end day is troublesome:
where date(created_at) between '2010-11-01' and '2010-11-30'