如何正确设计状态表

时间:2015-08-03 06:43:24

标签: sql database postgresql

这个问题更多的是关于什么是最好的approch来设计一个包含系统中proccess状态的表。

我有一个处于状态的进程:init, waiting for price approve, sent, accepted,finished

状态可以一步一步前进,也可以一步一步前进,这意味着从init开始,您无法通过sent跳转到waitig for price approve。< / p>

我设计了下表(PostgreSQL,但这对问题并不重要):

CREATE TABLE status
(
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL,
  backid Integer,
  forwardid integer,

  CONSTRAINT id_pkey PRIMARY KEY (id)
  CONSTRAINT backid_fkey FOREIGN KEY (id)
      REFERENCES status(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION
  CONSTRAINT forward_fkey FOREIGN KEY (id)
      REFERENCES status(id) MATCH SIMPLE ON UPDATE NO ACTION ON DELETE NO ACTION
)

基本上意味着:

id name                         backid  forwardid
1  init                                    2
2  waiting for price approve      1        3
3  sent                           2        4
4  accepted                       3        5
5  finished                       4

注意:在1之后,2将无法获得id name backid forwardid 1 init 3 3 sent 1 4 4 accepted 3 5 5 finished 4 。将来我可以删除其中一行,它可能看起来像这样:

Doubly linked list

它与Doubly linked list的行为相同。 我们的目标是,如果将来会有一个新的状态,它将被提交到桌面,我不会在其他任何地方进行更改。当客户要求转发/退回时,它将自动完成到新的&#34;列表&#34;状态。

事情是,这种方式似乎有点不优雅。插入新状态将与插入SQL的算法相同。这看起来似乎太多了,而且$today = date("Ymd"); $args = array( 'post_type' => 'news', 'posts_per_page' => -1, 'meta_key' => 'pinned_news_item', 'meta_value' => '1', 'order' => 'DESC', 'orderby' => $today, ); 和数据库的能力似乎不合适。

你能建议一个更好的方法吗?

1 个答案:

答案 0 :(得分:2)

不需要forwardid,我认为删除它可以让生活更轻松(尽管如果您认为它可以让您的查询更容易,可以保留它)。

但我不会将id列定义为serial。我希望在插入和更新期间控制ID。

假设您的表定义,您可以使用recursive common table expression查询整个层次结构,包括某个排序列:

with recursive status_tree as (
   select id, name, backid, 1 as level
   from status
   where backid is null
   union all 
   select c.id, c.name, c.backid, p.level + 1
   from status c
     join status_tree p on p.id = c.backid
)
select *
from status_tree
order by level;

对于您的样本数据,这将返回:

id | name                      | backid | level
---+---------------------------+--------+------
 1 | init                      |        |     1
 2 | waiting ror price approve |      1 |     2
 3 | sent                      |      2 |     3
 4 | accepted                  |      3 |     4
 5 | finished                  |      4 |     5

插入新状态非常简单(这是手动定义的id使生活更轻松的一点:

-- create a new status that may be set after 'waiting ror price approve'
insert into status (id, name, backid) 
values (6, 'partially sent', 2);

-- now make the current 'sent' status an descendant of the new one
update status
  set backid = 6
where name = 'sent';

以上查询现在返回以下内容:

id | name                      | backid | level
---+---------------------------+--------+------
 1 | init                      |        |     1
 2 | waiting ror price approve |      1 |     2
 6 | partially sent            |      2 |     3
 3 | sent                      |      6 |     4
 4 | accepted                  |      3 |     5
 5 | finished                  |      4 |     6

状态值的顺序不反映id列的“顺序”。

如果需要,可以创建一个返回此信息的视图。

修改

如果您经常找到 next 状态而不是查看所有这些状态,那么您可以翻转逻辑并仅存储下一个逻辑:

CREATE TABLE status
(
  id integer PRIMARY KEY,
  name TEXT NOT NULL,
  next_status Integer references status
);

insert into status(id, name, next_status)
values 
(1,'init', 2),
(2,'waiting ror price approve', 3),
(3,'sent', 4),
(4,'accepted', 5),
(5,'finished', null);

要检索下一个状态,那么只需要一个select语句(不递归)。

您仍然可以使用CTE以正确的顺序获取所有状态值,您只需要反转连接条件:

with recursive status_tree as (
   select id, name, next_status, 1 as level
   from status
   where id = 1
   union all 
   select c.id, c.name, c.next_status, p.level + 1
   from status c
     join status_tree p on p.next_status = c.id
)
select *
from status_tree
order by level;

插入新节点的方式与backid完全相同(只需要“重新链接”“其他”行)

insert into status (id, name, next_status) 
values (6, 'partially sent', 3);

update status
  set next_status = 6
where name = 'waiting ror price approve';