2017-03-28 11 views
0

我正在收集日期以存儲在數據庫中,但必須說明用戶可能不知道確切日期的事實。我想提供僅輸入月份和年份的選項。將這些值存儲在數據庫中的最佳方式是什麼?Rails - 存儲日期時的日期可選

如果我使用一個沒有日期的日期對象,然後將它作爲日期存儲在數據庫中,那麼它默認爲月份的第一個,這將是不準確的。我的想法是可能將日,月和年分別存儲爲整數,然後在返回Date對象的模型上使用方法,以及日期是否準確(即已由用戶輸入,而不僅僅是由系統)。

雖然看起來有點亂,有沒有更好的方法來做到這一點?

謝謝。

+0

可以存儲日期在一列和精度等,如「2017年3月28日」,「日」和「2017年3月1日」,「月」,所以你會明顯它從確切的日期「2017-03-01」,「日」 –

回答

1

有提供多種解決方案。選擇一個供應您的使用情況最好的:

1.個別日期的部分領域

你已經有這個想法實驗。這似乎是這樣的:

create table t(
    year int, 
    month int, 
    day int 
) 

2017-03一個月可以用(2017, 3, NULL)表示。

請注意,所有字段都是NULL -able。至少如果你想獲得一些信息,你可以製作year NOT NULL

使用此模型,您必須使用客戶端邏輯來構造某種類似日期的對象以供進一步使用。

這種模式的一大缺點是很難索引。你可以索引f.ex. make_date(year, coalesce(month, 1), coalesce(day, 1)),但在查詢中使用它很不方便。此外,爲了不允許某些價值組合,這是毫無意義的(f.ex.給出一年一天,但不是一個月),您應該添加一個(非常長的)約束f.ex.

CHECK (CASE 
    WHEN year IS NULL THEN month IS NULL AND day IS NULL 
    ELSE CASE WHEN month IS NULL THEN day IS NULL END 
END) 

2.樣品的日期和精度

create table t(
    sample_date  date, 
    sample_precision date_precision -- enum of f.ex. 'year', 'month', 'day' 
) 

2017-03一個月可以與('2017-03-28', 'month')來表示。

這並不需要很長的CHECK的限制,但它是相當難的日期,如果sample_date確實只是一個樣本時的2017-03全月應連續來表示(f.ex.,採樣日期選擇甚至可能是2017-03-28)。當您使用第一個日期作爲sample_date(根據它可以採用的值,基於sample_precision),事情會變得輕鬆一些。但隨後將需要以下CHECK約束完整性:

CHECK (date_trunc(sample_precision::text, sample_date)::date = sample_date) 

(更多關於進一步做好這一點,以後)

3.可能的範圍

您可以存儲的可能範圍日期。用possible_startpossible_end或用PostgreSQL的daterange type

create table t(
    possible_start date, 
    possible_end date, 
    -- or 
    possible_range daterange 
) 

2017-03一個月可以用('2017-03-01', '2017-03-31')表示。

在這個模型中,當possible_start = possible_end那麼date值是確切的。您可以查詢現在兩個不同的東西:

  • 哪些行周圍發生的事情給定的日期(S)肯定(包含
  • 哪些行可能身邊發生的特定日期(S)(相交

這兩種查詢都可以使用帶有daterange的索引。

這樣的美是你不限於月份範圍。你可以直接使用任何長度的範圍。它唯一的缺點是範圍必須是連續的。

2. + 3.?

有一個變體,它具有所有的的優勢,不過貌似2.interval type

create table t(
    possible_start date, 
    possible_length interval day 
) 

2017-03一個月可以用('2017-03-01', '1 month')表示。

(該day預選賽限制interval的最小精度是個一天。它不需要timestamptimestamptz的解決方案。)

的最後日期可能與(possible_start + possible_length - interval '1 day')::date表示。或者,整個範圍爲(對於daterange索引):daterange(possible_start, (possible_start + possible_length)::date)(範圍在它們的末尾隱含排他性)。

http://rextester.com/AWIO2403

+0

之前的date_trunc感謝您的全面答案,我瞭解了很多,同時試圖瞭解你提供的每個解決方案。因爲我不需要索引日期,只有一天可能會丟失,我會使用第一個選項並分別存儲值。再次感謝! –

1

,你可以在一列和精度存儲日期等,對值進行比較,就可以使用date_trunc(precision_column,DATE),如:

t=# create table so36(d date,p text); 
CREATE TABLE 
t=# insert into so36 select now(),'day'; 
INSERT 0 1 
t=# insert into so36 select '2017-03-01','month'; 
INSERT 0 1 
t=# select *,date_trunc(p,d),date_trunc(p,now()) from so36; 
    d  | p |  date_trunc  |  date_trunc 
------------+-------+------------------------+------------------------ 
2017-03-28 | day | 2017-03-28 00:00:00+00 | 2017-03-28 00:00:00+00 
2017-03-01 | month | 2017-03-01 00:00:00+00 | 2017-03-01 00:00:00+00 
(2 rows) 
+0

感謝您提供此解決方案,我還沒有遇到過 –