簡體   English   中英

PostgreSQL插入如果不存在

[英]Postgresql insert if does not exist

我有以下查詢

INSERT INTO address (house_number, street, city_id)
    values(11, 'test st', (select id from city where LOWER(city) = LOWER('somecity')))

無論如何,如果在city中不存在“ somecity”,那么是否在city表中插入“ somecity”,然后在插入之后,它將返回插入行的ID?

我確實找到了這個答案,說可以使用upsert實現此目的

https://stackoverflow.com/a/31742830/492015

但是我找不到一個如果select不返回該行插入的示例。

除了嵌套INSERT,您還可以使用CTE一個接一個地執行INSERT,一個語句即可

WITH tmp AS (
    INSERT INTO test_city (city) VALUES ('somecity')
    ON CONFLICT (lower(city)) DO UPDATE SET city = excluded.city
    RETURNING id, city
)
INSERT INTO test_address (house_number, street, city_id)
SELECT house_number, street, id
FROM (VALUES (11, 'test st', 'somecity')) val (house_number, street, city)
LEFT JOIN tmp USING (city)
RETURNING *

使用此設置:

DROP TABLE IF EXISTS test_address;
DROP TABLE IF EXISTS test_city;
CREATE TABLE test_address (
    house_number int
    , street text
    , city_id int
    );
CREATE TABLE test_city (
    id int GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY
    , city text 
    );
CREATE UNIQUE INDEX test_city_uniq_idx ON test_city USING btree (lower(city));
INSERT INTO test_city (city) VALUES ('Somecity');

和上面的INSERT一起,查詢

SELECT * FROM test_address;

產量

| house_number | street  | city_id |
|--------------+---------+---------|
|           11 | test st |       1 |

SELECT * FROM test_city;

產量

| id | city     |
|----+----------|
|  1 | somecity |

請注意,CTE取代了

(select id from city where LOWER(city) = LOWER('somecity'))

用INSERT .. ON CONFLICT .. DO UPDATE語句:

INSERT INTO test_city (city) VALUES ('somecity')
ON CONFLICT (lower(city)) DO UPDATE SET city = excluded.city
RETURNING id, city

我使用DO UPDATE而不是DO NOTHING以便RETURNING id, city將始終返回某些內容。 如果使用DO NOTHING ,則在發生沖突時不返回任何內容。

但是請注意,使用的結果, city = excluded.city是,原來的'Somecity'被替換為'somecity' 我不確定您是否會接受這種行為,但是不幸的是,在發生沖突時,我還沒有弄清楚如何執行任何操作,但同時返回了idcity


您可能會遇到上述解決方案的另一個問題是,我在lower(city)上使用了唯一索引:

CREATE UNIQUE INDEX test_city_uniq_idx ON test_city USING btree (lower(city));

這使您可以在INSERT語句中使用相同的條件:

INSERT ... ON CONFLICT (lower(city))

替換出現在SELECT語句中的條件LOWER(city) = LOWER('somecity') 它產生了預期的效果,但是要權衡的是,現在您在(lower(city))上具有唯一索引。


關於如何插入兩個以上表的后續問題

您可以將多個CTE鏈接在一起 ,后續的CTE甚至可以引用先前的CTE。 例如,

CREATE UNIQUE INDEX city_uniq_idx ON city USING btree (lower(city));
CREATE UNIQUE INDEX state_uniq_idx ON state USING btree (lower(state_code));

WITH tmpcity AS 
(
   INSERT INTO
      city (city) 
   VALUES
      (
         'Miami'
      )
      ON CONFLICT (lower(city)) DO 
      UPDATE
      SET
         city = excluded.city RETURNING id, city
)
, tmpstate as 
(
   INSERT INTO
      state (state_code) 
   VALUES
      (
         'FL'
      )
      ON CONFLICT (lower(state_code)) DO 
      UPDATE
      SET
         state_code = excluded.state_code RETURNING id, state_code
)
INSERT INTO
   address (house_number, street, city_id, state_id) 
   SELECT
      house_number,
      street,
      tmpcity.id,
      tmpstate.id 
   FROM
      (
      VALUES
         (
            12,
            'fake st.',
            'Miami',
            'FL'
         )
      )
      val (house_number, street, city, state_code) 
      LEFT JOIN
         tmpcity USING (city) 
      LEFT JOIN
         tmpstate USING (state_code)
         ON CONFLICT (street) DO NOTHING

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM