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", ...?
Related
I have a syntax, but it doesn't work.
Here is my query:
SELECT *
FROM aqua.reading
WHERE
CAST(reading.pres_date AS VARCHAR)
BETWEEN LIKE '2022-10-18%' AND LIKE '2022-10-18%'
it says:
ERROR: type "like" does not exist
LINE 1: ... WHERE CAST(reading.pres_date AS VARCHAR) BETWEEN LIKE '2022...
^
SQL state: 42704
Character: 77
I am trying to get all the data with timestamp and timezone and implement a date range
Don't compare dates (or timestamps) as strings. Compare them to proper date (or timestamp) values. Given the fact that you use the same "date" but with a wildcard at the end, I am assuming(!) that pres_date is in fact a timestamp column and you want to find all rows with a given date regardless of the time value of the timestamp.
The best approach is to use a range query with >= ("greater than or equal) on the lower value and < (strictly lower than) on the next day:
SELECT *
FROM aqua.reading
WHERE reading.pres_date >= DATE '2022-10-18'
AND reading.pres_date < DATE '2022-10-19'
Alternatively you can cast the timestamp to a date and use the = operator if you really want to pick just one day:
SELECT *
FROM aqua.reading
WHERE cast(reading.pres_date as DATE) = DATE '2022-10-18'
However that will not make use of a potential index on pres_date so is likely to be slower than the range query from the first solution
I am trying to query PostgreSQL database for rows where interval has elapsed from the last run. Main columns for this purpose are processed_at as timestamptz and frequency (in minutes) as integer.
I am failing with operators, since not many of them can operate together timestamp & integer.
Can someone please propose a query that would solve this? Thank you very much for help
From here Date/time operators:
timestamp + interval → timestamp
Add an interval to a timestamp
timestamp '2001-09-28 01:00' + interval '23 hours' → 2001-09-29 00:00:00
select now() + (10::varchar || ' min')::interval;
?column?
-------------------------------
2021-10-15 09:05:37.927163-07
--Or in your case. If I'm following you are adding the interval.
select processed_at + (frequency::varchar || ' min')::interval;
The query takes the integer value of minutes and converts it to an interval of minutes that can be added to the timestamp.
Further explanation, || is the Postgres concatenation operator and ::varchar, ::interval are casting shorthand.
UPDATE
I keep forgetting about the make_*() functions for date/time/interval
--A shorter version
select processed_at + make_interval(mins => frequency);
Saves all the casting.
I have a column type time(7).
What i want is to add in time column to date.
i manage to get the only date using GETDATE() function but i fail to add the time part next to date.
Query:
SELECT [Compay]
,[Time]
,CAST(GETDATE() AS DATE) AS Today
,CAST(CAST(GETDATE() AS DATE) AS NVARCHAR) AS Today_AS_Nvarchar
,CAST([Time] AS NVARCHAR) AS Time_AS_Nvarchar
,CAST(CAST(GETDATE() AS DATE) AS NVARCHAR) + ' ' + CAST([Time] AS NVARCHAR) AS Today_Time_AS_Nvarchar
,CONVERT(datetime,CAST(CAST(GETDATE() AS DATE) AS NVARCHAR) + ' ' + CAST([Time] AS NVARCHAR),103)
FROM [Testing_Env].[dbo].[Com_CD_Test]
Error:
Conversion failed when converting date and/or time from character string.
The error arise on CONVERT(datetime,CAST(CAST(GETDATE() AS DATE) AS NVARCHAR) + ' ' + CAST([Time] AS NVARCHAR),103)
is there any easier/orthodox way to achieve it?
You can't add the new date and time data types together like you can the old data types; personally I think this is also better as it stop people treating dates and times like a numerical value.
Assuming you have a date column and a time column you have a few of options. The first is to CAST/CONVERT both to a datetime and then "add" them together. Because the "old" data types work more like numerical values this works, however, you will lose accuracy of your time value if it has a precision of 3 or higher (as datetime is only accurate to 1/300 seconds):
DECLARE #TimeValue time(7) = '17:52:12.1234567',
#DateValue date = '20211016';
SELECT CONVERT(datetime, #DateValue) + CONVERT(datetime, #TimeValue);
If loosing accuracy isn't an option, then you could to use conversion on the date value and use DATEDIFF and DATEADD. For a time(7) you'll want to be using nanoseconds (as microseconds isn't accurate enough). Unfortunately this poses another problem; DATEADD can't handle bigint values (still) and there is no DATEADD_BIG (like there is DATEDIFF_BIG), so this becomes overly complex. You need to first get the difference in milliseconds, and then add the remainder in nanoseconds to still be accurate to 1/1000000 of a second:
DECLARE #TimeValue time(7) = '17:52:12.1234567',
#DateValue date = '20211016';
SELECT DATEADD(NANOSECOND,DATEDIFF_BIG(NANOSECOND,'00:00:00', #TimeValue) % 1000000,DATEADD(MILLISECOND, DATEDIFF_BIG(MILLISECOND,'00:00:00', #TimeValue), CONVERT(datetime2(7),#DateValue)));
Finally, yes, you can convert to values to strings, and then to a datetime2 value; this is probably the easiest methiod. You just need to ensure you use style codes:
DECLARE #TimeValue time(7) = '17:52:12.1234567',
#DateValue date = '20211016';
SELECT CONVERT(datetime2(7),CONVERT(varchar(10), #DateValue, 23) + 'T' + CONVERT(varchar(17), #TimeValue, 114),126);
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.
My first query is:
SELECT distinct wfc_request_job_id,wfc_request_job_info,
replace(iso_cc,';',' ') as "iso_cc",to_char(wfc_request_start_ts,'yyyy-MM-dd HH:mm:ss') as ts,
sent_message_count,
(link_object_count + poi_object_count + point_address_object_count) as request_object_count
FROM wfc_request_job
where
wfc_request_job_id=173526;
This returns ts as 2015-08-16 03:08:59
Second Query:
SELECT wfc_request_job_id,wfc_request_start_ts,wfc_request_end_ts,replace(iso_cc,';',' ') as "iso_ccs",sent_message_count,wfc_queue_name
FROM wfc_request_job
where
to_char(wfc_request_start_ts,'YYYY-MM-DD') >= to_char(to_date('08/16/2015','MM/DD/YYYY'),'YYYY-MM-DD')
and to_char(wfc_request_start_ts,'YYYY-MM-DD') <= to_char(to_date('08/16/2015','MM/DD/YYYY'),'YYYY-MM-DD')
order by wfc_request_job_id desc
This returns ts of the job id mentioned above as - "2015-08-16 15:58:59.809+02"
How can I make both the queries return ts in UTC+02 - i.e. same timezone
The data type of wfc_request_start_ts is - timestamp with timezone
I changed to queries to have the format HH24:MI:SS however that did not help. Please note that the webapp using these queries will be opened in both Germany and USA.
According to postgresql manual to_char there is TZ (and OF as of v9.4) template patterns for Date/Time formatting.
Therefore in query you need to add it so
postgres=# select to_char(now(),'yyyy-MM-dd HH24:mm:ss TZ');
to_char
------------------------
2015-08-19 12:08:56 CEST
(1 row)
Also, make sure you specify timezone when converting
so instead
to_date('08/16/2015','MM/DD/YYYY')
use
TIMESTAMP WITH TIME ZONE '2015-08-16 00:00:00+02';
in second query.