Kiểu dữ liệu đơn cho các giá trị ngày không chính xác, được cho phép bởi ISO 8601


7

Làm cách nào tôi có thể lưu trữ giá trị ngày và thời gian với độ chính xác giảm trong loại PostgreSQL và để chúng hoạt động như giá trị ngày và / hoặc thời gian ?

ISO 8601 cho phép giá trị ngày với độ chính xác giảm. '1964', '1964-05', '1964-05-02' đều là các biểu diễn hợp lệ của một giá trị, nhằm tăng độ chính xác. Các loại 'datetime' của Python cũng cho phép các giá trị với độ chính xác giảm theo cách này.

Các loại thời gian riêng của PostgreSQL không cho phép giảm độ chính xác

Trong loại ngày gốc, mọi yếu tố của một ngày phải có mặt hoặc giá trị bị từ chối. Đặt các yếu tố dưới mức chính xác mong muốn thành '00' cũng không thành công.

=> SELECT CAST('1964-05-02' AS DATE);
    date    
------------
 1964-05-02
(1 row)

=> SELECT CAST('1964-05' AS DATE);
ERROR:  invalid input syntax for type date: "1964-05"
LINE 1: SELECT CAST('1964-05' AS DATE);
                    ^
=> SELECT CAST('1964' AS DATE);
ERROR:  invalid input syntax for type date: "1964"
LINE 1: SELECT CAST('1964' AS DATE);
                    ^
=> SELECT CAST('1964-00-00' AS DATE);
ERROR:  date/time field value out of range: "1964-00-00"
LINE 1: SELECT CAST('1964-00-00' AS DATE);
                    ^
HINT:  Perhaps you need a different "datestyle" setting.

Hành vi dự kiến ​​cho một ngày và / hoặc loại thời gian chính xác giảm

Có cách nào đơn giản, tiêu chuẩn để hỗ trợ nhập các giá trị ngày ISO 8601 với độ chính xác giảm vào loại ngày và / hoặc loại thời gian PostgreQuery không?

Tạo một loại cho điều này là có thể, nhưng tôi không biết làm thế nào. Tất nhiên, tôi cần các giá trị được kiểm tra phạm vi và xử lý các múi giờ và so sánh hợp lý với các giá trị thời gian khác, tất cả những điều hữu ích khác mà các loại tích hợp thực hiện.

Điều tôi mong đợi là, giống như giá trị '1964-05-02' đề cập đến toàn bộ khoảng thời gian từ 00:00:00 ngày hôm đó cho đến 00:00:00 ngày hôm sau, giá trị giảm độ chính xác sẽ chỉ đơn giản là đại diện cho một khoảng thời gian lớn hơn: '1962-05' đề cập đến toàn bộ khoảng thời gian từ 00:00:00 đầu tháng 5 năm 1962 đến 00:00:00 vào ngày đầu tiên của tháng 6 năm 1962.

Một ví dụ về những gì tôi muốn thấy:

=> SELECT CAST('1964-05-02 00:00' AS TIMESTAMP) = CAST('1964-05-02 00:00:00' AS TIMESTAMP);
 ?column? 
----------
 t
(1 row)

=> SELECT CAST('1964-05' AS TIMESTAMP) = CAST('1964-05-02' AS TIMESTAMP);
 ?column? 
----------
 t
(1 row)

Hiện tại các cựu cư xử như trên; cái sau phàn nàn về cú pháp đầu vào không hợp lệ của kiểu chữ đối với kiểu dấu thời gian. Trước mắt tôi, cả hai trường hợp giá trị độ chính xác giảm đều hoạt động hợp lý khi so sánh với giá trị độ chính xác tốt hơn.

Ý nghĩa của 1964-05ISO 8601 bao gồm các giá trị hơn-chính xác 1964-05-021964-05-02 18:271964-05-23. Vì vậy, những người nên so sánh bằng nhau.


Nói cách khác, bạn muốn toán tử đẳng thức đưa ra truekhi dấu thời gian 'có thể' bằng nhau (cho phép độ chính xác)?
Jack nói hãy thử topanswers.xyz

Không phải là người Haiti có thể là người; các giá trị được cố tình giảm độ chính xác. Ý nghĩa của 1964-05ISO 8601 bao gồm các giá trị hơn-chính xác 1964-05-021964-05-02 18:271964-05-23. Vì vậy, những người nên so sánh bằng nhau.
bignose

Quan điểm của tôi là bạn đang kéo dài định nghĩa về đẳng thức sao cho A = C và B = C không còn hàm ý A = B. Miễn là đó là những gì bạn muốn ... hãy xem câu trả lời được cập nhật của tôi
Jack nói hãy thử topanswers.xyz

Ngoài ra, bạn có muốn A = B ám chỉ B = A như tôi đã giả định trong bản chỉnh sửa của mình không?
Jack nói hãy thử topanswers.xyz

Câu hỏi hay, cảm ơn bạn đã khiến tôi suy nghĩ về yêu cầu của tôi. Tôi đang khám phá thêm tại stackoverflow.com/questions/7539122/ Cách
bignose

Câu trả lời:


6

Tôi đã sử dụng CHAR và VARCHAR trong quá khứ, thay thế các phần còn thiếu bằng dấu chấm hỏi hoặc dấu gạch ngang. Dấu hỏi có nghĩa là "không biết" và dấu gạch ngang có nghĩa là "không áp dụng". Điều này đã được chứng minh là đủ trực quan cho người dùng (thư ký và trợ lý trong vụ kiện phức tạp), đủ linh hoạt cho các luật sư và nó được sắp xếp hợp lý.

"1964------"
"1964-??-??"
"1964-05---"
"1964-05-??"
"1964-05-02"
"1964-06---"
"1964-06-??"

Bao bọc các khai báo và các ràng buộc KIỂM TRA của bạn trong TẠO DOMAIN hoặc TẠO LOẠI để giúp bảo trì dễ dàng hơn. TẠO DOMAIN không yêu cầu mã hóa bổ sung. CREATE TYPE yêu cầu các chức năng hỗ trợ được viết bằng ngôn ngữ cấp thấp.


3
+1 nhưng create typekhông yêu cầu các chức năng hỗ trợ bằng ngôn ngữ cấp thấp trừ khi bạn đang tạo loại cơ sở mới. Một loại hợp chất của a datetimevà độ chính xác enumchẳng hạn. create domainsau đó có thể được bọc xung quanh các loại để thêm các checkràng buộc. Cá nhân tôi muốn DB xử lý sự phức tạp của các múi giờ, năm nhuận, v.v. nếu chúng được yêu cầu vì vậy sẽ có xu hướng thích bọc các loại được cung cấp.
Jack nói hãy thử topanswers.xyz

@Jack Douglas: Điểm hay về chức năng hỗ trợ. Chúng tôi đã không phải đối phó với thời gian trong ngày. Chúng tôi đã phải đối phó với các loại thiếu chính xác mà ISO 8601 không lường trước được, như "196? - ?? - 01". . 'Quá khó để lấy loại hỗn hợp làm đầu vào và xuất ra một chuỗi dễ hiểu với các dấu hỏi trong đó.
Mike Sherrill 'Nhớ lại mèo'


1
@bignose: Các giá trị với độ chính xác tương tự so sánh chính xác cho bất đẳng thức. Các giá trị với các giới hạn khác nhau có thể không so sánh tốt, nhưng không rõ những gì ('1964 - ?? - ??' <'1964-05- ??') nên đánh giá. Chúng cũng không hỗ trợ số học khoảng, nhưng điều đó không được xác định rõ ở độ chính xác giảm.
Mike Sherrill 'Nhớ lại mèo'

2
@bignose: Không, tôi hiểu bạn và tôi hiểu họ không đóng vai trò là giá trị thời gian. Tôi đã chỉ ra những gì họ đã làm tốt, những gì họ làm không tốt và những gì không thực hiện có thể làm tốt một cách rõ rệt. Nếu bạn viết các hàm theo yêu cầu create type, bạn có thể thực hiện các biểu thức của biểu mẫu (DATE '1964-??-??' < 'DATE 1964-05-??')luôn luôn ước tính thành TRUE. Những gì bạn không thể làm là thuyết phục mọi người rằng nó có ý nghĩa để làm điều đó. Và đó có lẽ là lý do tại sao PostgreSQL, có hỗ trợ riêng cho khoảng 3 tá loại dữ liệu, về cơ bản không hỗ trợ loại dữ liệu này.
Mike Sherrill 'Nhớ lại mèo'

5

Không, loại khoảng hỗ trợ giảm độ chính xác nhưng không có loại ngày / giờ nào khác làm được.

Postgres cho phép bạn tự tạo ra create typenhưng không may cho phép các phần tử được thêm vào loại giới hạn tính hữu dụng của nó trong kịch bản này. Điều tốt nhất tôi có thể đưa ra yêu cầu bạn lặp lại các ràng buộc kiểm tra trên mọi trường nơi fuzzyloại được sử dụng:

create type preciseness as enum('day', 'month', 'year');
create type fuzzytimestamptz as (ts timestamptz, p preciseness);
create table t( id serial primary key,
                fuzzy fuzzytimestamptz
                    check( (fuzzy).ts is not null 
                           or ((fuzzy).ts is null and (fuzzy).p is not null) ),
                    check((fuzzy).ts=date_trunc('year', (fuzzy).ts) or (fuzzy).p<'year'),
                    check((fuzzy).ts=date_trunc('month', (fuzzy).ts) or (fuzzy).p<'month'),
                    check((fuzzy).ts=date_trunc('day', (fuzzy).ts) or (fuzzy).p<'day') );

insert into t(fuzzy) values (row(date_trunc('year', current_timestamp), 'year'));
insert into t(fuzzy) values (row(date_trunc('month', current_timestamp), 'month'));
insert into t(fuzzy) values (row(date_trunc('day', current_timestamp), 'day'));

select * from t;

 id |              fuzzy
----+----------------------------------
  1 | ("2011-01-01 00:00:00+00",year)
  2 | ("2011-09-01 00:00:00+01",month)
  3 | ("2011-09-23 00:00:00+01",day)

--edit - một toán tử đẳng thức mẫu:

create function fuzzytimestamptz_equality(fuzzytimestamptz, fuzzytimestamptz)
                returns boolean language plpgsql immutable as $$
begin
  return ($1.ts, $1.ts+coalesce('1 '||$1.p, '0')::interval)
         overlaps ($2.ts, $2.ts+coalesce('1 '||$2.p, '0')::interval);
end;$$;
--
create operator = ( procedure=fuzzytimestamptz_equality, 
                    leftarg=fuzzytimestamptz, 
                    rightarg=fuzzytimestamptz );

truy vấn mẫu:

select *, fuzzy=row(statement_timestamp(), null)::fuzzytimestamptz as equals_now,
          fuzzy=row(statement_timestamp()+'1 day'::interval, null)::fuzzytimestamptz as equals_tomorrow,
          fuzzy=row(date_trunc('month', statement_timestamp()), 'month')::fuzzytimestamptz as equals_fuzzymonth,
          fuzzy=row(date_trunc('month', statement_timestamp()+'1 month'::interval), 'month')::fuzzytimestamptz as equals_fuzzynextmonth
from t;
 id |               fuzzy                | equals_now | equals_tomorrow | equals_fuzzymonth | equals_fuzzynextmonth
----+------------------------------------+------------+-----------------+-------------------+-----------------------
  1 | ("2011-01-01 00:00:00+00",year)    | t          | t               | t                 | t
  2 | ("2011-09-01 00:00:00+01",month)   | t          | t               | t                 | f
  3 | ("2011-09-24 00:00:00+01",day)     | t          | f               | t                 | f
  4 | ("2011-09-24 11:45:23.810589+01",) | f          | f               | t                 | f

Những ràng buộc đó có thể được áp dụng bằng cách tạo DOMAIN, để tránh lặp lại chúng trên mỗi trường không?
bignose

Không, rất tiếc là không, hãy xem nhận xét thứ hai của tôi về câu trả lời của Catcall. Tôi tin rằng bạn có thể tạo một hàm tạo tùy chỉnh nhưng điều đó sẽ không ngăn cản sửa đổi sau này
Jack nói hãy thử topanswers.xyz
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.