PostgreSQL sequence based on another column

后端 未结 8 1378
情话喂你
情话喂你 2020-11-30 01:33

Lets say I have a table as such:

Column   |     Type    |                        Notes
---------+------------ +----------------------------------------------         


        
相关标签:
8条回答
  • 2020-11-30 02:07

    You could use a window function to assign your SEQ values, something like:

    INSERT INTO YourTable
        (ID, SEQ, DATA)
        SELECT ID, ROW_NUMBER() OVER(PARTITION BY ID ORDER BY DATA), DATA
            FROM YourSource
    
    0 讨论(0)
  • 2020-11-30 02:10

    I had the same need to dynamicaly store a tree-like structure, not to add all IDs at once.
    I prefer not use sequence table for each group as there could be thousands of them.
    It run in an intensive multi-processing environment, so it has to be race-condition-proof.
    Here the insert fonction for the 1st level. Other levels follow the same principle.

    Each group as independent non-reusable sequencial IDs, the function receives a group name & sub-group name, gives you the existing ID or creates it & returns the new ID.
    I tried a loop to have a single select, but the code is as long & harder to read.

    CREATE OR REPLACE FUNCTION getOrInsert(myGroupName TEXT, mySubGroupName TEXT)
      RETURNS INT AS
    $BODY$
    DECLARE
       myId INT;
    BEGIN -- 1st try to get it if it already exists
       SELECT id INTO myId FROM myTable
          WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
       IF NOT FOUND THEN
          -- Only 1 session can get it but others can read
          LOCK TABLE myTable IN SHARE ROW EXCLUSIVE MODE; 
          -- 2nd try in case of race condition
          SELECT id INTO myId FROM myTable
             WHERE groupName=myGroupName AND subGroupName=mySubGroupName;
          IF NOT FOUND THEN -- Doesn't exist. Get next ID for this group.
             SELECT COALESCE(MAX(id), 0)+1 INTO myId FROM myTable
                WHERE groupName=myGroupName;
             INSERT INTO myTable (groupName, id, subGroupName)
                VALUES (myGroupName, myId, mySubGroupName);
          END IF;
       END IF;
       RETURN myId;
    END;
    $BODY$
      LANGUAGE plpgsql VOLATILE COST 100;
    

    To try it:

    CREATE TABLE myTable (GroupName TEXT, SubGroupName TEXT, id INT);
    SELECT getOrInsert('groupA', 'subgroupX'); -- Returns 1
    ...
    SELECT * FROM myTable;
     groupname | subgroupname | id 
    -----------+--------------+----
     groupA    | subgroupX    |  1
     groupA    | subgroupY    |  2
     groupA    | subgroupZ    |  3
     groupB    | subgroupY    |  1
    
    0 讨论(0)
  • 2020-11-30 02:12

    If seq reflects (or should reflect) the order in which the rows are inserted, I'd rather use a timestamp that gets populated automatically and generate the sequence number on the fly when selecting the rows using row_number():

    create table some_table
    ( 
      id          integer   not null,
      inserted_at timestamp not null default current_timestamp,
      data text
    );
    

    The to get the seq column, you can do:

    select id,  
           row_number() over (partition by id order by inserted_at) as seq,
           data
    from some_table
    order by id, seq;
    

    The select is however going to be a bit slower compared to using a persisted seq column (especially with an index on id, seq).

    If that becomes a problem you can either look into using a materialized view, or adding the seq column and then updating it on a regular basis (I would not do this in a trigger for performance reasons).

    SQLFiddle example: http://sqlfiddle.com/#!15/db69b/1

    0 讨论(0)
  • 2020-11-30 02:15

    Here's a simple way using standard SQL:

    INSERT INTO mytable (id, seq, data)
    SELECT << your desired ID >>,
           COUNT(*) + 1,
           'Quick brown fox, lorem ipsum, lazy dog, etc etc.'
    FROM mytable
    WHERE id = << your desired ID (same as above) >>;
    

    See SQL Fiddle Demo.

    (If you wanted to be a bit cleverer you could consider creating a trigger to update the row using the same method immediately after an insert.)

    0 讨论(0)
  • 2020-11-30 02:17

    I don't have any postgresql-specific experience, but can you use a subquery in your insert statement? Something like, in Mysqlish,

    INSERT INTO MYTABLE SET 
       ID=4, 
       SEQ=(  SELECT MAX(SEQ)+1 FROM MYTABLE WHERE ID=4  ),
       DATA="Quick brown fox, lorem ipsum, lazy dog, etc etc."
    
    0 讨论(0)
  • 2020-11-30 02:25

    PostgreSQL supports grouped unique columns, as such:

    CREATE TABLE example (
        a integer,
        b integer,
        c integer,
        UNIQUE (a, c)
    );
    

    See PostgreSQL Documentation - Section 5.3.3

    Easy :-)

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