添加约束的不同方法有哪些,以便只能插入在订单日期可用的项目?

What are the different ways of adding a constraint so that only items that are available on the order date can be inserted?

order.date 必须在 item.date_from 和 item.date_to 之间...有哪些不同的方法?

CREATE TABLE "item" (
  "id" SERIAL PRIMARY KEY,
  "date_from" DATE NOT NULL,
  "date_to" DATE NOT NULL
);

CREATE TABLE "order" (
  "id" SERIAL PRIMARY KEY,
  "date" DATE NOT NULL
);

CREATE TABLE "order_item" (
  "order" INTEGER NOT NULL REFERENCES "order",
  "item" INTEGER NOT NULL REFERENCES "item" 
);

一个测试例子:

CREATE OR REPLACE FUNCTION public.item_date()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
DECLARE
    order_date date;
    from_date date;
    to_date date;
BEGIN
    select into order_date "date" from "order" where id = new.order;
    select into from_date, to_date date_from, date_to from item where id = new.item;
    --Use date range to test whether order date is in item date range.
    if order_date <@ daterange(from_date, to_date, '[]') then
        return new;
    else
        return null;
    end if;
END;
$function$

create trigger item_date_check before insert or update on order_item for each row execute function item_date();

insert into item values (1, '09/01/2021', '10/31/2021');
insert into item values (2, '07/01/2021', '08/31/2021');
insert into "order" values (1, '09/05/2021');

insert into order_item values (1, 1);
NOTICE:  Order date 2021-09-05, from_date 2021-09-01, to_date 2021-10-31
INSERT 0 1

--Returning NULL causes the INSERT not to happen.
insert into order_item values (1, 2);
NOTICE:  Order date 2021-09-05, from_date 2021-07-01, to_date 2021-08-31
INSERT 0 0


请注意,我必须引用“order”,因为它也是一个保留字。你可以看看 Key(reserved) Words. For range functions/operators see Range Function. For general information on range(s) see Range Types

检查约束适用于简单表达式。例如,对订单进行简单的完整性检查:check( date > '2010-01-01')。还有exclusion constraints which check no two rows have the same value as defined by the exclusion. But, with the exception of foreign key constraints,约束不查询其他tables.

你可以通过 insertupdate 上的触发器来解决这个问题,我将在下面进行介绍,但最好用参照完整性来解决这类问题。但是,我想不出办法。


您可以查看订单的可用商品。这里</code>是订单日期。</p> <pre><code>create temporary view items_available_to_order select * -- pluralize table names to avoid conflicting with keywords and columns from items -- date_from and date_to has become a single daterange when_available where items.when_available @>

然后只插入该视图中的项目。


如果你想走触发路线(你可以两者都做)写一个函数来检查订单的项目是否有效。它要么引发异常,要么引发 returns 触发器。 new 是插入的行,或更新后的行。

我更改了一些 table 和列名称和类型以避免常见的陷阱。

create function check_item_order_is_valid()
  returns trigger
  language 'plpgsql'
as $body$
declare
  item_is_available boolean;
begin
  select
    items.when_available @> orders.ordered_on into item_is_available
  from item_orders
  join items on items.id = new.order_id
  join orders on orders.id = new.item_id;

  if( not item_is_available) then
    raise exception 'Item #% is not available for order #%',
      new.item_id, new.order_id;
  end if;

  return new;
end
$body$

然后定义一个触发器,在 item/order table.

中插入或更新行时调用该函数
create trigger check_item_orders
  before insert or update
  on item_orders
  for each row
  execute function check_item_order_is_valid();

Demonstration.

如果项目的有效范围发生变化怎么办?您需要对项目进行更新触发器以检查其订单是否仍然有效。可能是。取决于您的业务逻辑。