【问题标题】:Constraint to disallow overlapping dates禁止重叠日期的约束
【发布时间】:2021-11-22 02:09:47
【问题描述】:

我正在尝试在产品促销的 postgres 中创建一个表,这是一个简化的示例:

create table promotions
(
    product_id text,
    start_date date,
    end_date date,
    discount numeric(5, 4)
)

对于特定产品,不应有重叠的促销活动,即该表不应允许有两行相同的product_id 具有重叠的日期范围。

我不知道这是否可能,但我认为最好将其编码为约束。

我一直在寻找解决方案,但找不到任何人解决这个特定问题。

【问题讨论】:

    标签: sql postgresql date constraints


    【解决方案1】:

    您正在使用daterange 寻找exclusion constraint

    alter table promotions
       add constraint no_overlapping_dates
       exclude using gist (product_id with =, 
                           daterange(start_date, end_date, '[]')  with &&);
    

    限制设置为在范围内包含结束日期。如果 end_date 是范围无效有效的第一个日期,请将daterange()函数的第二个参数更改为'[)'

    为此,您还需要install 扩展名btree_gist

    【讨论】:

      【解决方案2】:

      您也可以使用触发器和过程来检查它 类似的东西;

      
      CREATE OR REPLACE FUNCTION check_overlying_prom_dates() RETURNS trigger AS $$
          BEGIN
             IF (select end_date from promotions where product_id = NEW.product_id) >= NEW.start_date
             THEN
                  RAISE EXCEPTION 'cant overlay promotion dates';
              END IF;
              RETURN NEW;
          END;
      $$ LANGUAGE plpgsql;
      
      CREATE TRIGGER promotion_date_trigger 
      BEFORE INSERT OR UPDATE ON promotions
      FOR EACH ROW EXECUTE PROCEDURE check_overlying_prom_dates();
      

      【讨论】:

        【解决方案3】:

        更新

        正如@a_horse_with_no_name 所解释的那样,我已将实现更改为使用 btree_gist 的排除约束。

        不过,据我所知,btree_gist 只是 Postgres 13.0 中的受信任扩展,所以我保留此解决方案,以防任何试图解决此问题的人无法在其数据库上安装不受信任的扩展。


        谢谢大家。我尝试了@dkc 方法。最后的代码是这样的:

        /*
          If we have two date ranges [x1, y1] & [x2, y2], they can overlap in 4 ways:
        
          1. ([)] -> x1 <= x2 && y1 >= x2
          2. [(]) -> x1 <= y2 && y1 >= y2
          3. [()] -> x1 >= x2 && y1 <= y2
          4. ([]) -> x1 <= x2 && y1 >= y2
        */
        create or replace function check_overlying_prom_dates()
        returns trigger
        as $$
        begin
        if (
            -- case 1
            select bool_or(new.start_date <= start_date and new.end_date >= start_date)
            from promotions
            where product_id = new.product_id
            ) or (
            -- case 2
            select bool_or(new.start_date <= end_date and new.end_date >= end_date)
            from promotions
            where product_id = new.product_id
            ) or (
            -- case 3
            select bool_or(new.start_date >= start_date and new.end_date <= end_date)
            from promotions
            where product_id = new.product_id
            ) or (
            -- case 4
            select bool_or(new.start_date <= start_date and new.end_date >= end_date)
            from promotions
            where product_id = new.product_id
            )
        then
            raise exception 'cant overlay promotion dates';
        end if;
        return new;
        end;
        $$ language plpgsql;
        
        create trigger promotion_date_trigger 
        before insert or update on promotions
        for each row execute procedure check_overlying_prom_dates();
        

        【讨论】:

        • 排除约束会更有效率
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-21
        • 2021-10-18
        • 2022-01-14
        相关资源
        最近更新 更多