【问题标题】:How to implement priorities in SQL (postgres)如何在 SQL (postgres) 中实现优先级
【发布时间】:2012-02-08 22:58:46
【问题描述】:

我正在编写一些需要将项目存储在数据库中的软件,这些项目需要有一个“优先级”,所以我们最终得到了

    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 已离开优先级列表。

【问题讨论】:

    标签: php postgresql constraints redbean candidate-key


    【解决方案1】:

    好的,这是我尝试保持优先级的唯一性和连续性。由触发器+函数实现。困难的部分是避免因触发器内部的更新而导致的无限递归。这可以通过必须放置在桌子内的污垢/颜色标志来解决。它的价值并不重要;只是它的变化。

    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)
    

    【讨论】:

      【解决方案2】:

      单个事务中的两个更新应该可以在这么小的表上正常工作。

      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;
      

      为方便起见,您可以将其包装在存储过程中。

      【讨论】:

      • +1 - 虽然 Postgres 只支持函数,不支持存储过程。
      • 如果我将优先级为 3 的项目移至优先级 2 会怎样 - 这不会在优先级中留下空白吗?
      • 我刚刚澄清了问题的最后一部分。
      • 不,如果您使用正确的优先级和名称,它不会留下空白。
      猜你喜欢
      • 2012-04-15
      • 2011-12-06
      • 2020-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多