有条件的唯一约束

时间:2018-08-16 05:29:43

标签: sql postgresql

我有一个购物清单应用程序,用户可以在其中将任意数量的项目拖放到清单中。项目以二维顺序组织,其中每个项目都有一个整数位置,即索引。与该域一样,没有两个项目可以具有相同的位置。

当前在下表中表示:

  id    list_id    name    position
------------------------------------
  1        1       apple      0
  2        1       banana     1
  3        1       orange     2
  4        2       milk       3      

list_id是另一个表(lists)的外键。

此表具有以下唯一约束:

ALTER TABLE public.items
    ADD CONSTRAINT unique_position 
      UNIQUE (list_id, position)
      DEFERRABLE INITIALLY IMMEDIATE;

这有助于维护数据完整性,并防止应用程序中的错误(或顽皮的用户)导致在完全相同的位置插入两个项目的情况。 (之所以成为DEFERRABLE的原因是该约束需要推迟到包含多个UPDATE position语句的事务结束为止。)

现在,我想添加一个功能,用户也可以在主列表内创建子列表,并将项目插入到这些子列表中,以类似的方式进行。

我的计划是向原始sublist_id表中添加一个items列:

  id    list_id   name      position    sublist_id   
-----------------------------------------------------
  1        1      apple        0           NULL      
  2        1      banana       1           NULL      
  3        1      orange       2           NULL      
  4        2      milk         0           NULL      
  5        2      chicken      1           NULL      
  6        2      eggs         2           NULL     
  7        2      coke        NULL          1        
  8        2      water       NULL          1        

并引用另一个名为sublists的表:

  id    list_id   name   position_in_list
------------------------------------------
  1        2     drinks         3

然后使用名为sublist_items的第三个表连接两个表:

  id    sublist_id   item_id   position
----------------------------------------
  1        1            7          0
  1        1            8          1

此问题是items表将某些项目位置列为NULL,因为这些项目现在位于子列表中,并且足以跟踪子列表在主列表中的位置,以及项目在子列表中的位置。

问题1:如何更改原始约束,以便将其应用于两个表?换句话说,我仍然希望列表中的每个项目(无论是item还是sublist)都占据唯一的位置,除了来自两个{{1 }}和position表,其中items是通用表。

问题2:是否有更好的方法来组织数据?我知道从技术上讲,我不需要第一个表中的sublist_items列,但我认为我会添加它,以便它允许我编写其他约束,例如“仅当list_id不为空时,才允许sublist_id为空”。

1 个答案:

答案 0 :(得分:0)

这是一个有趣的问题:)

可以有很多方法来做到这一点。按照我的看法,您可以按照以下步骤操作:

  1. 创建一个虚拟 sublist_id(纯粹出于技术目的,用于没有任何sublist_id的商品)。
  2. 规范化您现有的表为:
  

子列表(Sublist_id,名称)(Sublist_id为PK)
  项目(item_id,sublist_id,名称)(将Sublist_id设置为非空外键)   从“子列表”表中获取)(item_id为PK)
   Items_position (item_id,sublist_id,list_id,位置)(您可以添加   检查约束以检查item_id和sublist_id的组合   应该在Items表中可用)(list_id和位置组合的唯一约束)

  1. 现在您可以按以下方式开始填充数据:

子列表:

Sublist_Id         Name
    0              Dummy
    1              Drinks

项目:

Item_Id       Sublist_id      Name      
   1               0          Apple
   2               0          Eggs
   3               1          Water
   4               1          Orange

Items_Position:

 Item_id      Sublist_id     List_id     Position
    1             0             1           1
    3             1             1           0
    2             0             2           1
    4             1             3           1

让我知道以防万一。