Change the column type to 'Date' from 'Character varying' in PostgreSQL 11.0 - postgresql

I have a postgreSQL 11.0 table with dates in below format, but the column type 'character varying'.
id primary_completion_date
0 December2019
1 April2020
2 September2021
3 September2022
4 December2021
Is it possible to convert the column type to 'date'. When I try to convert it to date, It changes the column content to below:
id primary_completion_date
0 12-2019-01
1 04-2020-01
2 09-2021-01
3 09-2022-01
4 12-2021-01
I am using following statement in my script.
alter table tbl alter primary_completion_date type date using to_date(primary_completion_date, 'MonthYYYY')
How can I retain the column content as the input but change the column type to 'Date'.

How can I retain the column content as the input but change the column type to 'Date'*"
You can't because December2019 is not a valid value for a date column.
But you can always format the date value when you retrieve it:
select id, to_char(primary_completion_date, 'MonthYYYY') as formatted_completion_date
from the_table

Related

Type error of getting average by id in KDB

I am trying make a function for the aggregate consumption by mid in a kdb+ table (aggregate value by mid). Also this table is being imported from a csv file like this:
table: ("JJP";enlist",")0:`:data.csv
Where the meta data is for the table columns is:
mid is type long(j), value(j) is type long and ts is type timestamp (p).
Here is my function:
agg: {select avg value by mid from table}
but I get the
'type
[0] get select avg value by mid from table
But the type of value is type long (j). So I am not sure why I can't get the avg I also tried this with type int.
Value can't be used as a column name because it is keyword used in kdb+. Renaming the column should correct the issue.
value is a keyword and should not be used as a column name.
https://code.kx.com/q/ref/value/
You can remove it as a column name using .Q.id
https://code.kx.com/q/ref/dotq/#qid-sanitize
q)t:flip`value`price!(1 2;1 2)
q)t
value price
-----------
1 1
2 2
q)t:.Q.id t
q)t
value1 price
------------
1 1
2 2
Or xcol
https://code.kx.com/q/ref/cols/#xcol
q)(enlist[`value]!enlist[`val]) xcol t
val price
---------
1 1
2 2
You can rename the value column as you read it:
flip`mid`val`ts!("JJP";",")0:`:data.csv

How to change date format of a column based on regex in PostgreSQL11.0

I have a table in PostgreSQL 11.0 with following column with date (column type: character varying).
id date_col
1 April2006
2 May2005
3 null
4
5 May16,2019
I would like to convert the column to 'date'
As there are two different date format, I am using a CASE statement to alter the column type based on a date pattern.
select *,
case
when date_col ~ '^[A-Za-z]+\d+,\d+' then alter table tbl alter date_col type date using to_date((NULLIF(date_col , 'null')), 'MonthDD,YYYY')
when date_col ~ '^[A-Za-z]+,\d+' then alter table tbl alter date_col type date using to_date((NULLIF(date_col, 'null')), 'MonthYYYY')
else null
end
from tbl
I am getting following error:
[Code: 0, SQL State: 42601] ERROR: syntax error at or near "table"
Position: 93 [Script position: 93 - 98]
The expected output is:
id date_col
1 2006-04-01
2 2005-05-01
3 null
4
5 2019-05-16
Any help is highly appreciated!!
You definitely can't alter a column one row at a time. Your better bet is to update the existing values so they are all the same format, then issue a single ALTER statement.

unique date field postgresql default value

I have a date column which I want to be unique once populated, but want the date field to be ignored if it is not populated.
In MySQL the way this is accomplished is to set the date column to "not null" and give it a default value of '0000-00-00' - this allows all other fields in the unique index to be "checked" even if the date column is not populated yet.
This does not work in PosgreSQL because '0000-00-00' is not a valid date, so you cannot store it in a date field (this makes sense to me).
At first glance, leaving the field nullable seemed like an option, but this creates a problem:
=> create table uniq_test(NUMBER bigint not null, date DATE, UNIQUE(number, date));
CREATE TABLE
=> insert into uniq_test(number) values(1);
INSERT 0 1
=> insert into uniq_test(number) values(1);
INSERT 0 1
=> insert into uniq_test(number) values(1);
INSERT 0 1
=> insert into uniq_test(number) values(1);
INSERT 0 1
=> select * from uniq_test;
number | date
--------+------
1 |
1 |
1 |
1 |
(4 rows)
NULL apparently "isn't equal to itself" and so it does not count towards constraints.
If I add an additional unique constraint only on the number field, it checks only number and not date and so I cannot have two numbers with different dates.
I could select a default date that is a 'valid date' (but outside working scope) to get around this, and could (in fact) get away with that for the current project, but there are actually cases I might be encountering in the next few years where it will not in fact be evident that the date is a non-real date just because it is "a long time ago" or "in the future."
The advantage the '0000-00-00' mechanic had for me was precisely that this date isn't real and therefore indicated a non-populated entry (where 'non-populated' was a valid uniqueness attribute). When I look around for solutions to this on the internet, most of what I find is "just use NULL" and "storing zeros is stupid."
TL;DR
Is there a PostgreSQL best practice for needing to include "not populated" as a possible value in a unique constraint including a date field?
Not clear what you want. This is my guess:
create table uniq_test (number bigint not null, date date);
create unique index i1 on uniq_test (number, date)
where date is not null;
create unique index i2 on uniq_test (number)
where date is null;
There will be an unique constraint for not null dates and another one for null dates effectively turning the (number, date) tuples into distinct values.
Check partial index
It's not a best practice, but you can do it such way:
t=# create table so35(i int, d date);
CREATE TABLE
t=# create unique index i35 on so35(i, coalesce(d,'-infinity'));
CREATE INDEX
t=# insert into so35 (i) select 1;
INSERT 0 1
t=# insert into so35 (i) select 2;
INSERT 0 1
t=# insert into so35 (i) select 2;
ERROR: duplicate key value violates unique constraint "i35"
DETAIL: Key (i, (COALESCE(d, '-infinity'::date)))=(2, -infinity) already exists.
STATEMENT: insert into so35 (i) select 2;

PostgreSQL : Update column with same vaue

Following is sample table and data
create table chk_vals (vals text);
insert into chk_vals values ('1|2|4|3|9|8|34|35|38|1|37|1508|1534');
So,How to update column vals by appending integer in 4th position of the existing value(ie. 3 | is used as a seperator) into the last position along with symbol |
as you can see the existsing value if 1|2|4|3|9|8|34|35|38|1|37|1508|1534 and the output should be 1|2|4|3|9|8|34|35|38|1|37|1508|1534|3
Use PostgreSQL's split_part() to splits the field and find the value at position 4
select split_part(vals,'|',4) val from chk_vals
this will return value 3
update chk_vals
set vals=vals||format('|%s',(select split_part(vals,'|',4) val from chk_vals))
Format()

Extract year from date within WHERE clause

I need to include EXTRACT() function within WHERE clause as follow:
SELECT * FROM my_table WHERE EXTRACT(YEAR FROM date) = '2014';
I get a message like this:
pg_catalog.date_part(unknown, text) doesn't exist**
SQL State 42883
Here is my_table content (gid INTEGER, date DATE):
gid | date
-------+-------------
1 | 2014-12-12
2 | 2014-12-08
3 | 2013-17-15
I have to do it this way because the query is sent from a form on a website that includes a 'Year' field where users enter the year on a 4-digits basis.
The problem is that your column is of data type text, while EXTRACT() only works for date / time types.
You should convert your column to the appropriate data type.
ALTER TABLE my_table ALTER COLUMN date TYPE date;
That's smaller (4 bytes instead of 11 for the text), faster and cleaner (disallows illegal dates and most typos).
If you have non-standard format add a USING clause with a conversion expression. Example:
Alter character field to date
Also, for your queries to be fast with a plain index on date you should rather use sargable predicates. Like:
SELECT * FROM my_table
WHERE date >= '2014-01-01'
AND date < '2015-01-01';
Or, to go with your 4-digit input for the year:
SELECT * FROM my_table
WHERE date >= to_date('2014', 'YYYY')
AND date < to_date('2015', 'YYYY');
You could also be more explicit:
to_date('2014' || '0101', 'YYYYMMNDD')
Both produce the same date '2014-01-01'.
Aside: date is a reserved word in standard SQL and a basic type name in Postgres. Don't use it as identifier.
This happens because the column has a text or varchar type, as opposed to date or timestamp. This is easily reproducible:
SELECT 1 WHERE extract(year from '2014-01-01'::text)='2014';
yields this error:
ERROR: function pg_catalog.date_part(unknown, text) does not exist
LINE 1: SELECT 1 WHERE extract(year from '2014-01-01'::text)='2014';
^ HINT: No function matches the given name and argument types. You might need to add explicit type casts.
extract or is underlying function date_part does not exist for text-like datatypes, but they're not needed anyway. Extracting the year from this date format is equivalent to getting the 4 first characters, so your query would be:
SELECT * FROM my_table WHERE left(date,4)='2014';