Sql POSTGRES-仅在填写特定列时插入

Sql POSTGRES-仅在填写特定列时插入,sql,database,postgresql,Sql,Database,Postgresql,拥有: ID start_date end_date account 1 2016-06-30 null 123 1 2017-04-19 null 111 1 2017-04-26 2017-07-30 789 insert into table (ID, start_date, account) values (1,2016-08-01,123) Insert into insert_only (ID, start_da

拥有:

ID start_date  end_date     account 
1   2016-06-30  null         123
1   2017-04-19  null         111
1   2017-04-26  2017-07-30   789
insert into table (ID, start_date, account) values (1,2016-08-01,123) 
Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
BEFORE: 

stackoverflow=# select * from insert_only;
 id | start_date |  end_date  | account 
----+------------+------------+---------
  1 | 2016-08-01 |            |     123
  1 | 2017-08-01 |            |     111
  1 | 2017-04-26 | 2017-07-30 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
(4 rows)

stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
INSERT 0 0

stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
INSERT 0 1

AFTER:

stackoverflow=# select * from insert_only;
 id | start_date |  end_date  | account 
----+------------+------------+---------
  1 | 2016-08-01 |            |     123
  1 | 2017-08-01 |            |     111
  1 | 2017-04-26 | 2017-07-30 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
(5 rows)
想要实现的目标:

ID start_date  end_date     account 
1   2016-06-30  null         123
1   2017-04-19  null         111
1   2017-04-26  2017-07-30   789
insert into table (ID, start_date, account) values (1,2016-08-01,123) 
Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
BEFORE: 

stackoverflow=# select * from insert_only;
 id | start_date |  end_date  | account 
----+------------+------------+---------
  1 | 2016-08-01 |            |     123
  1 | 2017-08-01 |            |     111
  1 | 2017-04-26 | 2017-07-30 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
(4 rows)

stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
INSERT 0 0

stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
INSERT 0 1

AFTER:

stackoverflow=# select * from insert_only;
 id | start_date |  end_date  | account 
----+------------+------------+---------
  1 | 2016-08-01 |            |     123
  1 | 2017-08-01 |            |     111
  1 | 2017-04-26 | 2017-07-30 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
  1 | 2016-08-01 | 2016-08-01 |     789
(5 rows)
是否有办法创建约束/索引/其他机制来检查:

  • 如果ID和帐户存在且结束日期为空->不执行任何操作
  • 如果ID和帐户存在且结束日期不为空->插入行
  • 所以:

    ID start_date  end_date     account 
    1   2016-06-30  null         123
    1   2017-04-19  null         111
    1   2017-04-26  2017-07-30   789
    
    insert into table (ID, start_date, account) values (1,2016-08-01,123) 
    
    Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    
    Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    
    BEFORE: 
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (4 rows)
    
    stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    INSERT 0 0
    
    stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    INSERT 0 1
    
    AFTER:
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (5 rows)
    
    ->什么也没发生

    insert into table (ID, start_date, account) values (1,2017-04-26, 789)
    
    ->插入行

    我在想,可能是一个带有存储函数的检查约束,它将检查上述内容或类似的内容?或索引:

    create unique index unique_account on table (ID, account) where end_date is null
    

    这是最佳实践

    警告:在高度事务数据库中使用触发器将导致性能问题

    解决方案:我假设您使用PostgreSQL 9.5或更高版本

    直到这一步你几乎完成

    create unique index unique_account on table (ID, account) where end_date is null
    
    您只需在PostgreSQL 9.5或更高版本中使用
    UPSERT
    query即可

    ANS案例1:

    ID start_date  end_date     account 
    1   2016-06-30  null         123
    1   2017-04-19  null         111
    1   2017-04-26  2017-07-30   789
    
    insert into table (ID, start_date, account) values (1,2016-08-01,123) 
    
    Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    
    Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    
    BEFORE: 
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (4 rows)
    
    stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    INSERT 0 0
    
    stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    INSERT 0 1
    
    AFTER:
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (5 rows)
    
    *我们需要
    “冲突”
    的唯一索引。此查询意味着,如果您插入相同的
    id+account+date为空
    ,则
    “冲突时”
    将执行且
    不执行任何操作

    ANS案例2:

    ID start_date  end_date     account 
    1   2016-06-30  null         123
    1   2017-04-19  null         111
    1   2017-04-26  2017-07-30   789
    
    insert into table (ID, start_date, account) values (1,2016-08-01,123) 
    
    Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    
    Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    
    BEFORE: 
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (4 rows)
    
    stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    INSERT 0 0
    
    stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    INSERT 0 1
    
    AFTER:
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (5 rows)
    
    *使用相同的查询,我们输入账户的
    end\u date
    789
    插入将成功

    输出:

    ID start_date  end_date     account 
    1   2016-06-30  null         123
    1   2017-04-19  null         111
    1   2017-04-26  2017-07-30   789
    
    insert into table (ID, start_date, account) values (1,2016-08-01,123) 
    
    Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    
    Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    
    BEFORE: 
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (4 rows)
    
    stackoverflow=# Insert into insert_only (ID, start_date, account) values (1,'2016-08-01',123) ON CONFLICT DO NOTHING;
    INSERT 0 0
    
    stackoverflow=# Insert into insert_only (ID, start_date, end_date,account) values (1,'2016-08-01','2016-08-01',789) ON CONFLICT DO NOTHING;
    INSERT 0 1
    
    AFTER:
    
    stackoverflow=# select * from insert_only;
     id | start_date |  end_date  | account 
    ----+------------+------------+---------
      1 | 2016-08-01 |            |     123
      1 | 2017-08-01 |            |     111
      1 | 2017-04-26 | 2017-07-30 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
      1 | 2016-08-01 | 2016-08-01 |     789
    (5 rows)
    

    可以通过将insert包装到存储过程中来实现这一点。然后拒绝对表的写访问。因此,所有写入操作都必须通过应用所有必要检查的过程。哦,但你已经考虑过了。我感觉你实际上想要一个约束来强制执行非重叠的日期范围(每个{id,account})。在这种情况下,使用
    +无限
    作为开放区间比空区间更实用。希望这个解决方案对你有所帮助。永远不要在insert中使用触发器进行验证,当您的数据增长到100万时,这将在将来造成麻烦。