Django ORM: filter by hour range - sql

I'm trying to implement a filter for hour range, it should returns records with a date between hourA and hourB (ie: "give me the records saved between 16pm and 18pm").
My attempts:
1) Using new 1.6 __hour filter and __in or __range:
MyModel.objects.filter(date__hour__in=(16, 17, 18))
MyModel.objects.filter(date__hour__range=(16, 18))
The code above generates exceptions
2) Using Q objects:
hList = [Q(date__hour=h) for h in (16, 17, 18)]
MyModel.objects.filter(reduce(operator.or_, hList))
This version works, but is very inefficient, since for each hour in the range it repeats the extract() call by generating something like:
where
extract(hour from date) = 16 or
extract(hour from date) = 17 or
extract(hour from date) = 18
when instead the right raw SQL should be:
where extract(hour from date) in (16, 17, 18)
…how can I filter by hour range in an effective manner, without relying on raw sql?

I managed to solve the issue in this way:
all_points = MyModel.objects.all()
all_points.extra(where=['extract(hour from MyDateField) in (16, 17, 18)'])

In Django 1.9+ you can chain hour lookups, so the examples from the question will work:
MyModel.objects.filter(date__hour__in=(16, 17, 18))
MyModel.objects.filter(date__hour__range=(16, 18))

Related

SQL solution to extract total number of hours from a string column

I have a varchar(255) column in table1:
time
--------------------------
Monday|10:00-18:00
Tuesday|10:00-16:00
Friday|10:00-20:00
How do I extract the number of hours from the above column using SQLite? I tried using varchar() and datetime() none of which worked. Should I be using regex to get the time?
Thank you in advance.
You can extract the time values using:
select substr(time, -11, 5), substr(time, -5)
You can then use julianday() to get the difference in seconds (or any other unit):
select round((julianday(substr(time, -5)) - julianday(substr(time, -11, 5))) * 60 * 60 * 24) as seconds_diff
from t;
Here is a db<>fiddle.

Change month and day only in BigQuery

Is there an equivalent to DATEFROMPARTSin BigQuery? I'm trying to change only the month and day in my timestamp, not the year.
Here's my table in DATETIME:
BirthYear
2014-12-12T00:00:00
2015-01-07T00:00:00
I want to change only the month and day but keep the year. For example change the bottom row to: 2015-04-01T00:00:00
The following query works in MS SQL and I'm trying to rewrite it in BigQuery:
UPDATE `table` SET BirthYear = DATEFROMPARTS(BirthYear, 04, 01) WHERE BirthYear IS NULL
BigQuery equivalent of datefromparts(year(birthdate), 4, 1) is
date(extract(year from BirthYear), 4, 1)
also, if you need it to "convert" back to datetime you might want to use as below
datetime(date(extract(year from BirthYear), 4, 1))
For the following SQL Server expression:
datefromparts(year(birthdate), 4, 1)
In BigQuery, you could do this with datetime_trunc() and datetime_add():
datetime_add(datetime_trunc(birthdate, year), interval 4 month)
This gives you a datetime value. You can use date_trunc() and date_add() if you want to handle dates instead.

Format date information from string with SQL

Is there a way to convert a string like this '160806CD01' into a date like this '2016-08-06 00:00:00' with SQL where the year, month, and date are 16, 8, and 6 respectively?
The principle is to first extract the part of the string that contains the date, then use a conversion function to turn the string portion it into a date datatype.
The functions to use do vary depending on the RDBMS ; here are some examples :
Oracle :
TO_DATE(SUBSTR(col, 1, 6), 'YYMMDD')
MySQL/MariaDB :
STR_TO_DATE(SUBSTR(col, 1, 6), '%y%m%d')
SqlServer :
CAST(CONCAT('20', SUBSTRING(col, 1, 6)) as datetime)
Postgres :
TO_DATE(SUBSTRING(col, 1, 6), 'YYMMDD')

Calculations on dynamic date variable

Im using Oracle SQL platform and Im using a variable #Quarter to prompt for a Quarter and related data that I want to see. This variable is based on a column that has a format "YYYY QQ" (for example "2018 Q2").
Now Its really easy to pass that variable but how can I add a time to this variable to get a next or previous quarter?
I tried: TIMESTAMPADD(SQL_TSI_QUARTER, -1, #QUARTER')
and the sql issued looked like:
(TIMESTAMPADD(SQL_TSI_QUARTER, -1, '2017 Q 4')
unfortunately my variable doesnt have appropriate format, its just "2017 Q 4" so not a timestamp...
I also tried: TIMESTAMPADD(SQL_TSI_QUARTER, -1, CAST('#QUARTER' AS DATE))
and the sql issued looked:
(TIMESTAMPADD(SQL_TSI_QUARTER, -1, CAST('2017 Q 4' AS DATE)))
like it didnt even recognize the function CAST inside...
Is there anything I can do to make a calculations on that variable?
Many Thanks,
Alex
You can cut up your input with substr and get it into a date format within the Quarter's range. Then you'll just need to add quarters (shown as quarters * 3 months: -1 * 3 and 1 * 3 in my example) to get those before/after.
with x as (select '2017 Q4' as input from dual)
select input,
to_date(substr(input,1,4) || '-' || substr(input,7,1) * 3, 'yyyy-mm') as in_date_format,
to_char(add_months(to_date(substr(input,1,4) || '-' || substr(input,7,1) * 3, 'yyyy-mm'), -1 * 3), 'yyyy "Q"q') as last_quarter,
to_char(add_months(to_date(substr(input,1,4) || '-' || substr(input,7,1) * 3, 'yyyy-mm'), 1 * 3), 'yyyy "Q"q') as next_quarter
from x;
Without seeing your code, this is the best I could do to represent what I am talking about...

SQL: How to get the date yyyy/mm/dd based on the year and day number?

I have the following string 2015089 or 2016075, for example.
I need to get the result in yyyy/mm/dd format based on the given input.
So, based on 2015089, I get, 2015/mm/dd. dd is a 89th day of 2015 and mm is a month that has 89th day.
How can I do something like that?
I think the simplest way is to convert to a date using dateadd():
select dateadd(day, right(str, 3) - 1, datefromparts(left(str, 4) + 0, 1, 1) )
That is, add one less than the number of days to the beginning of the year. This assumes that Jan 1 is represented as "1" and not "0".
You can then format the date however you like.
In pre-SQL Server 2012, you can do:
select dateadd(day, right(str, 3) - 1, cast(left(str, 4) + '0101' as date))