PostgreSQL - Create table and set specific date format - sql

I want to create a new table and set a date type with a specific format. Is that possible?
For example:
CREATE TABLE User (
...
EXPIRATION DATE " YYYY/MM"
...
)

I suggest a different approach: Never store date / time as character type (text, varchar(), ...) to begin with. Use an appropriate type, probably date in your case.
Also, never use reserved words as identifier. user is just not possible to begin with, you would have to double-quote, which I would discourage.
Could look like this:
CREATE TABLE usr (
usr_id serial PRIMARY KEY
,usr text UNIQUE
,expiration_date date
...
);
Now, various input formats are possible, as long as they are unambiguous. The related question #DrColossos has linked to in his comment has more on that.
The manual has all the details.
To enforce a particular input format, you could run the text literal through the to_date() function. Example:
INSERT INTO usr (usr, expiration_date)
VALUES ('flippin_user', to_date($my_date_literal, ' YYYY/MM');
Note: if you include the leading blank in the pattern, it is expected from the input!
Finally, you can format your date any way you like with to_char() on output:
SELECT usr, to_char(expiration_date, ' YYYY/MM') AS formatted_exp_date
WHERE usr_id = 1;

Related

Converting all data in a Varchar column to a date format

I'm working on a table with a column, 'Expiry Date', as a varchar with all data formatted as DD/MM/YYYY.
The creator of the table has used the wrong type for this expiry date column and now the client needs to filter and show all records before and after the current date as the time. This means the type needs to be changed to date or datetime type to be able to use the CURDATE() function.
However, the current format of the values does not satisfy and wont allow the type to change unless the format is changed to YYYY-MM-DD (or similar).
Is there any way to mass format the values in this column and this column alone as there are thousands of entries and formatting one by one would be extremely time consuming.
Let me assume that you are using MySQL.
Perhaps the simplest method is to add a generated column that is a date:
alter table t add column expiry_date_date as
(str_to_date(expiry_date, '%d/%m/%Y'));
You can also fix the data:
update t
set expiry_date = str_to_date(expiry_date, '%d/%m/%Y');
This will implicitly convert the result of str_to_date() to a date, which will be in the YYYY-MM-DD format.
More importantly, you can then do:
alter table t modify column expiry_date date;
Here is a db<>fiddle.
You can do similar operations in other databases, but the exact code is a bit different.
What you need is an update on that column, but before doing it I suggest you to check if the result is what you want.
select replace(expiry_date, '/', '-') new_expiry_date
from table_name
If this returns the results you want you can run the following update:
update table_name
set expiry_date = replace(expiry_date, '/', '-')
Of course you will need to replace expiry_date and table_name with the names of your column and table.

How to order by date when a column is of type nvarchar in Microsoft SQL Server

I have an issue where I created a column sent_Date of type nvarchar while it's storing date and time.
Now when I try to sort it by date, it's not doing so correctly.
I am using this query:
select *
from tbl_skip
where sent_date > '9/27/2020 7:29:11 PM'
order by SENT_DATE desc
Like the comments have said, the real solution here is fix your design. That means changing the column's data type an nvarchar to a date and time data type, I'm going to use a datetime2(0) here, as your data is accurate to a second, so seems the most appropriate.
Firstly we need to convert the value to as ISO value. I'm also, however, going to create a new column called Bad_Sent_Date, to store values that could not be converted. Experience has taught many of us that systems that incorrectly use string data types to store dates (or numerical data) rarely have good data integrity rules on the value (because if they did, it wouldn't be an nvarchar) to start, so have bad values like '29/02/2019' or mix styles, such as having both '09/29/2020' and '29/09/2020'.
Based on the single example we have, I will assume your data is supposed to be in the format MM/dd/yy hh:mm:ss AM/PM:
ALTER TABLE dbo.tbl_skip ADD Bad_Sent_Date nvarchar(30) NULL;
GO
UPDATE TABLE dbo.tbl_skip
SET Bad_Sent_Date = CASE WHEN TRY_CONVERT(datetime2(0),Sent_date,101) IS NULL THEN Sent_date END,
Sent_Date = CONVERT(nvarchar(30),TRY_CONVERT(datetime2(0),Sent_date,101),126);
GO
Now we have an ISO format, we can change the table's data type:
ALTER TABLE dbo.tbl_skip ALTER COLUMN Sent_date datetime2(0) NULL;
Note that if you do have constraints on the column Sent_date, or it isn't NULLable, you will first need to DROP said CONSTRAINTs, change the column to be NULLable and then recreate said CONSTRAINTs after you have altered the column.
You can also review the "dates" that failed to be converted with the following:
SELECT bad_sent_date
FROM dbo.tbl_skip
WHERE bad_sent_date IS NOT NULL
AND Sent_date IS NULL;
Once that's all done, then your query simply needs an update to use an unambiguous date literal, and it'll work:
SELECT *
FROM tbl_skip
WHERE sent_date > '2020-09-27T19:29:11' --'9/27/2020 7:29:11 PM'
ORDER BY SENT_DATE DESC;
You can convert the data from string to datetime.
Please note i used 100 as an example to convert to date time. You can use below link to see if its behaving correctly. link -https://www.w3schools.com/sql/func_sqlserver_convert.asp
select *
from tbl_skip
where sent_date > convert(datetime,'9/27/2020 7:29:11 PM',100)
ORDER BY CONVERT(datetime,SENT_DATE,100) desc
You should be able to convert it to a datetime
select *
from tbl_skip
where sent_date > '9/27/2020 7:29:11 PM'
order by convert(datetime,SENT_DATE) desc
Just make sure the data in column is legit. If so, it would make sense to convert the column type to a datetime.
alter table tbl_skip alter column SENT_DATE datetime
If the data is mixed, you may need to fix it or use something like
order by try_convert(datetime,SENT_DATE) desc

insert timestamp oracle

I have a table with the following format:
CREATE TABLE perflog(
REQ_TIME TIMESTAMP,
RESP_TIME TIMESTAMP,
OPERATION VARCHAR2(50),
STATUS_CODE VARCHAR2(10),
STATUS_BODY VARCHAR2(30)
);
-I want to insert a timestamp in the following format: e.g. 2020-07-27T23:33:41.427330
-I'm getting the following error:
SQL> insert into perflog(REQ_TIME) VALUES(2020-07-27T23:33:41.427330);
SP2-0552: Bind variable "33" not declared.
I don't get how to declare the timestamp in order to insert dates like the above. Sorry if it is a noob question but I'm a begginer.
Simply wrapping your value in single quotes isn't enough:
insert into perflog(REQ_TIME) VALUES('2020-07-27T23:33:41.427330');
ORA-01843: not a valid month
The actual error you get will depend on your session's NLS settings (and it's possible it would work - for you, if you set your session up in a certain way - but then not necessarily for anyone else.)
Oracle has timestamp literals which you can use instead of to_timestamp(), but unfortunately they don't allow the "T":
insert into perflog(REQ_TIME) VALUES(TIMESTAMP '2020-07-27T23:33:41.427330');
ORA-01861: literal does not match format string
and you can't remove it within the call (e.g. with replace) as that then isn't a literal; so you would have to change the "T" to a space externally:
insert into perflog(REQ_TIME) VALUES(TIMESTAMP '2020-07-27 23:33:41.427330');
If you're stuck with a string with that format then use an explicitly to_timestamp() call to convert your string to the data type you want, supplying the matching format mask, including a character-literal `"T"':
insert into perflog(REQ_TIME)
VALUES(TO_TIMESTAMP('2020-07-27T23:33:41.427330', 'YYYY-MM-DD"T"HH24:MI:SS.FF6'));
db<>fiddle
It's worth noting that timestamps (and dates) do not have a specific human-readable format when stored in the database. Oracle uses one of several internal representations, depending on the flavour of datetime you're using. Your client, IDE or application will format that as a readable string when you query the data, usually using your session NLS settings again. To get the data back as a string in a specific format you should use to_char() with the appropriate format supplied.
db<>fiddle with some examples.
Use a timestamp literal using a space character instead of a T:
insert into perflog(REQ_TIME) VALUES( TIMESTAMP '2020-07-27 23:33:41.427330');
db<>fiddle

Add a generated column in postgres using to_char and a date

I have a date column created_at in a postgres table. I have a long running query that groups the data by week, so part of the query is
(SELECT TO_CHAR(score.created_at, 'IYYY-IW') AS week,
customer_id, AVG(score) AS avg_week_score FROM score
This is inefficient, and I would like to use the new generated column functionality in Postgres 12 to rather store a text version of each date as it is inserted.
I tried to run
alter table score add column gen_week text generated always as (TO_CHAR(created_at, 'IYYY-IW')) stored;
but got
ERROR: generation expression is not immutable
I guessed that this has to do with the fact that created_at is dependent on locale, and especially timezone, so I tried
alter table score add column week_gen text generated always as (TO_CHAR(created_at::timestamptz at time zone 'UTC', 'IYYY-IW')) stored;
to make the timezone explicit, but this produces the same error.
Is there a way to make to_char behave immutably with a date object, or do I have to define my own immutable function based on to_char?
The problem is that to_char() could take modifies from the locale -- and functions either are or are not immutable.
You can do the same thing using extract():
alter table score add column gen_week text generated always as
((extract(isoyear from created_at) * 100 + extract(week from created_at))::text ) stored;
Actually, the above doesn't put the hyphen in, so:
alter table score add column gen_week text generated always as
(extract(isoyear from created_at)::text || '-' || lpad(extract(week from created_at)::text, 2, '0')) stored;
Here is a db<>fiddle.

Create a table with default values that use the primary key column in a function

I just dabble in Oracle, so go easy on me if this is basic...I've searched extensively for an answer and the answer doesn't exist, it's not possible, or my searches haven't used the correct terminology.
Anyway, I'm writing DDL that will create a date dimension table. I've got a date key, which is the primary key for the table, that I'm trying to propagate across all the other columns in the table to use as the default value for those columns when a new value is added to the date key column.
Here's a snippet of what I've tried so far, but gave me the error "ORA-00984: column not allowed here":
CREATE TABLE DATE_DIM
(
DATE_KEY DATE NOT NULL,
CONSTRAINT DATE_KEY_PK PRIMARY KEY (DATE_KEY),
DAY_NUMBER NUMBER(2) DEFAULT TO_NUMBER(TO_CHAR(DATE_KEY,'DD')),
DAY_NAME VARCHAR(36) DEFAULT TO_CHAR(DATE_KEY,'Day')
)
If I substitute SYSDATE for DATE_KEY, I don't return any errors. But, assuming everything that I'm conceptualizing comes to fruition, I will need to insert a more specific date than sysdate...and just for clarity's sake, no other columns would be update-able.
Ultimately, this idea would expand to include other tables with different types of data, so I understand if you're wondering why I wouldn't just create a large table full of dates using a script, Excel, etc. I just thought the date dimension provided a good example to explain what I'm trying to achieve.
Thanks for your help.
You can use virtual columns for something like this. These are calculated on the fly based on the expression that defines the columns:
CREATE TABLE date_dim (
Date_Key DATE NOT NULL,
Day_Number AS (EXTRACT(DAY FROM Date_Key)),
Day_Name AS (TO_CHAR(Date_Key, 'FMDay'))
)
Some notes:
I've generally found it easier to not specify the type for a virtual column. You can, but often something weird happens. Your table was no exception: Oracle complained about the VARCHAR2(36) for Day_Name, saying it should be at least VARCHAR2(75).
I used the EXTRACT function to get the day number because I think it's more descriptive for this; you should use whatever you're most comfortable with.
I used the format string FMDay for Day_Name because the Day format code returns a string that's the length of the longest day name (Wednesday in English), so all other day names will have trailing spaces. The FM format code does a lot of useful things, and one of them is to trim trailing spaces.