Use multiple conflict_target in ON CONFLICT clause

前端 未结 9 758
傲寒
傲寒 2020-11-29 02:25

I have two columns in table col1, col2, they both are unique indexed (col1 is unique and so is col2).

I need at insert into this table, u

相关标签:
9条回答
  • 2020-11-29 02:33

    Kind of hacky but I solved this by concatenating the two values from col1 and col2 into a new column, col3 (kind of like an index of the two) and compared against that. This only works if you need it to match BOTH col1 and col2.

    INSERT INTO table
    ...
    ON CONFLICT ( col3 ) 
    DO UPDATE 
    SET 
    -- update needed columns here
    

    Where col3 = the concatenation of the values from col1 and col2.

    0 讨论(0)
  • 2020-11-29 02:35

    If you are using postgres 9.5, you can use the EXCLUDED space.

    Example taken from What's new in PostgreSQL 9.5:

    INSERT INTO user_logins (username, logins)
    VALUES ('Naomi',1),('James',1)
    ON CONFLICT (username)
    DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins;
    
    0 讨论(0)
  • 2020-11-29 02:36

    ON CONFLICT is very clumsy solution, run

    UPDATE dupes SET key1=$1, key2=$2 where key3=$3    
    if rowcount > 0    
      INSERT dupes (key1, key2, key3) values ($1,$2,$3);
    

    works on Oracle, Postgres and all other database

    0 讨论(0)
  • 2020-11-29 02:41

    Vlad got the right idea.

    First you have to create a table unique constraint on the columns col1, col2 Then once you do that you can do the following:

    INSERT INTO dupes values(3,2,'c') 
    ON CONFLICT ON CONSTRAINT dupes_pkey 
    DO UPDATE SET col3 = 'c', col2 = 2
    
    0 讨论(0)
  • 2020-11-29 02:43

    ON CONFLICT requires a unique index* to do the conflict detection. So you just need to create a unique index on both columns:

    t=# create table t (id integer, a text, b text);
    CREATE TABLE
    t=# create unique index idx_t_id_a on t (id, a);
    CREATE INDEX
    t=# insert into t values (1, 'a', 'foo');
    INSERT 0 1
    t=# insert into t values (1, 'a', 'bar') on conflict (id, a) do update set b = 'bar';
    INSERT 0 1
    t=# select * from t;
     id | a |  b  
    ----+---+-----
      1 | a | bar
    

    * In addition to unique indexes, you can also use exclusion constraints. These are a bit more general than unique constraints. Suppose your table had columns for id and valid_time (and valid_time is a tsrange), and you wanted to allow duplicate ids, but not for overlapping time periods. A unique constraint won't help you, but with an exclusion constraint you can say "exclude new records if their id equals an old id and also their valid_time overlaps its valid_time."

    0 讨论(0)
  • 2020-11-29 02:47

    You can typically (I would think) generate a statement with only one on conflict that specifies the one and only constraint that is of relevance, for the thing you are inserting.

    Because typically, only one constraint is the "relevant" one, at a time. (If many, then I'm wondering if something is weird / oddly-designed, hmm.)

    Example:
    (License: Not CC0, only CC-By)

    // there're these unique constraints:
    //   unique (site_id, people_id, page_id)
    //   unique (site_id, people_id, pages_in_whole_site)
    //   unique (site_id, people_id, pages_in_category_id)
    // and only *one* of page-id, category-id, whole-site-true/false
    // can be specified. So only one constraint is "active", at a time.
    
    val thingColumnName = thingColumnName(notfificationPreference)
    
    val insertStatement = s"""
      insert into page_notf_prefs (
        site_id,
        people_id,
        notf_level,
        page_id,
        pages_in_whole_site,
        pages_in_category_id)
      values (?, ?, ?, ?, ?, ?)
      -- There can be only one on-conflict clause.
      on conflict (site_id, people_id, $thingColumnName)   <—— look
      do update set
        notf_level = excluded.notf_level
      """
    
    val values = List(
      siteId.asAnyRef,
      notfPref.peopleId.asAnyRef,
      notfPref.notfLevel.toInt.asAnyRef,
      // Only one of these is non-null:
      notfPref.pageId.orNullVarchar,
      if (notfPref.wholeSite) true.asAnyRef else NullBoolean,
      notfPref.pagesInCategoryId.orNullInt)
    
    runUpdateSingleRow(insertStatement, values)
    

    And:

    private def thingColumnName(notfPref: PageNotfPref): String =
      if (notfPref.pageId.isDefined)
        "page_id"
      else if (notfPref.pagesInCategoryId.isDefined)
        "pages_in_category_id"
      else if (notfPref.wholeSite)
        "pages_in_whole_site"
      else
        die("TyE2ABK057")
    

    The on conflict clause is dynamically generated, depending on what I'm trying to do. If I'm inserting a notification preference, for a page — then there can be a unique conflict, on the site_id, people_id, page_id constraint. And if I'm configuring notification prefs, for a category — then instead I know that the constraint that can get violated, is site_id, people_id, category_id.

    So I can, and fairly likely you too, in your case?, generate the correct on conflict (... columns ), because I know what I want to do, and then I know which single one of the many unique constraints, is the one that can get violated.

    0 讨论(0)
提交回复
热议问题