postgresqltriggerstabbing

How do I get a column with consecutive, increasing numbers, without having any numbers missing?


Possible Duplicates:
How to show row numbers in PostgreSQL query?
Resequencing a column with identifier in Postgresql
PostgreSQL Record Reordering using Update with a Sub-Select

I am just asking if in PostgreSQL exist such possibility that if I have 5 rows and in one column there are numbers 1, 2, 3, 4, 5 and in those column is not primary key if I delete say tird row that postgreSQL re enumerate this column so I can have 1, 2, 3, 4 instead of 1, 2, 4, 5?


Solution

  • Adapted this from a previous answer. This kind of stuff happens often when applications want a tabbing-order for variables (read: records in an EAV model) , which could also be (part of) an alternate key.


            -- Make some data
    DROP SCHEMA tmp CASCADE;
    CREATE SCHEMA tmp ;
    SET search_path=tmp;
    
    CREATE TABLE fruits
            ( id INTEGER NOT NULL PRIMARY KEY
            , priority INTEGER NOT NULL
            , flipflag boolean NOT NULL default false
            , zname varchar NOT NULL
            , CONSTRAINT unique_priority UNIQUE (priority) DEFERRABLE INITIALLY DEFERRED
            );
    INSERT INTO fruits(id,zname,priority) VALUES
     (1  , 'Pear' ,4)
    ,(2  , 'Apple' ,2)
    ,(3  , 'Orange' ,1)
    ,(4  , 'Banana' ,3)
    ,(5  , 'Peach' ,5)
            ;
    
            -- Trigger functions for Insert/update/delete
    CREATE function shift_priority()
    RETURNS TRIGGER AS $body$
    
    BEGIN
            UPDATE fruits fr
            SET priority = priority +1
            , flipflag = NOT flipflag       -- alternating bit protocol ;-)
            WHERE NEW.priority < OLD.priority
            AND OLD.flipflag = NEW.flipflag -- redundant condition
            AND fr.priority >= NEW.priority
            AND fr.priority < OLD.priority
            AND fr.id <> NEW.id             -- exlude the initiating row
                    ;
            UPDATE fruits fr
            SET priority = priority -1
            , flipflag = NOT flipflag
            WHERE NEW.priority > OLD.priority
            AND OLD.flipflag = NEW.flipflag
            AND fr.priority <= NEW.priority
            AND fr.priority > OLD.priority
            AND fr.id <> NEW.id
            ;
            RETURN NEW;
    END;
    
    $body$
    language plpgsql;
    
    CREATE function shift_down_priority()
    RETURNS TRIGGER AS $body$
    
    BEGIN
    
            UPDATE fruits fr
            SET priority = priority -1
            , flipflag = NOT flipflag       -- alternating bit protocol ;-)
            WHERE fr.priority > OLD.priority
                    ;
            RETURN NEW;
    END;
    
    $body$
    language plpgsql;
    
    CREATE function shift_up_priority()
    RETURNS TRIGGER AS $body$
    
    BEGIN
            UPDATE fruits fr
            SET priority = priority +1
            , flipflag = NOT flipflag       -- alternating bit protocol ;-)
            WHERE fr.priority >= NEW.priority
                    ;
            RETURN NEW;
    END;
    
    $body$
    language plpgsql;
    
            -- Triggers for Insert/Update/Delete
    CREATE TRIGGER shift_priority_u
            AFTER UPDATE OF priority ON fruits
            FOR EACH ROW
            WHEN (OLD.flipflag = NEW.flipflag AND OLD.priority <> NEW.priority)
            EXECUTE PROCEDURE shift_priority()
            ;
    CREATE TRIGGER shift_priority_d
            AFTER DELETE ON fruits
            FOR EACH ROW
            EXECUTE PROCEDURE shift_down_priority()
            ;
    CREATE TRIGGER shift_priority_i
            BEFORE INSERT ON fruits
            FOR EACH ROW
            EXECUTE PROCEDURE shift_up_priority()
            ;
    
            -- Do some I/U/D operations
    \echo Pears are Okay
    UPDATE fruits
    SET priority = 1
    WHERE id=1; -- 1,4
    
    SELECT * FROM fruits ORDER BY priority;
    
    \echo dont want bananas
    DELETE FROM fruits WHERE id = 4;
    SELECT * FROM fruits ORDER BY priority;
    
    \echo  We want Kiwis
    INSERT INTO fruits(id,zname,priority) VALUES (4  , 'Kiwi' ,3) ;
    SELECT * FROM fruits ORDER BY priority;
    

    Result:

    Pears are Okay
    UPDATE 1
     id | priority | flipflag | zname  
    ----+----------+----------+--------
      1 |        1 | f        | Pear
      3 |        2 | t        | Orange
      2 |        3 | t        | Apple
      4 |        4 | t        | Banana
      5 |        5 | f        | Peach
    (5 rows)
    
    dont want bananas
    DELETE 1
     id | priority | flipflag | zname  
    ----+----------+----------+--------
      1 |        1 | f        | Pear
      3 |        2 | t        | Orange
      2 |        3 | t        | Apple
      5 |        4 | t        | Peach
    (4 rows)
    
    We want Kiwis
    INSERT 0 1
     id | priority | flipflag | zname  
    ----+----------+----------+--------
      1 |        1 | f        | Pear
      3 |        2 | t        | Orange
      4 |        3 | f        | Kiwi
      2 |        4 | f        | Apple
      5 |        5 | f        | Peach
    (5 rows)