会议室预定系统实践(轻松解放开发) - PostgreSQL tsrange(时间范围类型) + 排他约束

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介:

标签

PostgreSQL , tsrange , 范围 , exclude using , 排他约束 , btree_gist , 会议室预定 , 时间重叠 , 空间重叠


背景

PostgreSQL 范围、数组、空间类型(range, array, geometry),都有交叉属性,例如时间范围:7点到9点,8点到9点,这两个内容是有重叠部分的。例如数组类型:[1,2,3]和[2,4,5]是有交叉部分的。例如空间类型也有交叉的属性。

那么在设计时,实际上业务上会有这样的约束,不允许对象有相交。

例如会议室预定系统,不允许两个人预定的会议室时间交叉,否则就有可能一个会议室在某个时间段被多人共享了,业务上是不允许的。

那么如何做到这样的约束呢?

PostgreSQL 提供了exclude约束,可以实现这个需求。

exclude 约束的语法

  EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |    
    
exclude_element in an EXCLUDE constraint is:    
    
{ column_name | ( expression ) } [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]    

exclude 约束常用的操作符

范围、数组、空间类型的相交操作符如下:

postgres=# \do &&    
                                          List of operators    
   Schema   | Name | Left arg type | Right arg type | Result type |           Description                
------------+------+---------------+----------------+-------------+----------------------------------    
 pg_catalog | &&   | anyarray      | anyarray       | boolean     | overlaps    
 pg_catalog | &&   | anyrange      | anyrange       | boolean     | overlaps    
 pg_catalog | &&   | box           | box            | boolean     | overlaps    
 pg_catalog | &&   | circle        | circle         | boolean     | overlaps    
 pg_catalog | &&   | inet          | inet           | boolean     | overlaps (is subnet or supernet)    
 pg_catalog | &&   | polygon       | polygon        | boolean     | overlaps    
 pg_catalog | &&   | tinterval     | tinterval      | boolean     | overlaps    
 pg_catalog | &&   | tsquery       | tsquery        | tsquery     | AND-concatenate    
 public     | &&   | integer[]     | integer[]      | boolean     | overlaps    
(9 rows)    

会议室预定系统的例子

1、创建btree_gist插件.

postgres=# create extension btree_gist;    
CREATE EXTENSION    

2、创建会议室预定表

postgres=# create table t_meeting (    
  roomid int,   -- 会议室ID    
  who int,      -- 谁定了这个会议室    
  ts tsrange,   -- 时间范围    
  desc text,    -- 会议内容描述    
  exclude using gist (roomid with = , ts with &&)   -- 排他约束,同一个会议室,不允许有时间范围交叉的记录    
);    
CREATE TABLE    

3、预定会议室,如果同一个会议室输入的时间不允许预定(有交叉),则自动报错。实现强约束。

postgres=# insert into t_meeting values (1, 1, $$['2017-01-01 07:00:00', '2017-01-01 08:00:00')$$);    
INSERT 0 1    
    
postgres=# insert into t_meeting values (1,1,$$['2017-01-01 07:00:00', '2017-01-01 08:00:00')$$);    
ERROR:  conflicting key value violates exclusion constraint "t_meeting_roomid_ts_excl"    
DETAIL:  Key (roomid, ts)=(1, ["2017-01-01 07:00:00","2017-01-01 08:00:00")) conflicts with existing key (roomid, ts)=(1, ["2017-01-01 07:00:00","2017-01-01 08:00:00")).    
    
postgres=# insert into t_meeting values (2,1,$$['2017-01-01 07:00:00', '2017-01-01 08:00:00')$$);    
INSERT 0 1    
    
postgres=# insert into t_meeting values (1,1,$$['2017-01-01 09:00:00', '2017-01-01 10:00:00')$$);    
INSERT 0 1    
    
postgres=# insert into t_meeting values (1,1,$$['2017-01-01 09:00:00', '2017-01-01 11:00:00')$$);    
ERROR:  conflicting key value violates exclusion constraint "t_meeting_roomid_ts_excl"    
DETAIL:  Key (roomid, ts)=(1, ["2017-01-01 09:00:00","2017-01-01 11:00:00")) conflicts with existing key (roomid, ts)=(1, ["2017-01-01 09:00:00","2017-01-01 10:00:00")).    
    
postgres=# insert into t_meeting values (1,1,$$['2017-01-01 08:00:00', '2017-01-01 09:00:00')$$);    
INSERT 0 1    
postgres=# select * from t_meeting order by roomid, ts;    
 roomid | who |                      ts                           
--------+-----+-----------------------------------------------    
      1 |   1 | ["2017-01-01 07:00:00","2017-01-01 08:00:00")    
      1 |   1 | ["2017-01-01 08:00:00","2017-01-01 09:00:00")    
      1 |   1 | ["2017-01-01 09:00:00","2017-01-01 10:00:00")    
      2 |   1 | ["2017-01-01 07:00:00","2017-01-01 08:00:00")    
(4 rows)    

4、查询某个时间段还有哪些会议室能预定

会议室ID表,假设有50个会议室。

create table t_room (roomid int primary key);    
    
insert into t_room select generate_series(1,50);    

假设用户要预定 某一天:7点到9点的会议室,这样操作即可:

select roomid from t_room    
except    
select roomid from t_meeting where ts && $$['2017-01-01 07:00:00', '2017-01-01 09:00:00')$$;    
 roomid     
--------    
     14    
      3    
      4    
     16    
     42    
     50    
     19    
     13    
     40    
     46    
     18    
     34    
     39    
      7    
     35    
     43    
     23    
     36    
     29    
     30    
     28    
      8    
     24    
     32    
     10    
     33    
      9    
     45    
     22    
     49    
     48    
     38    
     37    
      5    
     12    
     31    
     11    
     27    
     20    
     44    
     41    
      6    
     21    
     15    
     47    
     17    
     26    
     25    
(48 rows)    
    
    
postgres=# explain (analyze,verbose,timing,costs,buffers) select roomid from t_room    
except    
select roomid from t_meeting where ts && $$['2017-01-01 07:00:00', '2017-01-01 09:00:00')$$;    
                                                                     QUERY PLAN                                                                         
----------------------------------------------------------------------------------------------------------------------------------------------------    
 HashSetOp Except  (cost=0.00..77.28 rows=2550 width=8) (actual time=0.074..0.085 rows=48 loops=1)    
   Output: "*SELECT* 1".roomid, (0)    
   Buffers: shared hit=3    
   ->  Append  (cost=0.00..70.88 rows=2562 width=8) (actual time=0.013..0.058 rows=53 loops=1)    
         Buffers: shared hit=3    
         ->  Subquery Scan on "*SELECT* 1"  (cost=0.00..61.00 rows=2550 width=8) (actual time=0.012..0.029 rows=50 loops=1)    
               Output: "*SELECT* 1".roomid, 0    
               Buffers: shared hit=1    
               ->  Seq Scan on public.t_room  (cost=0.00..35.50 rows=2550 width=4) (actual time=0.010..0.016 rows=50 loops=1)    
                     Output: t_room.roomid    
                     Buffers: shared hit=1    
         ->  Subquery Scan on "*SELECT* 2"  (cost=1.44..9.88 rows=12 width=8) (actual time=0.018..0.019 rows=3 loops=1)    
               Output: "*SELECT* 2".roomid, 1    
               Buffers: shared hit=2    
               ->  Bitmap Heap Scan on public.t_meeting  (cost=1.44..9.76 rows=12 width=4) (actual time=0.018..0.018 rows=3 loops=1)    
                     Output: t_meeting.roomid    
                     Recheck Cond: (t_meeting.ts && '["2017-01-01 07:00:00","2017-01-01 09:00:00")'::tsrange)    
                     Heap Blocks: exact=1    
                     Buffers: shared hit=2    
                     ->  Bitmap Index Scan on t_meeting_roomid_ts_excl  (cost=0.00..1.44 rows=12 width=0) (actual time=0.010..0.010 rows=4 loops=1)    
                           Index Cond: (t_meeting.ts && '["2017-01-01 07:00:00","2017-01-01 09:00:00")'::tsrange)    
                           Buffers: shared hit=1    
 Planning time: 0.123 ms    
 Execution time: 0.172 ms    
(24 rows)    

速度杠杠的。开发也方便了。

小结

使用PostgreSQL,时间范围类型、exclude约束,很好的帮助业务系统实现会议室预定的强约束。

使用except语法,很方便的找到需要预定的时间段还有那些会议室是空闲的。

开不开心,解放开发人员的大脑。

参考

https://www.postgresql.org/docs/10/static/sql-createtable.html#SQL-CREATETABLE-EXCLUDE

相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
3月前
|
存储 SQL Cloud Native
深入了解云原生数据库CockroachDB的概念与实践
作为一种全球领先的分布式SQL数据库,CockroachDB以其高可用性、强一致性和灵活性等特点备受关注。本文将深入探讨CockroachDB的概念、设计思想以及实践应用,并结合实例演示其在云原生环境下的优越表现。
|
3月前
|
Cloud Native 关系型数据库 大数据
CockroachDB:云原生数据库的新概念与实践
本文将介绍CockroachDB,一种先进的云原生数据库,它具备分布式、强一致性和高可用性等特点。我们将探讨CockroachDB的基本原理、架构设计以及在实际应用中的种种优势和挑战。
|
3月前
|
Cloud Native 关系型数据库 分布式数据库
|
5月前
|
关系型数据库 Go PostgreSQL
golang pgx自定义PostgreSQL类型
golang的pgx驱动提供了大约70种PostgreSQL类型支持,但还是有一些类型没有涵盖,本文介绍如何自己编写代码支持特殊的类型。
75 3
|
1月前
|
关系型数据库 Serverless 分布式数据库
PolarDB的Serverless能力与同类型产品的对比
【2月更文挑战第21天】PolarDB的Serverless能力与同类型产品的对比
18 2
|
2月前
|
关系型数据库 Linux Shell
Centos系统上安装PostgreSQL和常用PostgreSQL功能
Centos系统上安装PostgreSQL和常用PostgreSQL功能
|
3月前
|
存储 关系型数据库 分布式数据库
PolarDB IMCI助力云学堂实现高效HTAP系统
云学堂数据库全量使用PolarDB,成本节约30%以上
108 0
|
3月前
|
Cloud Native 关系型数据库 分布式数据库
凭安征信引入阿里云PolarDB云数据库支撑企业征信核心业务系统
凭安征信是国家中小企业公共服务示范平台,主营信用管理服务包括信用管家、水滴信用及可信认证。通过采用阿里云PolarDB云原生数据库替代RDS数据库帮助客户全面实现业务系统性能提升1-2倍,通过PolarDB企业级能力的加持下,运维更加简便,操作更加简单,数据安全能力更强。
|
3月前
|
关系型数据库 分布式数据库 PolarDB
在PolarDB中,对于join操作,系统会采用拉取内表
【1月更文挑战第21天】【1月更文挑战第103篇】在PolarDB中,对于join操作,系统会采用拉取内表
19 1
|
3月前
|
关系型数据库 分布式数据库 数据库
阿里云PolarDB开发者大会首度召开,让数据库开发像“搭积木”一样简单
阿里云PolarDB开发者大会首度召开,让数据库开发像“搭积木”一样简单
109 0

相关产品

  • 云原生数据库 PolarDB