Как реализовать приоритеты в SQL (postgres)

Я пишу какое-то программное обеспечение, которое требует хранения элементов в базе данных, элементы должны иметь «приоритет», поэтому мы получаем

    ID  |  Name        |  Priority
--------+--------------+----------
    1   | Pear         |  4
    2   | Apple        |  2
    3   | Orange       |  1
    4   | Banana       |  3

Итак, теперь приоритетным фруктом является апельсин, затем яблоко, затем банан, затем груша.

Теперь я хочу сделать грушу приоритетом номер один, поэтому груша, апельсин, яблоко, банан. Таблица будет выглядеть так:

    ID  |  Name        |  Priority
--------+--------------+----------
    1   | Pear         |  1
    2   | Apple        |  3
    3   | Orange       |  2
    4   | Banana       |  4

Каков наилучший способ добиться этого с помощью PHP и Postgres. Учитывая, что в таблице не будет больше 12-13 элементов, я подумал о том, чтобы ВЫБРАТЬ всю таблицу и переписать приоритеты, прежде чем ОБНОВЛЯТЬ все обратно.

* Важно *

Приоритеты могут быть изменены в любом порядке, поэтому приоритет 7 может быть установлен на приоритет 3 (таким образом перемещая все ниже приоритета 3 на ступеньку ниже), и нам нужно закрыть пробел, который оставил элемент с приоритетом 7, который был перемещен в приоритет 3. в списке приоритетов.


person Tony Million    schedule 12.01.2012    source источник


Ответы (2)


Хорошо, вот моя попытка сохранить уникальные и последовательные приоритеты. Реализуется функцией триггер+. Трудная часть состоит в том, чтобы избежать бесконечной рекурсии, которая может возникнуть в результате обновлений внутри триггера. Это решается флажком грязи/цвета, который должен быть помещен внутри стола. Его значение не важно; только его изменение.

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp ;
SET search_path=tmp;

CREATE TABLE fruits
        ( id INTEGER NOT NULL PRIMARY KEY
        , zname varchar NOT NULL
        , priority INTEGER NOT NULL
        , flipflag boolean NOT NULL default false
        , 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)
        ;

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 TRIGGER shift_priority
        AFTER UPDATE OF priority ON fruits
        FOR EACH ROW
        WHEN (OLD.flipflag = NEW.flipflag AND OLD.priority <> NEW.priority)
        EXECUTE PROCEDURE shift_priority()
        ;

UPDATE fruits
SET priority = 1
WHERE id=1;

РЕЗУЛЬТАТЫ:

SELECT * FROM fruits ORDER BY id;
NOTICE:  drop cascades to 2 other objects
DETAIL:  drop cascades to table tmp.fruits
drop cascades to function tmp.shift_priority()
DROP SCHEMA
CREATE SCHEMA
SET
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "fruits_pkey" for table "fruits"
NOTICE:  CREATE TABLE / UNIQUE will create implicit index "unique_priority" for table "fruits"
CREATE TABLE
INSERT 0 4
CREATE FUNCTION
CREATE TRIGGER
UPDATE 1
 id | zname  | priority | flipflag 
----+--------+----------+----------
  1 | Pear   |        1 | f
  2 | Apple  |        3 | t
  3 | Orange |        2 | t
  4 | Banana |        4 | t
(4 rows)
person wildplasser    schedule 28.07.2012

Два обновления в одной транзакции должны нормально работать на такой маленькой таблице.

create temp table priorities (
  id integer primary key,
  name varchar(15) not null,
  priority integer not null check (priority > 0 and priority < 100)
);

insert into priorities values 
(1,'Pear',4),
(2,'Apple',2),
(3,'Orange',1),
(4,'Banana',3);

-- Make Pear priority 1.
begin;
update priorities 
set priority = priority + 1
-- The value below is the priority you're aiming for. You want
-- Pear to be #1, so you use ">= 1".
where priority >= 1;

update priorities
set priority = 1 where name = 'Pear';
commit;

Для удобства вы можете обернуть это в хранимую процедуру.

person Mike Sherrill 'Cat Recall'    schedule 12.01.2012
comment
+1 — хотя Postgres поддерживает только функции, а не хранимые процедуры. - person Mike Christensen; 12.01.2012
comment
А что, если я переместил элемент с приоритетом 3 в приоритет 2 — не оставит ли это пробел в приоритетах? - person Tony Million; 12.01.2012
comment
Я только что уточнил последнюю часть вопроса. - person Tony Million; 12.01.2012
comment
Нет, это не оставит пробел, если вы используете правильный приоритет и имя. - person Mike Sherrill 'Cat Recall'; 12.01.2012