How to manipulate column data in postgres - postgresql

I need to manipulate column data in postgres
When I run the query-
SELECT t.date::varchar
FROM generate_series(timestamp '2020-02-27'
, timestamp '2020-03-01'
, interval '1 day') AS t(date);
it returns -
2020-02-27 00:00:00
2020-02-28 00:00:00
2020-02-29 00:00:00
2020-03-01 00:00:00
I want -
20200227
20200228
20200229
20200301
Removed '-' and truncated from end.
Can someone guide

If you don't specifically need some features of a varchar, by default use text instead.
You don't need to cast every time - generate_series() will do that automatically once it detects your step is an interval. That's unless you specifically want the generate_series(timestamp,timestamp,inteval) variant, not generate_series(timestamptz,timestamptz,inteval).
If you cast to be explicit, cast your dates as date. Regardless of whether you leave it as text literal or make them actual dates, PostgreSQL will have to cast them to match the function definition.
If you're planning to group things by a text-based date or do that to truncate timestamps, consider date_bin() and date_trunc() as well as simply holding things as a native date type. It'll take less space, run faster and enable native date-specific functions.
Make sure you're using to_char() to its full potential - it can save a lot of formatting later.
SELECT to_char(t.date,'YYYYMMDD') as date
FROM generate_series('2020-02-27'
, '2020-03-01'
, interval '1 day') AS t(date);
-- date
------------
-- 20200227
-- 20200228
-- 20200229
-- 20200301

Related

How to store mmyy in Postgres?

There are two variables:
$mm = "01";
$yy = "22";
I need to store it in one column Postges mmyy.
What is type mmyy must be if there is not day?
Or I can use the first day of month always like: 010122.
Eventually, I want to filter rows with where mmyy > now().
If you want to treat it like a date/time value, preferably store it as date. That would be:
date '2022-01-01'
Occupies only 4 bytes, same as int4.
Always use ISO format, which is unambiguous regardless of your locale settings. More in the manual.
To convert your variables, you might use the Postgres function to_date():
test=> SELECT to_date('2201', 'YYMM');
to_date
------------
2022-01-01
(1 row)
test=> SELECT to_date('22'::text || '01'::text, 'YYMM');
to_date
------------
2022-01-01
(1 row)
Or prepare a date literal in ISO format.
When compared to now() (which returns timestamp with time zone) the date value is coerced to the first instant of Jan 1st, 2022 at the time zone determined by the current setting of your session. See:
Difference between now() and current_timestamp
So it works as intended out of the box - except that you possibly haven't thought about time zones, yet ...
For other tasks with date arithmetic, an integer might be a good choice. See:
How do you do date math that ignores the year?

When do I need to cast a value as a date?

I'm working myself through the Datacamp SQL track, and I'm currently working with date values. I've encountered two examples which seem contradictory to me.
-- Count requests created on January 31, 2017
SELECT count(*)
FROM evanston311
WHERE date_created::date='2017-01-31';
And:
-- Count requests created on February 29, 2016
SELECT count(*)
FROM evanston311
WHERE date_created>= '2016-02-29'
AND date_created< '2016-03-01';
Why do I need to cast the value as date in the first case but not the other?
As with most typed languages, you can rely on implicit type casting... until you can't.
Something like date_created >= '2016-02-29' Postgres can use the type of date_created to figure out how to implicitly cast '2016-02-29'. There's no ambiguity. But sometimes Postgres can't make a guess at all.
OTOH a function like date_part has multiple signatures date_part(text, timestamp) and date_part(text, interval). If you pass it a date string...
test=# select date_part('day', '2019-01-03');
ERROR: function date_part(unknown, unknown) is not unique
LINE 1: select date_part('day', '2019-01-03');
^
HINT: Could not choose a best candidate function. You might need to add explicit type casts.
...Postgres cannot make a guess because the second string could be interpreted as either a timestamp or an interval type. You need to resolve this ambiguity.
# select date_part('day', '2019-01-03'::date);
date_part
-----------
3
Now that Postgres knows you're passing in a date it can correctly guess to use it as a timestamp.
Another reason is as a cheap way to truncate timestamps. In your example date_created::date = '2017-01-31' will truncate date_created to be a date and make the comparison work. Of course, date_created should already be a date...
You can use it on the value being compared if you're not sure if that value will be a date or a timestamp.
select * from table
where date_created = $1::date
This will work the same with '2019-01-02' or '2019-01-02 03:04:05'.
Which brings us to our final reason: making up for bad schemas. Like if date_created is actually a timestamp, or all too common, text. In that case you need to explicitly control how comparisons are made. For example, let's say we had text_created of type text that contained timestamps as strings: naught. And maybe some poorly formatted data crept in that has extra spaces on the end...
-- Text comparison compares the values exactly.
test=# select * from test where text_created = '2019-01-04';
date_created | time_created | text_created
--------------+--------------+--------------
-- Date comparison compares as dates ignoring the extra whitespace.
test=# select * from test where text_created::date = '2019-01-04';
date_created | time_created | text_created
--------------+--------------+--------------
| | 2019-01-04
See Chapter 10. Type Conversion in the Postgres docs for more.

timezone aware date_trunc function

The following query
SELECT the_date FROM date_trunc('day', timestamp with time zone
'2001-01-1 00:00:00+0100') as the_date
results to
the_date
2000-12-31 00:00
Is there a way to tell date_trunc to do day/month/year conversions based on the timezone it is feeded with?
The expected output would be: 2001-01-1 00:00+0100
You need to specify at which time zone you want it to show
select
date_trunc(
'day',
timestamp with time zone '2001-01-1 00:00:00+0100' at time zone '-02'
) as the_date;
the_date
---------------------
2001-01-01 00:00:00
AT TIME ZONE
While the marked answer might be correct for the OP's weird circumstances it is more likely incorrect for others. You need to convert the timestamp returned by date_trunc to the proper timezone.
select
date_trunc(
'day',
some_timestamp at time zone users_timezone
) at time zone users_timezone as the_date;
The important thing to understand is date_trunc returns a timestamp with no timezone attached to it. You need to convert the timestamp to the proper timezone because the database client or whatever downstream might have a different timezone.
#Adam's answer is definitely more helpful. Although I think we can improve on it again because if we're truncating a Timestamp to a single day (or week/month/etc), then we want to make sure that we're dealing with a Date object, not a Timestamp. Otherwise we may give other pieces of code the impression that something just actually happened to occur at midnight (or potentially some other misleading time of day).
So I would use:
SELECT date_trunc('day', some_timestamp AT TIME ZONE users_timezone)::date AS the_date;
which casts the result to a Date, rather than Timestamp.
The result will be something like:
the_date
------------
2019-09-14
instead of the more misleading result of:
the_date
---------------------
2019-09-14 00:00:00

Oracle SQL : Expand year to a full date

This must be simple to answer, but how do you expand in Oracle a year to a full date, e.g.
1996 to 1996-01-01 00:00:00 ?
EDIT
The data type of the year is char, and I want to end up by comparing this year to a string-date, e.g.
1996 <= '1998-31-12 12:04:35'
It is important that the expanded data is expanded in the same data Format (since I get the dates preformatted)
At the end I need something like this
WHERE ( to_date(table.year_char ,'YYYY') <= '1996-12-31 00:00:00')
or sth like this
WHERE ( to_char(to_date(table.year_char ,'YYYY')) <= '1996-12-31 00:00:00')
or anything which works
If you're starting with the year as a string and you want to end up with a DATE object, you use the TO_DATE() function; but you need to supply a dummy month or it'll default to the first day of the current month in the specified year:
select to_date('1996', 'YYYY') from dual;
May, 01 1996 00:00:00+0000
SQL Fiddle
With the month, and to make it clearer the day too, appended and a suitable format model:
select to_date('1996' ||'-01-01', 'YYYY-MM-DD') from dual
January, 01 1996 00:00:00+0000
SQL Fiddle. I've left the year and the '-01-01' literal separate and concatenated on the assumption that you'll be using a variable really...
In a WHERE clause, using the sample date you initially showed:
select * from dual
where to_date('1996' ||'-01-01', 'YYYY-MM-DD')
<= to_date('1998-31-12 12:04:35', 'YYYY-DD-MM HH24:MI:SS')
Or if you're actually comparing to a string as your second example suggests, just leave it as a string, as you want both sides of the comparison to be the same data type without any implicit conversion that might cause you problems later. The string you have fortunately has the data in a format that is comparable:
WHERE (table.year_char || '-01-01 00:00:00' <= '1996-12-31 00:00:00')
You could convert it to and back from a DATE but there isn't any benefit in doing so.

Default timestamp format and fractional seconds

I'm trying to format the timestamps in my Postgres database to a certain format:
YYYY-MM-DD HH24:MI:SS
By doing:
update myTable set tds = to_char(tds, 'YYYY-MM-DD HH24:MI:SS')::timestamp;
I managed to set all the previously stored tds to this format. However, any newly added entry goes back to: YYYY-MM-DD HH24:MI:SS.MS since the default is set to now().
How do I change this so that newly added entries also have the format: YYYY-MM-DD HH24:MI:SS?
There is no format stored in a timestamp type. You can set its default to a timestamp truncated to the second at creation time
create table t (
tds timestamp default date_trunc('second', now())
)
Or alter the table
alter table t
alter column tds
set default date_trunc('second', now());
insert into t values (default);
INSERT 0 1
select * from t;
tds
---------------------
2014-03-11 19:24:11
If you just don't want to show the milliseconds part format the output
select to_char(now(), 'YYYY-MM-DD HH24:MI:SS');
to_char
---------------------
2014-03-11 19:39:40
The types timestamp or timestamptz optionally take a precision modifier p: timestamp(p).
To round to full seconds, set the default to:
now()::timestamp(0)
or:
now()::timestamptz(0)
Standard SQL functions CURRENT_TIMESTAMP (returns timestamptz) or LOCALTIMESTAMP (returns timestamp) allow the same precision modifier:
CURRENT_TIMESTAMP(0)
LOCALTIMESTAMP(0)
That's a bit shorter than calling date_trunc() - which truncates fractional seconds (may be what you really want!)
date_trunc('second', now())
Store timestamps as timestamptz (or timestamp), not as character type.
Finally, to make sure that ...
newly added entries also have the format: YYYY-MM-DD HH24:MI:SS
you could define your column as type timestamptz(0). This covers all values entered into that column, not just the default. But the rounding may introduce timestamps up to half a second in the future. If that can be an issue in any way, rather use date_trunc().
See #Clodoaldo's answer for instructions on to_char() and how to ALTER TABLE.
This related answer for in-depth information on timestamps and time zone handling:
Ignoring time zones altogether in Rails and PostgreSQL