PostgreSQL: SELECT integers as DATE or TIMESTAMP - sql

I have a table where I have multiple integer columns: year, month and day. Unfortunately, while the three should have been grouped into one DATE column from the beginning, I am now stuck and now need to view it as such. Is there a function that can do something along the lines of:
SELECT makedate(year, month, day), othercolumn FROM tablename;
or
SELECT maketimestamp(year, month, day, 0, 0), othercolumn FROM tablename;

You can
SELECT format('%s-%s-%s', "year", "month", "day")::date
FROM ...
or use date maths:
SELECT DATE '0001-01-01'
+ ("year"-1) * INTERVAL '1' YEAR
+ ("month"-1) * INTERVAL '1' MONTH
+ ("day"-1) * INTERVAL '1' DAY
FROM ...
Frankly, it's surprising that PostgreSQL doesn't offer a date-constructor like you describe. It's something I should think about writing a patch for.
In fact, a quick look at the sources shows that there's an int date2j(int y, int m, int d) function at the C level already, in src/backend/utils/adt/datetime.c. It just needs to be exposed at the SQL level with a wrapper to convert to a Datum.
OK, now here's a simple makedate extension that adds a single function implemented in C, named makedate. A pure-SQL version is also provided if you don't want to compile and install an extension. I'll submit the C function for the 9.4 commitfest; meanwhile that extension can be installed to provide a fast and simple date constructor:
regress=# SELECT makedate(2012,01,01);
makedate
------------
2012-01-01
(1 row)

PostgreSQL 9.4+
In PostgreSQL 9.4, a function was added to do just this
make_date(year int, month int, day int)

There may be a more elegant method, but this will give you a date.
select to_date(to_char(year * 10000 + month * 100 + day,'00000000'), 'yyyymmdd')
from tablename;

Try something like:
SELECT year * interval '1 year' +
month * interval '1 month' +
day * interval '1 day'
FROM tablename;

Related

PostgreSQL: SELECT * from table WHERE timestamp IS WITHIN THIS MONTH

For some reason I'm kind of lost on how to archive:
SELECT * FROM table WHERE timestamp IS WITHIN THIS MONTH;
I've looked at https://www.postgresql.org/docs/9.4/static/functions-datetime.html, but are only able to select X days backwards.
I'm running PostgreSQL 9.4
... WHERE date_trunc('month', timestamp)
= date_trunc('month', current_timestamp);
Alternatively:
... WHERE timestamp >= date_trunc('month', current_timestamp)
AND timestamp < date_trunc('month', current_timestamp) + INTERVAL '1 month';
The second version can use an index on timestamp, the first would need one on the expression date_trunc('month', timestamp).
Why don't you just filter the month with between ?
Pass the start of this month as variable1, and the end of this month as variable2...
SELECT * FROM table WHERE
timestamp >= __month_start AND timestamp < __next_month_start
e.g.
SELECT * FROM table
WHERE
(
timestamp >= '20170701'::timestamp
AND
timestamp < '20170801'::timestamp
)
Unlike using functions in the where-clause, this maintains sargability.
What Laurenz Albe suggested will work, however you're going to have a performance penalty because you'll lose cardinality on that field, you either have to index expression you're going to query (Apparently PostgreSQL allows to do that: https://www.postgresql.org/docs/current/static/indexes-expressional.html) or create a separate column to store yyyy-mm values and query it.

How to add variable minutes to a given date?

I have the next query (I'm using postgresql):
SELECT TIMESTAMP fecha_cita + cast((select tiempo_intervencion from cita_intervencion) as interval)
from cita;
What I'm doing here is basically taking a date like this '2001-09-28 01:00' from the 'cita' table (that's what fecha_cita is) and I want to add more time to this complete date, in this case 'tiempo_intervencion' is something like '120 minutes' but this information is in a different table called 'cita_intervencion', the problem is that since these are variables dates and times and not a fixed date, things like SELECT TIMESTAMP '2010-11-19 01:11:22' + INTERVAL '120 minutes doesn't work for me, I get errors like:
ERROR: syntax error at or near "fecha_cita"
LINE 1: SELECT TIMESTAMP fecha_cita + cast((select tiempo_intervenci...
ERROR: cannot cast type d_entero_p to interval
LINE 1: ...ct tiempo_intervencion from cita_intervencion) as interval) ...
I've looked up on google for some information on this and I was trying to follow this, but I can't find anything that can solve my problem.
You need a join and then cast the string to an interval:
SELECT c.fecha_cita, fecha_cita + ci.tiempo_intervencion::interval
from cita c
join cita_intervencion ci on c.id = ci.cita_id;
This assumes that there is some column in cita_intervencion that links that back to the cita table. If you really do not have that you can do something like this:
SELECT c.fecha_cita, fecha_cita + (select tiempo_intervencion::interval from cita_intervencion)
from cita c
But that will only work if cita_intervencion contains exactly one row.
The casting to an interval will only work if the values in tiempo_intervencion follow the rules for an interval.
You do not need the timestamp keyword for columns already defined as a timestamp that is only needed to introduce a timestamp literal (constant) value.
SELECT TIMESTAMP '2010-11-19 01:11:22' + INTERVAL '120 minutes doesn't work for me
That works if you add the missing ' for the interval literal:
SELECT TIMESTAMP '2010-11-19 01:11:22' + INTERVAL '120 minutes'
You can use:
select DATEADD(MINUTE,120,GETDATE())

Add number of days in given date

In PostgreSQL:
Let say:
SELECT DATE(NOW())+interval '1 day';
It will show date tomorrow.
Problem:
While I having a column duration in table tbl, following SQL does not work :
SELECT DATE(NOW())+interval duration || ' day' from tbl;
I just wondering how to make the '1 day' become a variable instead of a constant.
Do not try to make '1day' a variable, just multiply it by duration variable:
SELECT DATE(NOW()+duration * INTERVAL '1day') FROM tbl;

Optimizing SQL Query and Dynamically using current date

I am trying to optimize a simple SQL query and was wondering if anyone has any suggestions. I am developing using Oracle SQL Developer (which I don't like) on an Oracle 11g database. The query I am using is:
SELECT count(*)
FROM my_table
WHERE my_date
BETWEEN TO_DATE('2012-5-09T05.00.00','YYYY-MM-DD"T"HH24:MI:SS')
AND TO_DATE('2012-5-10T04.59.59','YYYY-MM-DD"T"HH24:MI:SS')
AND my_code='33'
GROUP BY my_code;
Also, I want to be able to use this query dynamically by changing the part of the date to be whatever the current date is, but I want to be able to specify the hour. So I want to be comparing something like:
getdate() + 'T05.00.00'
I have no idea how to do this and the getdate() function doesn't seem to work in SQL Developer/I don't know how to use it correctly.
So what I'm looking for is optimization suggestions and pointers on how to just dynamically change the day-month-year part of the date I want to constrain my results to. Thanks!
To get current date, you can use SYSDATE. To add x number of hours to it, you can add x/24. So something like this:
Example: Get current date + 5 hours
SELECT SYSDATE + 5/24 FROM dual
So in your example:
SELECT count(*)
FROM my_table
WHERE my_date
BETWEEN sysdate
AND sysdate + 5/24 -- if you want 5 hours ahead, for example
AND my_code='33'
GROUP BY my_code;
If you want to be able to change the number of hours, you could make this code into a function, and pass in the hours and code as variables.
Something like this:
CREATE FUNCTION myfunc
(
p_num_hours INT
, p_my_code VARCHAR
) RETURN INT
AS
l_ret INT;
BEGIN
SELECT count(*)
INTO l_ret
FROM my_table
WHERE my_date
BETWEEN sysdate
AND sysdate + p_num_hours/24
AND my_code=p_my_code
RETURN l_ret;
END;
As an alternative to adding fractional days via expressions such as "5 / 24" you might want to use an INTERVAL constant. For example:
SELECT count(*)
FROM my_table
WHERE my_date BETWEEN (TRUNC(SYSDATE) + INTERVAL '5' HOUR)
AND (TRUNC(SYSDATE) + INTERVAL '1' DAY +
INTERVAL '5' HOUR - INTERVAL '1' SECOND) AND
my_code='33'
GROUP BY my_code
I like to use INTERVAL constants because it's quite clear what these constants represent. With the fractional-day constants I sometimes get confused ('course, I sometimes get confused, regardless... :-)
Share and enjoy.
If I understand correctly, something like
select count(*)
from my_table
where trunc(my_date) = trunc(sysdate)
and my_code = '33'
group by my_code;
or
select count(*)
from my_table
where my_date
between sysdate and sysdate + 5/24
and my_code = '33'
group by my_code;
HTH.
Alessandro

Postgresql - query problem

I have a variable which is in this format 2011-05-13.
I want to make a query that adds one day to this variable and searches for days like this in the database.
I made this query but it doesnt work.
select phone from employee where date like (select date '2011-05-13' + 1) %
Can anyone help?
You need an INTERVAL:
SELECT phone FROM employee WHERE datefield = (date '2011-05-13' + INTERVAL '1 DAY') ;
Edit: Don't use LIKE when you're working with dates, there is no LIKE for a date.
Try the following:
SELECT phone FROM employee WHERE to_char(date, 'yyyy-mm-dd')::timestamp = ('2011-05-13'::timestamp + '1 day'::interval)