我应该将 org.joda.time.YearMonth 作为日期存储还是作为 PostgreSQL 中的两列存储?
Should I store a org.joda.time.YearMonth as date or as two columns in PostgreSQL?
我在我的数据模型中存储特定月份(含年份)的值。为简单起见,假设我将像这样存储几个月的平均温度:
| month | degreeCelsius |
+---------+---------------|
| 2010-01 | 5.2 |
| 2010-02 | 6 |
| 2010-03 | 6.8 |
| ... | ... |
现在我想将此数据保存到 PostgreSQL table,我想知道月份要使用哪种数据类型。在我的 Java 应用程序中,我使用 org.joda.time.YearMonth
类型来表示月份。
基本上我有两个想法:要么我使用 date
类型,它也会为 "day" 保存一个值,这将毫无意义。由于月份是 table 的主键,因此重要的是不要为同一个月(但日期不同)插入两个值,并且我可以可靠地搜索特定年份的平均温度 -月。因此,我可以创建一个强制执行日期必须始终为“1”的约束,如果我想要 2010 年 2 月的值,我可以可靠地搜索 2010-02-01
。
然而,保留日期感觉有点尴尬,查看数据模型的其他人可能会对日期部分感到困惑。所以另一个想法是将该类型拆分为两列:year
和 month
。这是否引入了我没有看到的新问题?哪种方案是"better"/我应该注意哪些缺点?
我会建议一个日期,因为这样您可以在 sql 查询中进行日期运算时直接使用它。
请注意,实现您提到的检查约束的一个好方法是 value=date_trunc('month', value)
。这样您就可以保证没有与该值关联的时间值。 (编辑:实际上没关系,因为 postgres 有一个不接受时间值的日期数据类型。我习惯于使用 oracle,我们遇到的 "date" 数据类型实际上是一个时间戳)
我不喜欢为此使用日期。在这种情况下,日期违反了最小惊喜原则。
所以我会将月份存储为一列或两列。如果您需要进行日期运算——根据我的经验,这不太可能——您可以通过连接和强制转换获得日期或时间戳。
需要注意的两个关键问题是限制和特权。
一栏
将有效月份存储在 table 中,并为其设置外键引用。
create table months (
cal_month char(7) primary key
);
insert into months
select left(generate_series('2010-01-01'::timestamp,
'2100-12-01'::timestamp,
'1 month')::text, 7);
您需要撤销 几乎 所有人对 table 的权限。
create table avg_temps (
cal_month char(7) primary key references months (cal_month),
temp_c numeric(3, 1)
);
insert into avg_temps values
('2010-01', 5.2), ('2010-02', 6), ('2010-03', 6.8);
两列
您可以将基础 table 放在不同的架构中。 (也适用于一栏方案。)这样可以更简单地控制权限。
create schema temperature;
create table temperature.avg_temps (
cal_year integer not null
check (cal_year between 2010 and 2100),
cal_month integer not null
check (cal_month between 1 and 12),
temp_c numeric(3, 1)
);
insert into avg_temps values
(2010, 1, 5.2), (2010, 2, 6), (2010, 3, 6.8);
并使用 public 架构中的视图使其看起来整洁。
create view public.avg_temps as
select cal_year || '-' || lpad(cal_month::text, 2, '0') as cal_month, temp_c
from temperature.avg_temps;
我自己更喜欢单栏方法。
我在我的数据模型中存储特定月份(含年份)的值。为简单起见,假设我将像这样存储几个月的平均温度:
| month | degreeCelsius |
+---------+---------------|
| 2010-01 | 5.2 |
| 2010-02 | 6 |
| 2010-03 | 6.8 |
| ... | ... |
现在我想将此数据保存到 PostgreSQL table,我想知道月份要使用哪种数据类型。在我的 Java 应用程序中,我使用 org.joda.time.YearMonth
类型来表示月份。
基本上我有两个想法:要么我使用 date
类型,它也会为 "day" 保存一个值,这将毫无意义。由于月份是 table 的主键,因此重要的是不要为同一个月(但日期不同)插入两个值,并且我可以可靠地搜索特定年份的平均温度 -月。因此,我可以创建一个强制执行日期必须始终为“1”的约束,如果我想要 2010 年 2 月的值,我可以可靠地搜索 2010-02-01
。
然而,保留日期感觉有点尴尬,查看数据模型的其他人可能会对日期部分感到困惑。所以另一个想法是将该类型拆分为两列:year
和 month
。这是否引入了我没有看到的新问题?哪种方案是"better"/我应该注意哪些缺点?
我会建议一个日期,因为这样您可以在 sql 查询中进行日期运算时直接使用它。
请注意,实现您提到的检查约束的一个好方法是 value=date_trunc('month', value)
。这样您就可以保证没有与该值关联的时间值。 (编辑:实际上没关系,因为 postgres 有一个不接受时间值的日期数据类型。我习惯于使用 oracle,我们遇到的 "date" 数据类型实际上是一个时间戳)
我不喜欢为此使用日期。在这种情况下,日期违反了最小惊喜原则。
所以我会将月份存储为一列或两列。如果您需要进行日期运算——根据我的经验,这不太可能——您可以通过连接和强制转换获得日期或时间戳。
需要注意的两个关键问题是限制和特权。
一栏
将有效月份存储在 table 中,并为其设置外键引用。
create table months (
cal_month char(7) primary key
);
insert into months
select left(generate_series('2010-01-01'::timestamp,
'2100-12-01'::timestamp,
'1 month')::text, 7);
您需要撤销 几乎 所有人对 table 的权限。
create table avg_temps (
cal_month char(7) primary key references months (cal_month),
temp_c numeric(3, 1)
);
insert into avg_temps values
('2010-01', 5.2), ('2010-02', 6), ('2010-03', 6.8);
两列
您可以将基础 table 放在不同的架构中。 (也适用于一栏方案。)这样可以更简单地控制权限。
create schema temperature;
create table temperature.avg_temps (
cal_year integer not null
check (cal_year between 2010 and 2100),
cal_month integer not null
check (cal_month between 1 and 12),
temp_c numeric(3, 1)
);
insert into avg_temps values
(2010, 1, 5.2), (2010, 2, 6), (2010, 3, 6.8);
并使用 public 架构中的视图使其看起来整洁。
create view public.avg_temps as
select cal_year || '-' || lpad(cal_month::text, 2, '0') as cal_month, temp_c
from temperature.avg_temps;
我自己更喜欢单栏方法。