简体   繁体   中英

Multi-row conditional insert with DEFAULT values

Here is a simplified version of a PostgreSQL query I have, to insert multiple rows into a table:

INSERT INTO my_table (a, b, datasource)
VALUES
  ('foo', 'bar', 'my source'),
  ('foo', DEFAULT, 'my source'),
  (DEFAULT, 'bar', 'my source');

Notice that a DEFAULT value could appear in either column a or b .

That works fine, however I only want to insert rows that meet some criteria, and additionally it would be nice to remove the duplication for values that are constant for all the rows that are being inserted.

So I try to change it to something like this:

INSERT INTO my_table (a, b, datasource)
SELECT v.*, 'my source'
FROM (VALUES ('foo', 'bar'), ('foo', DEFAULT), (DEFAULT, 'bar')) AS v (a, b)
WHERE v.a = 'foo';

This results in the error: DEFAULT is not allowed in this context .

How do I work around this?

It might be worth noting that in reality, the WHERE condition is actually complex and involves other tables. Also there are several thousand rows to insert at a time, with the value expressions being programmatically generated. It is possible to make it so that the literal default values for the table are output instead of DEFAULT , however that requires hardcoding them outside the database, which I want to avoid (because if I change them in the DB and forget to update the hardcoded values, it will cause issues).

Since I couldn't find anything more elegant (as evidenced by the current lack of other answers) I ended up using the suggestion to insert the values into a temporary table first. PostgreSQL makes it really easy to create a "clone" table ( using the LIKE keyword ) with the same default values but without some of the other unnecessary stuff:

CREATE TEMPORARY TABLE temp_table (
  LIKE my_table INCLUDING DEFAULTS
);

INSERT INTO temp_table (a, b)
VALUES
  ('foo', 'bar'),
  ('foo', DEFAULT),
  (DEFAULT, 'bar');

Then it's just a matter of using the temp table in place of the VALUES clause from before:

INSERT INTO my_table (a, b, datasource)
SELECT a, b, 'my source'
FROM temp_table
WHERE a = 'foo';

One small caveat is that you can't avoid NOT NULL constraints from being copied to the temporary table. For example, if the datasource column was NOT NULL (and didn't have a default value), I would have to add the value (or a placeholder) in the first INSERT query, or drop the column.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM