If an ALTER TABLE to change the columns' default values isn't an option, you can join the values to be inserted and the default values in a SELECT statement and redirect it to the INSERT, e.g.
INSERT INTO users (email,is_active, created_at, updated_at)
SELECT email,is_active, created_at, updated_at
FROM
(VALUES ('Jimmy Smith', '[email protected]'),
('Dave Jones', '[email protected]')
) insert_values (name,email),
(VALUES (true, current_timestamp, current_timestamp))
default_values (is_active, created_at, updated_at);
Despite of being less bulky, I see no real improvement in this approach. I would still prefer to, if possible, alter the columns' default values, or just repeat the values in every INSERT record:
INSERT INTO users (email,is_active, created_at, updated_at) VALUES
('[email protected]',TRUE, current_timestamp, current_timestamp),
('[email protected]',TRUE, current_timestamp, current_timestamp)
If the amount of columns used in the INSERT vary and you're not sure when a NULL might come in, use COALESCE with the default value in the SELECT:
INSERT INTO users (email,is_active, created_at, updated_at)
SELECT
email,
COALESCE(insert_values.is_active::boolean, default_values.is_active),
COALESCE(insert_values.created_at::timestamp, default_values.created_at),
COALESCE(insert_values.updated_at::timestamp, default_values.updated_at)
FROM
(VALUES ('[email protected]',false,NULL,NULL), -- overriding 'is_active'
('[email protected]',false,NULL,NULL) -- overriding 'is_active'
) insert_values (email,is_active, created_at, updated_at),
(VALUES (true, current_timestamp, current_timestamp))
default_values (is_active, created_at, updated_at);
Demo: db<>fiddle
namehas no column in the target table