Eloquent / DB get months between two dates - sql

I'm using Laravel 5.4.
I have a Booking model which contains a start_date and an end_date. The user chooses a month from a dropdown list.
I would like Eloquent to return all records where the month is between the boundaries of the start_date and end_date.
e.g Show all Bookings that are in September (even if the booking started before (or during September) AND (boolean) ends in September or later.
Any suggestion would be welcome. Happy for this to be a raw SQL statement if needs be.

This is what worked for me:
$start = Carbon::now()->addMonth(-1);
$end = Carbon::now()->addMonth();
$bookings = Booking::whereRaw('MONTH(created_at) > :0 AND MONTH(created_at) < :1',[
$start->month, $end->month
])
->orWhereRaw('MONTH(created_at) = :3 AND DAYOFMONTH(created_at) >= :4', [
$start->month, $start->day
])
->orWhereRaw('MONTH(created_at) = :5 AND DAYOFMONTH(created_at) <= :6', [
$end->month, $end->day
])
->get();
If you want to exclude the start and end dates you can replace >= with > and <= with <.
And ofcourse, you have to change the $start and $end dates. They should be Carbon\Carbon objects.

First, on client side or server side parse the users picked month to something like:
$selected = "2017-09";
Then:
Booking::where([
['start_time','>', $selected],
['end_time','<', $selected]
])->get()

This seems to work:
$bookings = DB::table('bookings')
->whereMonth('start_date', '<', $date->month)->whereMonth('end_date', '>=', $date->month)
->orWhere(function($query) use ($date) {
$query->whereMonth('start_date', $date->month);
})->get();

Should be pretty straightforward.
I'm not sure what format the "month" is in but you should use DateTime::createFromFormat() to get your DateTime object. Feel free to use Carbon if you are familiar. I find for simple things like this, it's adding unnecessary complexity.
Then query using it.
Booking::whereBetween($dateTime->format('Y-m-00'), [DB::raw('start_date'), DB::raw('end_date')])->get();

Adding some explain :
Booking::whereRaw("(start_date, end_date) overlaps (date '2021-06-01', date '2021-06-30')")->get();

Related

Date automatically where clause - SQL

I have on my DB the dates that I can filter like this:
select *
where
a.y=2021 and a.m=2 and a.d=7
However if I run this query tomorrow I'll have to go there and change manually.
Is there a way to do this automatically as in if I run the query tomorrow I'll get d=8 and the day after d=9 and so on?
I tried to use get date but I get the following error:
SQL Error [6]: Query failed (#20210207_153809_06316_2g4as): line 2:7: Function 'getdate' not registered
I also don't know if that is the right solution. Does anybody know how to fix that?
you can use NOW to get the current date, and use YEAR , MONTH , DAY to get parts of the date
SELECT *
FROM [TABLE]
WHERE a.y=YEAR(NOW()) and a.m=MONTH(NOW()) and a.d=DAY(NOW())
The best solution is to have a date column in your data. Then you can just use:
where datecol = current_date
Or whatever your particular database uses for the current date.
Absent that, you have to split the current date into parts. In Standard SQL, this looks like:
where y = extract(year from current_date) and
m = extract(month from current_date) and
d = extract(day from current_date)
That said, date functions notoriously vary among databases, so the exact syntax depends on your database.
For instance, a common way to write this in SQL Server would be:
where y = year(getdate()) and
m = month(getdate()) and
d = day(getdate())

Closest day in array sql

I'm trying to find a record and order it by the closets day based on the current day.
Let me try to illustrate with an example.
Say that John wants to find out when he has to teach next. John teaches the following days (days are converted to numbers, where 0 = Sunday, 1 = Monday ...) [1,2]. The current day is Friday (5) and so the result should be 1.
Another example:
Karen wants to figure out when she has to teach next. Karen teaches the following days [0,2,3]. The current day is Thursday (4) and so the result should be 0.
Current query:
TeamOverview.where(coach: current_user.id).order(:day = [next closets day missing here])
Model:
t.string :name
t.integer :coach
t.int :day
Possible records in model:
id: 1, name: John, day: 1
id: 2, name: John, day: 2
id: 3, name: Karen, day: 0
id: 4, name: Karen, day: 2
id: 5, name: Karen, day: 3
To avoid a complex solution I'd rather select all working days and do the work in ruby. Max number of results from query is 7 - that should be fine. Additionally changes are high that you already have such a query (maybe scoped on TeamOverview), you can reuse this and on a second call it may also come from query cache.
working_days = TeamOverview.where(coach: current_user.id).pluck(:day)
working_days.find { |day_num| day_num > today_num } || working_days.first
For getting the next day sorted top you will need to order by distance from current day.
Unfortunately, the %(modulo operator with postgresql keeps the sign
for negative values. Thus, you can not use day - current_day.
But the ordering on following expression will sort the rows according to your intened logic:
(7 + day - current_day) % 7
WHere day is the day form the model row and current_dayis the value for the current day to use with the calculation.
If you prefer a more explicit solution you might turn to
CASE WHEN day < current_day THEN 7 + day - current_day ELSE day - current_day END
(res =TeamOverview.where(coach: current_user.id).order("day").find(:first, :conditions => [ "(day > ? )",current_day]) ) ? res : TeamOverview.where(coach: current_user.id).order("day").find(:first, :conditions => [ "(day < ? )",current_day])
This will give you desired result
You can achieve this in pure SQL with a two-part ordering:
SELECT *
FROM team_overviews
ORDER BY day < (current_day), day
A simple implementation in ActiveRecord for this is:
TeamOverview
.where(coach: current_user.id)
.order("day < #{current_day}")
.order(:day)
Or, if you prefer:
TeamOverview
.where(coach: current_user.id)
.order("day < #{current_day}, day")
Warning: The above code is potentially vulnerable to SQL injection. Be sure that you trust the source of current_day, and sanitise (in this case, probably just call to_i) if necessary.
Unfortunately, looking at the rails source code (as of the current 4.2.6 version at time of posting), it looks as though the ? syntax is not supported by the order ActiveRecord method - i.e. the following is invalid: TeamOverview.order("day < ?", current_day). Perhaps that would be a good addition to the library.
Following code will find next upcoming day, if you need days then remove .first argument
next_day = TeamOverview.where("day > ? and coach_id = ?", current_day, current_user.id).order("day").first

Sql where t.date attribute is less than current year

In my Rails 3 app, I'm attempting to do a find of current students by their school's name and their graduation date in relation to the current year. I can do a successful find for users without a graduation date (see below), but I want to search users who have a graduation date attribute less than - or greater than - the current year. FYI, I'm using PostgreSQL.
The fields I'm using are set up as follows:
t.string :high_school
t.date :hs_grad_year
Here's the find I have working currently:
<%= pluralize(Profile.where(:high_school => "#{#highschool.name}").where("hs_grad_year IS NOT NULL").count, "person") %>
There's a couple issues with your code (getting records from your database should be done in the controller, not in the view, and you're using high school names as foreign keys instead of an id field, for example), but to answer your question:
Profile.where %'
high_school = ? AND
EXTRACT(year FROM hs_grad_year) < EXTRACT(year FROM current_date)
',
#highschool.name
You want a .where("hs_grad_year < extract(year from current_date)") in there.
In Postgres, current_date is the current date (yyyy-mm-dd), and the extract function pulls just one part of that date out of it. You can read more about date and time functions in Postgres here.
Profile.
where(:high_school => #highschool.name).
where("hs_grad_year < ? OR hs_grad_year > ?", Date.today.year, Date.today.year)

using django orm to realize my filter

My raw sql statement is as follows:
select* from t
where dateadd(ss,closedate-datediff(ss,getdate(),getutcdate()),'1970-1-1')
between convert (datetime,'2011-7-1 00:00:00') and convert(datetime,'2011-7-31 23:59:59')
closedate is one of my column in table t ,it means:seconds from 1970-1-1.
HOW can i realize this query using Django ORM?,here involved dateadd,datediff(sql server 2005) APIs,
I dont know how to handler this prob.
Are you trying to do a date range query ? Something like, get all records which fall between 2 dates?
If yes, then something like the following would give you the records in the last 30days
today = date.today() + timedelta(days=1)
n_days_ago = today + timedelta(days=-30)
trans = Transactions.objects.filter(payment_received_date__range=[n_days_ago ,today])

select * from table where datetime in month (without breaking index)

I have a bunch of timestamped rows (using the 'datetime' data type)
I want to select all the rows that have a timestamp that is within a particular month.
The column is indexed so I can't do MONTH(timestamp) = 3 because that'll make this index unuseable.
If I have year and month variables (in perl), is there a horrific bit of SQL I can use like:
timestamp BETWEEN DATE($year, $month, 0) AND DATE($year, $month, 31);
But nicer, and actually works?
I would actually go with the idea you proposed ; maybe with a small difference :
select *
from your_table
where date_field >= '2010-01-01'
and date_field < '2010-02-01'
(Of course, up to you the use $year and $month properly)
Note the < '2010-02-01' part : you might have to consider this, if you have dates that include the time.
For instance, if you have a line with a date like '2010-01-31 12:53:12', you probably want to have that line selected -- and, by default, '2010-01-31' means '2010-01-31 00:00:00'.
Maybe that doesn't look 'nice' to the eye ; but it'll work ; and use the index... It's the kind of solution I generaly use when I have that kind of problem.
This is substantively Pascal MARTIN's answer, but avoids having to know explicitly what the next year/month is (so you don't have to increment year and wrap around the $month, when $month == 12):
my $sth = $mysql_dbh->prepare(<<__EOSQL);
SELECT ...
FROM tbl
WHERE ts >= ? AND ts < (? + INTERVAL 1 MONTH)
__EOSQL
my $yyyymm = $year . '-' . sprintf('%02d', $month);
$sth->execute($yyyymm, $yyyymm);
For bonus fugly points, you could also do this:
... WHERE ts BETWEEN ? AND (? + INTERVAL 1 MONTH - INTERVAL 1 SECOND)
That - INTERVAL 1 SECOND will coerce the upper boundary from a DATE into a DATETIME/TIMESTAMP type set to the last second of a day, which is, as Pascal indicated, what you want on the upper bound.
If you need the same month of every year the index you have will not help you and no amount of SQL syntax trickery will help you
On the other hand if you need a month of a particular year then any query with date ranges should do it
Another alternative is to add an extra column to the table that stores the month, precomputed. That would just be a simple int column, and is trivial to index. Unless you're dealing with a kajillion rows, the extra space for an unsigned tiny int is neglible (one byte + db overhead per row).
It'd require a bit of extra work to keep synched with the timestamp column, but that's what triggers are for.
How about WHERE MONTH(`date`) = '$month' AND YEAR(`date`) = '$year'