postgresql 在不总是有意义的地方使外键可为空

scyqe7ek  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(2)|浏览(147)

我正试图为这个用例想出一个好的数据库设计
我有一个名为expenses的表,如下所示

id
category_id
item_id

问题

  • item_id列将没有意义,如果费用类别是即。“租金”
  • 只有当类别是i时,它才有意义。"食物"

我想到了两种不同的方法

    • 第一种方法:**

item_id列设置为nullable

    • 第二种方法:**

创建一个名为expense_item的单独表,包含以下列

expense_id
item_id

并从expenses表中删除item_id列。
我不知道我应该采取什么方法,或者是否有更好的方法。我不知道该怎么处理这个案子

eoigrqb6

eoigrqb61#

下面的技巧只是你第一种方法的扩展。允许item_id为NULL,但根据类别验证其null值或非null值。我假设你有一个categories表,如果没有,你创建它无论如何你都需要它。现在创建或添加列item_required boolean。创建一个触发器,用于检查指定类别的item_id是否存在item_required。结果如下:(参见demo here

create table expenses(exp_id   integer  generated always as identity
                                        primary key
                     , cat_id  integer  references categories(cat_id) 
                     , item_id integer
                     ); 
                     
create or replace function validate_category_item()
   returns trigger 
  language plpgsql
as $$
begin 
    if  not exists(select null                              
                     from categories                    
                    where cat_id = new.cat_id             
                      and (   (    new.item_id is null
                               and not requires_item
                              )
                          or  (    new.item_id is not null
                               and requires_item
                              )
                          )
                  )
    then
        raise exception E'*** Item Id Exception. ***\n1. Specified Category requires item and Item missing. \n2. Specified Category does not take Item but Item Present ';
    end if; 
  
    return new;
       
end;
$$;

create trigger cat_item_check_aiur
  after insert or update on expenses
  for each row 
  execute function validate_category_item();

这里的假设是,一个类别要么必须有一个项目,要么不能有一个项目。当然,您需要根据您的确切要求调整触发器。
如果它还不存在,下面是categories table的示例设置。

create table categories( cat_id   integer  generated always as identity
                                           primary key 
                       , name     text
                       , requires_item boolean
                       ); 

insert into categories(name,requires_item)
     values ('Rent', false)
          , ('Food', true); 
      
select * from categories;
uemypmqf

uemypmqf2#

你可以像www.example.com一样对Rent进行分类mint.com。
对于像租金这样的支出,这将是item_idcategory_id可以是HomeHome Expenses。这一类别还可以包括家居装修、家具等项目。

相关问题