sql >> Databasteknik >  >> RDS >> PostgreSQL

Hur får jag en kolumn med på varandra följande, ökande siffror, utan att några siffror saknas?

Har anpassat detta från ett tidigare svar. Den här typen av saker händer ofta när program vill ha en tabbordning för variabler (läs:poster i en EAV-modell) , som också kan vara (en del av) en alternativ nyckel.

  • priority fältet måste hållas i följd. [detta är tabbordningen ]
  • på INSERT:alla poster med prioritet>=den nya posten bör ha sina prioriteringar ökade
  • på liknande sätt:på DELETE -> minskas
  • om en posts prioritet ändras av en UPPDATERING, bör posterna mellan det gamla och det nya prioritetsvärdet ha sina prioriteter flyttade uppåt eller nedåt.
  • för att undvika rekursiv triggeranrop:
    • de triggerbaserade uppdateringarna vänder på flipflag av alla rekord de rör.
    • Och de testar för old.flipflag=new.flipflag för att upptäcka riktiga uppdateringar. (de som inte orsakas av en trigger)
        -- 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;

Resultat:

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)



  1. Lägg till främmande nyckel till befintlig tabell

  2. Vad är SQL-operatörer och hur fungerar de?

  3. Databasdesign för att skapa tabeller i farten

  4. SQLite kan inte öppna databasfil (kod 14) vid frekvent SELECT-fråga