I am inserting a null date with a INSERT ... SELECT FROM statement in sql
CREATE TABLE null_date (
id bigserial PRIMARY KEY
, some_date date
);
WITH date_data (some_date) AS (
VALUES (null)
)
INSERT INTO null_date (some_date)
SELECT some_date
FROM date_data;
and it fails with
ERROR: column "some_date" is of type date but expression is of type text
LINE 5: SELECT some_date
^
HINT: You will need to rewrite or cast the expression.
However, if I try to insert it directly, it works
INSERT INTO null_date (some_date)
VALUES (null)
can somebody please help me understand what's happening here? Here is the link to db<>fiddle. Thanks
The problem is that the VALUES statement and consequently the WITH clause will treat the NULL value as type text, because PostgreSQL doesn't know which data type the NULL should be. You don't have that problem with INSERT INTO ... VALUES (...), because here PostgreSQL knows right away that the NULL value with unknown type will be inserted into a certain column, so it will resolve it to the target data type.
In cases where PostgreSQL cannot guess the data type from context, you had better use an explicit type cast:
WITH date_data (some_date) AS (
VALUES (CAST(null AS date))
)
INSERT INTO null_date (some_date)
SELECT some_date
FROM date_data;
PostgreSQL used to behave differently in cases like this, but commit 1e7c4bb0049 changed that in 2017. Read the commit message for an explanation.
Related
I have a task to create a Liquibase migration to change a value affext in table trp_order_sold, which is right now int8, to varchar (or any other text type if it's more likely to be possible).
The script I made is following:
ALTER TABLE public.trp_order_sold
ALTER COLUMN affext SET DATA TYPE VARCHAR
USING affext::varchar;
I expected that USING affext::text; part is gonna work as a converter, however with or without it I am getting this error:
ERROR: operator does not exist: varchar >= integer
Hint: No operator matches the given name and argument types. You might need to add explicit type casts.
Any hints on what I'm doing wrong? Also I am writing a PostgreSQL script but a working XML equivalent would be fine for me as well.
These would most typically use or depend on your column:
a generated column
a trigger
a trigger's when condition
a view or a rule
a check constraint
In my test (online demo) only the last one leads to the error you showed:
create table test_table(col1 int);
--CREATE TABLE
alter table test_table add constraint test_constraint check (col1 >= 1);
--ALTER TABLE
alter table test_table alter column col1 type text using col1::text;
--ERROR: operator does not exist: text >= integer
--HINT: No operator matches the given name and argument types. You might need to add explicit type casts.
You'll have to check the constraints on your table with \d+ command in psql, or by querying the system tables:
SELECT con.*
FROM pg_catalog.pg_constraint con
INNER JOIN pg_catalog.pg_class rel
ON rel.oid = con.conrelid
INNER JOIN pg_catalog.pg_namespace nsp
ON nsp.oid = connamespace
WHERE nsp.nspname = 'your_table_schema'
AND rel.relname = 'your_table_name';
Then you will need to drop the constraint causing the problem and build a new one to work with your new data type.
Since integer 20 goes before integer 100, but text '20' goes after text '100', if you plan to keep the old ordering behaviour you'd need this type of cast:
case when affext<0 then '-' else '0' end||lpad(ltrim(affext::text,'-'),10,'0')
and then make sure new incoming affext values are cast accordingly in an insert and update trigger. Or use a numeric ICU collation similar to this.
I am using a temp table to hold some data while I tidy up a database table. Narrowed down, it goes something like this:
CREATE TEMP TABLE fw_temp AS SELECT time, version, device_id FROM fw_status;
DELETE FROM fw_status;
INSERT INTO fw_status SELECT * FROM fw_temp
On my INSERT statement I get some.. interesting behaviour when the values in the device_id column are null:
SQL Error [42804]: ERROR: column "device_id" is of type bigint but expression is of type text
Hint: You will need to rewrite or cast the expression.
As the error message may lead on, device_id is indeed bigint type in the table, but appears to have lost the type when we select from the temp table.
Making a general select,
CREATE TEMP TABLE fw_temp AS SELECT * FROM fw_status;
DELETE FROM fw_status;
INSERT INTO fw_status SELECT * FROM fw_temp
interestingly does not cause this error.
Now, the real temp table is a bit more complex, so sadly going with SELECT * is not an option. How can I go about this error?
Changing to
CREATE TEMP TABLE fw_temp AS SELECT time, version, device_id FROM fw_status;
DELETE FROM fw_status;
INSERT INTO fw_status (time, version, device_id) SELECT time, version, device_id FROM fw_temp
resolved the issue. Apparently, you can mix general select and general insert or specific select and specific insert, but not general select and specific insert. I am still puzzled as to what is going on. Any enlightenment would be appreciated
I am trying to insert into a numeric column a decimal but I keep getting an error.
Below is my statement
INSERT INTO blse VALUES (2082.7, 'Total Nonfarm' ,'Alabama','01/31/2020');
it basically says i need to cast this statement. I do not know what I am doing wrong.. I am beginner taking this class.
and this is the error:
It is highly recommended that you specify the columns in an INSERT statement:
INSERT INTO tab (col1, col2, col3)
VALUES (val1, val2, val3);
That way, you can be certain what value is inserted where.
Since you didn't do that, the first value in the VALUES clause gets inserted into the first table column, which is of type date. That causes the error you observe.
create table Department
(Dname varchar(255) NOT NULL, Dnumber int NOT NULL PRIMARY KEY, Mgr_SSN char(9) NOT NULL, Mgr_start_Date DATE);
insert into Department values('HR', '1', '11001', '2012-04-05 10:15:00');
I am getting the error "not valid month".
Should we define date format when we create the table?
I am using Oracle11g.
When you have a DATE column, you should always insert a DATE, not a VARCHAR2. Relying on implicit casting to correctly convert the string is a bad idea-- it is very easy for different sessions to have different NLS settings and, thus, to do the implicit conversion differently (either resulting in a different DATE or an error). The easiest way to do that is to use the to_date function.
insert into Department( dname,
dnumber,
mgr_ssn,
mgr_start_date )
values('HR',
1,
'11001',
to_date( '2012-04-05 10:15:00', 'yyyy-mm-dd hh24:mi:ss') );
I also modified the statement to list the columns, which is generally a good practice since it ensures that you don't have to look up the physical order of columns in the table every time and since it allows the INSERT statement to work in the future if you add new columns to the table. Since dnumber is a NUMBER, I also changed the INSERT statement to insert a number rather than inserting a string (again, don't rely on implicit conversion if there is no need to do so). I did not correct the apparent bug that you have a CHAR(9) column representing a social security number for which you are inserting a 5 character string.
I'm trying to create an index on the cast of a varchar column to date. I'm doing something like this:
CREATE INDEX date_index ON table_name (CAST(varchar_column AS DATE));
I'm getting the error: functions in index expression must be marked IMMUTABLE But I don't get why, the cast to date doesn't depends on the timezone or something like that (which makes a cast to timestamp with time zone give this error).
Any help?
Your first error was to store a date as a varchar column. You should not do that.
The proper fix for your problem is to convert the column to a real date column.
Now I'm pretty sure the answer to that statement is "I didn't design the database and I cannot change it", so here is a workaround:
CAST and to_char() are not immutable because they can return different values for the same input value depending on the current session's settings.
If you know you have a consistent format of all values in the table (which - if you had - would mean you can convert the column to a real date column) then you can create your own function that converts a varchar to a date and is marked as immutable.
create or replace function fix_bad_datatype(the_date varchar)
returns date
language sql
immutable
as
$body$
select to_date(the_date, 'yyyy-mm-dd');
$body$
ROWS 1
/
With that definition you can create an index on the expression:
CREATE INDEX date_index ON table_name (fix_bad_datatype(varchar_column));
But you have to use exactly that function call in your query so that Postgres uses it:
select *
from foo
where fix_bad_datatype(varchar_column) < current_date;
Note that this approach will fail badly if you have just one "illegal" value in your varchar column. The only sensible solution is to store dates as dates,
Please provide the database version, table ddl, and some example data.
Would making your own immutable function do what you want, like this? Also look into creating a new cast in the docs and see if that does anything for you.
create table emp2 (emp2_id integer, hire_date VARCHAR(100));
insert into emp2(hire_date)
select now();
select cast(hire_date as DATE)
from emp2
CREATE FUNCTION my_date_cast(VARCHAR) RETURNS DATE
AS 'select cast($1 as DATE)'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
CREATE INDEX idx_emp2_hire_date ON emp2 (my_date_cast(hire_date));