Combining INSERT statements in a data-modifying CTE with a CASE expression

后端 未结 1 869
栀梦
栀梦 2020-12-21 11:52

My question is some kind of extension to Erwin Brandstetter\'s excellent answer in this thread on the correct use of WITH.

My old query looks like this:

相关标签:
1条回答
  • 2020-12-21 12:16

    You cannot nest INSERT statements in a CASE expression. Deriving from what I see, this completely different approach should do it:

    Assumptions

    • You don't actually need the outer SELECT.

    • dm_name / rm_name are defined unique in dm / rm and not empty (<> ''). You should have a CHECK constraint to make sure.

    • Column default for both d_id and r_id in z are NULL (default).

    dm_name and rm_name mutually exclusive

    If both are never present at the same time.

    WITH d1 AS (
       INSERT INTO d (dm_id)
       SELECT dm.dm_id 
       FROM   import
       JOIN   dm USING (dm_name)
       RETURNING d_id
       )
    , r1 AS (
       INSERT INTO r (rm_id)
       SELECT rm.rm_id 
       FROM   import
       JOIN   rm USING (rm_name)
       RETURNING r_id
       )
    , z1 AS (
       INSERT INTO z (d_id, r_id)
       SELECT d_id, r_id
       FROM d1 FULL JOIN r1 ON FALSE
       RETURNING z_id
       )
    INSERT INTO port (z_id)
    SELECT z_id
    FROM   z1;
    

    The FULL JOIN .. ON FALSE produces a derived table with all rows from d1 and r1 appended with NULL for the respective other column (no overlap between the two). So we just need one INSERT instead of two. Minor optimization.

    dm_name and rm_name can coexist

    WITH i AS (
       SELECT dm.dm_id, rm.rm_id
       FROM   import
       LEFT   JOIN dm USING (dm_name)
       LEFT   JOIN rm USING (rm_name)
       )
    , d1 AS (
       INSERT INTO d (dm_id)
       SELECT dm_id FROM i WHERE dm_id IS NOT NULL
       RETURNING dm_id, d_id
       )
    , r1 AS (
       INSERT INTO r (rm_id)
       SELECT rm_id FROM i WHERE rm_id IS NOT NULL
       RETURNING rm_id, r_id
       )
    , z1 AS (
       INSERT INTO z (d_id, r_id)
       SELECT d1.d_id, r1.r_id
       FROM   i
       LEFT   JOIN d1 USING (dm_id)
       LEFT   JOIN r1 USING (rm_id)
       WHERE  d1.dm_id IS NOT NULL OR
              r1.rm_id IS NOT NULL
       RETURNING z_id
       )
    INSERT INTO port (z_id)
    SELECT z_id FROM z1;
    

    Notes

    Both versions also work if neither exists.

    INSERT inserts nothing if the SELECT does not returns row(s).

    If you have to deal with concurrent write access that could conflict with this operation the quick fix would be to lock involved tables before you run this statement in the same transaction.

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