我需要设计一个(postgres)数据库表,该表可以保存某些内容的动态范围。
示例: 我们有一个课程表。每门课程可以有(最少和最多)或(一定数量)的参与者。 一门数学课程可以由4至10名学生开始,而一门物理课程则需要8名学生来开始。
之后,我希望能够查询该信息。 假设我希望所有能容纳6名学生的课程。应该退还数学课程,不应该退还物理课程,因为它恰好需要8名学生。
当我查询8名学生时,两个课程都应退还。
对于实现,我考虑了两个简单的字段:min_students
和max_students
。然后,我可以简单地检查数字是否等于或介于这些数字之间。
问题是:我必须每次都填写两列。另外,这门物理课程需要8名学生。
示例:
name | min_students | max_students
--------|--------------|-------------
math | 4 | 10
physics | 8 | 8
有没有更优雅/有效的方法?我还考虑过将max_students列设置为可空,以便检查
min_students = X OR (min_students >= X AND max_students <= Y)
会更有效吗?性能如何?
答案 0 :(得分:2)
每门课程可以(最少和最多)或(一定数量)参加者。
所有课程都有最小值和最大值,对于某些课程,它恰好具有相同的值。看起来似乎很琐碎,但是以这种方式思考可以让您以一种更简单的方式定义问题。
代替:
min_students == X OR (min_students >= X AND max_students <= Y)
您可以将其表示为:
num_students BETWEEN min_students AND max_students
BETWEEN
包含所有内容,因此8 BETWEEN 8 and 8
是真实的
其他条件使查询对于人类而言难以理解,并且导致遗漏边缘情况,并且无论如何通常会导致效率低下的查询。着重于使代码易于理解或“优雅”,并且决不牺牲性能的可读性,除非您确实首先确定自己有性能问题,并且优化实际上是有帮助的。
如果您的表具有1000万行,则如果在极其有限的硬件上运行,可能值得超级优化磁盘使用情况,但是即使在20 MB的情况下减少表的磁盘使用率也几乎可以肯定在任何正常情况下都浪费时间当它不会使代码更复杂时。
此外,每行除包含其中的任何实际数据外还占用23-24个字节,因此剃除一两个字节不会有太大的区别。在某些情况下,将值设置为NULL实际上可以增加磁盘使用量。
使用范围数据类型时,比较如下所示:
num_students @> x
其中num_students
表示范围(例如4到10),而@>
表示“包含值”
create table num_sequence (num int);
create table courses_range (name text, num_students int4range);
insert into num_sequence select generate_series(3,10);
insert into courses_range values
('math', '[4,4]'), ('physics', '[6,7]'), ('dance', '[7,9]');
select * from num_sequence
left join courses_range on num_students @> num;
num | name | num_students
-----+---------+--------------
3 | |
4 | math | [4,5)
5 | |
6 | physics | [6,8)
7 | physics | [6,8)
7 | dance | [7,10)
8 | dance | [7,10)
9 | dance | [7,10)
10 | |
请注意,范围的输出格式类似于[x,y),硬括号表示包含,而括号表示排除,对于整数:[4,4] = [4,5)=(3,5)