How can I select data between an hour range without using extract? - postgresql

I'm trying to select data between an hour range, without using extract to be able to use the indexes in the table. I do the query like this:
SELECT *
FROM activities
WHERE "dateParam"::timestamp >= '21:00:00'::timestamp
AND "dateParam"::timestamp <= '23:00:00'::timestamp;
But I get an 22007 state and it doesnt work, how could I be able to do it?

Try:
SELECT *
FROM activities
WHERE "dateParam"::timestamp >= current_date + '21:00:00'::time
AND "dateParam"::timestamp <= current_date + '23:00:00'::time;
A timestamp needs a date portion so add it.

Related

Joining a QUERY function with OR

I currently have a QUERY function which is set up based on a start date cell and an end date cell, formula as below:
=QUERY(Haulage!$A$3:$L$29," Select * Where A >= date """&text('2022 Stats'!S1, "yyyy-mm-dd")&""" AND A <= date """&text('2022 Stats'!T1, "yyyy-mm-dd")&"""")
This is working fine but I would like to adapt it so I can also narrow the query down further with the use of a dropdown. I have the following IF formula for this:
=IF('2022 Stats'!V1="All TOCs",""," AND LOWER(K) = LOWER('"&'2022 Stats'!V1&"') " )
This seems to yield the correct results but I am struggling to get the two to work together......
Link to sheet: https://docs.google.com/spreadsheets/d/1wTWuvFwMTqJ-sjIZbXWpGOS1WKwpODj2R8KAzqlqkuw/edit?usp=sharing
Try:
=QUERY(Haulage!A3:L29,
"where A >= date '"&TEXT('2022 Stats'!S1, "yyyy-mm-dd")&"'
and A <= date '"&TEXT('2022 Stats'!T1, "yyyy-mm-dd")&"'"&
IF('2022 Stats'!V1="All TOCs",,"
and lower(K) = '"&LOWER('2022 Stats'!V1)&"'"))

Recurring future date at every 3 month after create date in PostgreSQL

I am looking for a function in PostgreSQL which help me to generate recurring date after every 90 days from created date
for example: here is a demo table of mine.
id date name
1 "2020-09-08" "abc"
2 "2020-09-08" "xyz"
3 "2020-09-08" "def"
I need furure date like 2020-12-08, 2021-03-08, 2021-06-08, and so on
First it's important to note that, if you happen to have a date represented as text, then you can convert it to a date via:
SELECT TO_DATE('2017-01-03','YYYY-MM-DD');
So, if you happen to have a text as an input, then you will need to convert it to date. Next, you need to know that if you have a date, you can add days to it, like
SELECT CURRENT_DATE + INTERVAL '90 day';
Now, you need to understand that you can use dynamic variables, like:
select now() + interval '1 day' * 180;
Finally, you will need a temporary table to generate several values described as above. Read more here: How to return temp table result in postgresql function
Summary:
create a function
that generates a temporary table
where you insert as many records as you like
having the date shifted
and converting text to date if needed
You can create a function that returns a SETOF dates/timestamps. The below function takes 3 parameters: a timestamp, an interval, the num_of_periods desired. It returns num_of_periods + 1 timestamps, as it returns the original timestamp and the num_of_periods each the specified interval apart.
create or replace
function generate_periodic_time_intervals
( start_date timestamp
, period_length interval
, num_of_periods integer
, out gen_timestamp timestamp
)
returns setof timestamp
language sql
immutable strict
as $$
select (start_date + n * period_length)::timestamp
from generate_series(0,num_of_periods) gs(n)
$$;
For your particular case to timestamp/date as necessary. The same function would work for your case with the interval specified as '3 months' or of '90 days'. Just a note the interval specified can be any valid INTERVAL data type. See here. It also demonstrates the difference between 3 months and 90 days.

Postgresql Query very slow with ::date, ::time, and interval

I have a sql query that is very slow:
select number1 from mytable
where symbol = 25
and timeframe = 1
and date::date = '2008-02-05'
and date::time='10:40:00' + INTERVAL '30 minutes'
The goal is to return one value, and postgresql takes 1.7 seconds to return the desired value(always a single value). I need to execute hundreds of those queries for one task, so this gets extremely slow.
Executing the same query, but pointing to the time directly without using interval and ::date, ::time takes only 17ms:
select number1 from mytable
where symbol = 25
and timeframe = 1
and date = '2008-02-05 11:10:00'
I thought it would be faster if I would not use ::date and ::time, but when I execute a query like:
select number1 from mytable
where symbol = 25
and timeframe = 1
and date = '2008-02-05 10:40:00' + interval '30 minutes'
I get a sql error (22007). I've experimented with different variations but I couldn't get interval to work without using ::date and ::time. Date/Time Functions on postgresql.org didn't help me out.
The table got a multi column index on symbol, timeframe, date.
Is there a fast way to execute the query with adding time, or a working syntax with interval where I do not have to use ::date and ::time? Or do I need to have a special index when using queries like these?
Postgresql version is 9.2.
Edit:
The format of the table is:
date = timestamp with time zone,
symbol, timeframe = numeric.
Edit 2:
Using
select open from ohlc_dukascopy_bid
where symbol = 25
and timeframe = 1
and date = timestamp '2008-02-05 10:40:00' + interval '30' minute
Explain shows:
"Index Scan using mcbidindex on mytable (cost=0.00..116.03 rows=1 width=7)"
" Index Cond: ((symbol = 25) AND (timeframe = 1) AND (date = '2008-02-05 11:10:00'::timestamp without time zone))"
Time is now considerably faster: 86ms on first run.
The first version will not use a (regular) index on the column named date.
You didn't provide much information, but assuming the column named date has the datatype timestamp (and not date), then the following should work:
and date = timestamp '2008-02-05 10:40:00' + interval '30 minutes'
this should use an index on the column named date (but only if it is in fact a timestamp not a date). It is essentially the same as yours, the only difference is the explicit timestamp literal (although Postgres should understand '2008-02-05 10:40:00' as a timestamp literal as well).
You will need to run an explain to find out if it's using an index.
And please: change the name of that column. It's bad practise to use a reserved word as an identifier, and it's a really horrible name, which doesn't say anything about what kind of information is stored in the column. Is it the "start date", the "end date", the "due date", ...?

SQL SERVER passing getdate() or string date not working correctly

CREATE PROCEDURE sp_ME
#ID int,
#ThisDate datetime = null
AS
SET NOCOUNT ON
IF #ThisDate IS NULL
BEGIN
SET #ThisDate = CURRENT_TIMESTAMP
END
DECLARE #intErrorCode int,
#QBegin datetime,
#QEnd datetime
SELECT #intErrorCode = ##ERROR
IF #ThisDate BETWEEN '01/01/' + CONVERT(VARCHAR(4), YEAR(#ThisDate))
AND '03/31/' + CONVERT(VARCHAR(4), YEAR(#ThisDate))
BEGIN
Select #QBegin = DATEADD(s,0,CAST ('10/01/' AS varchar(6) ) +
CONVERT(VARCHAR(4),DATEPART (year,#ThisDate)-1))
Select #QEnd = DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,#QBegin)+3,0))
SELECT * FROM QUERY
WHERE MEID = #ID
AND mydate >= #QBegin
AND mydate <= #QEnd)
END
SELECT #intErrorCode = ##ERROR
IF (#intErrorCode <> 0) GOTO ErrHandler
ErrHandler:
RETURN #intErrorCode
GO
It returns a dataset when you leave it blank and it assumes and fills in the date, however when you plug in a date it just states "The command completed successfully."
Any help would be more than appreciated.
At a guess, you need to query the previous quarter's results, which would just be this query:
SELECT * FROM QUERY
WHERE MEID = #ID
AND mydate >= DATEADD(quarter,DATEDIFF(quarter,'20010101',#ThisDate),'20001001'),
AND mydate < DATEADD(quarter,DATEDIFF(quarter,'20010101',#ThisDate),'20010101'))
And get rid of that big if condition, etc.
You could also get rid of the first if, if you put COALESCE(#ThisDate,CURRENT_TIMESTAMP) in the above, where I currently have #ThisDate.
I use the DATEADD(quarter,DATEDIFF(quarter,'20010101',#ThisDate),'20001001') pattern for a lot of datetime manipulation. It let's you achieve a lot in a few operations. In this case, it's the difference between the two dates ('20010101','20001001') which is giving us the previous quarter.
You'll frequently encounter the DATEADD/DATEDIFF pattern in questions involving removing the time portion from a datetime value. The canonical version of this is DATEADD(day,DATEDIFF(day,0,#Date),0). But the pattern can be generally extended to work with any of the datetime components. If you choose month rather than day, you'll get midnight at the start of the first of the month (of the day you've supplied)
Where it gets tricky is when you use dates (instead of 0), and especially if you don't use the same date for both calculations. This allows you to apply an additional offset that seems almost "free" - you're already using this construct to remove the time component, the fact that you can compute e.g. the last date in a quarter/month/etc is a bonus.

How can I compare two datetime fields but ignore the year?

I get to dust off my VBScript hat and write some classic ASP to query a SQL Server 2000 database.
Here's the scenario:
I have two datetime fields called fieldA and fieldB.
fieldB will never have a year value that's greater than the year of fieldA
It is possible the that two fields will have the same year.
What I want is all records where fieldA >= fieldB, independent of the year. Just pretend that each field is just a month & day.
How can I get this? My knowledge of T-SQL date/time functions is spotty at best.
You may want to use the built in time functions such as DAY and MONTH. e.g.
SELECT * from table where
MONTH(fieldA) > MONTH(fieldB) OR(
MONTH(fieldA) = MONTH(fieldB) AND DAY(fieldA) >= DAY(fieldB))
Selecting all rows where either the fieldA's month is greater or the months are the same and fieldA's day is greater.
select *
from t
where datepart(month,t.fieldA) >= datepart(month,t.fieldB)
or (datepart(month,t.fieldA) = datepart(month,t.fieldB)
and datepart(day,t.fieldA) >= datepart(day,t.fieldB))
If you care about hours, minutes, seconds, you'll need to extend this to cover the cases, although it may be faster to cast to a suitable string, remove the year and compare.
select *
from t
where substring(convert(varchar,t.fieldA,21),5,20)
>= substring(convert(varchar,t.fieldB,21),5,20)
SELECT *
FROM SOME_TABLE
WHERE MONTH(fieldA) > MONTH(fieldB)
OR ( MONTH(fieldA) = MONTH(fieldB) AND DAY(fieldA) >= DAY(fieldB) )
I would approach this from a Julian date perspective, convert each field into the Julian date (number of days after the first of year), then compare those values.
This may or may not produce desired results with respect to leap years.
If you were worried about hours, minutes, seconds, etc., you could adjust the DateDiff functions to calculate the number of hours (or minutes or seconds) since the beginning of the year.
SELECT *
FROM SOME_Table
WHERE DateDiff(d, '1/01/' + Cast(DatePart(yy, fieldA) AS VarChar(5)), fieldA) >=
DateDiff(d, '1/01/' + Cast(DatePart(yy, fieldB) AS VarChar(5)), fieldB)
Temp table for testing
Create table #t (calDate date)
Declare #curDate date = '2010-01-01'
while #curDate < '2021-01-01'
begin
insert into #t values (#curDate)
Set #curDate = dateadd(dd,1,#curDate)
end
Example of any date greater than or equal to today
Declare #testDate date = getdate()
SELECT *
FROM #t
WHERE datediff(dd,dateadd(yy,1900 - year(#testDate),#testDate),dateadd(yy,1900 - year(calDate),calDate)) >= 0
One more example with any day less than today
Declare #testDate date = getdate()
SELECT *
FROM #t
WHERE datediff(dd,dateadd(yy,1900 - year(#testDate),#testDate),dateadd(yy,1900 - year(calDate),calDate)) < 0