2012-01-12 53 views
0

我正在寫一些需要存儲在數據庫中的項目的軟件,該項目需要有一個「優先級」,所以我們最終如何實現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中。

回答

0

好的,這裏是我試圖保持優先級獨特和連續。由觸發器+函數實現。困難的部分是避免可能由觸發器內的更新導致的無限遞歸。這是由一個污垢/顏色標誌,它必須放在桌子內部解決。它的價值並不重要;只有它的變化。

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) 
1

單個事務中的兩個更新應該可以在這樣一個小表上正常工作。

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; 

爲了方便起見,您可以將其包裝在存儲過程中。

+0

'+ 1' - 儘管Postgres的只支持功能,而不是存儲過程。 – 2012-01-12 17:07:09

+0

如果我將優先級爲3的項目移至優先級2,那麼這會不會在優先級上留下空隙? – 2012-01-12 17:10:47

+0

我剛纔澄清了問題的最後一部分。 – 2012-01-12 17:16:08