2012-07-02 51 views
6

Tôi có bảng với một trong các cột là ngày theo định dạng 'YYYY-MM-DD'. Tôi có thể sử dụng lựa chọn để nhận tất cả dữ liệu trong phạm vi hàng tháng không? Giả sử tôi muốn tất cả dữ liệu từ 2012-01-xx đến 2013-04-xx. Vì vậy, tôi về cơ bản tìm kiếm một truy vấn SQL như một đưa ra dưới đây:Lựa chọn Postgresql giữa phạm vi tháng

SELECT * FROM table WHERE date IN BETWEEN '2012-01' AND '2013-04' (INVALID QUERY) 

Vì mỗi tháng bắt đầu với '01' Tôi có thể sửa đổi các truy vấn trên để điều chỉnh điều kiện bắt đầu.

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND '2013-04' (INVALID QUERY) 

Bây giờ vấn đề đi kèm với ngày kết thúc. Tôi phải tính toán ngày cuối cùng của tháng cụ thể theo cách thủ công, lấy tất cả các yếu tố trong tài khoản như độ dài của tháng, năm nhuận vv, vì truy vấn không thành công nếu ngày đã cho không hợp lệ. Vì vậy, hiện tại tôi đang làm một việc như sau:

SELECT * FROM table WHERE date IN BETWEEN '2012-01-01' AND 'VALID_MONTH_END_DATE' (VALID Query) 

Tôi muốn biết liệu có cách nào để tránh tính toán ngày kết thúc hợp lệ này không?

Làm rõ

Tôi đã nghĩ trên ngày đầu tiên của tháng tiếp theo, nhưng thậm chí sau đó tôi sẽ phải áp dụng một số logic nói, nếu tháng mười hai của mình, vào tháng tới sẽ là tháng Giêng năm tới. Tôi muốn biết nếu một giải pháp chỉ SQL là có thể?

Trả lời

4

Đây là một nhu cầu rất phổ biến trong môi trường báo cáo.Tôi đã tạo ra một số chức năng để thích ứng với những thao tác ngày

CREATE OR REPLACE FUNCTION public.fn_getlastofmonth (
    date 
) 
RETURNS date AS 
$body$ 
begin 
    return (to_char(($1 + interval '1 month'),'YYYY-MM') || '-01')::date - 1; 
end; 
$body$ 
LANGUAGE 'plpgsql' 
VOLATILE 
CALLED ON NULL INPUT 
SECURITY INVOKER 
COST 100; 

Sau đó, bạn có thể sử dụng ...

WHERE date >= '2012-01-01' 
    AND date < fn_getlastofmonth('2013-04-01') 
10

Bạn nên tránh BETWEEN để so sánh phạm vi ngày. Sử dụng tốt hơn >=< vì nó hoạt động bình đẳng với các cột/giá trị ngày và giờ.

Một cách (nếu bạn có thể xây dựng những ngày bên ngoài):

WHERE date >= DATE '2012-01-01' 
    AND date < DATE '2013-05-01'  --- first date of the next month 

Bạn cũng có thể sử dụng ngày số học:

WHERE date >= DATE '2012-01-01' 
    AND date < DATE ('2013-04-01' + INTERVAL '1 MONTH') 

hoặc OVERLAPS điều hành:

WHERE (date, date) OVERLAPS 
     (DATE '2012-01-01', DATE '2013-05-01') 

Bạn cũng nên đọc tài liệu Postgres: Date/Time Functions and Operators

Các manual explains here tại sao OVERLAPS tác phẩm theo cách này:

Mỗi khoảng thời gian được coi là đại diện cho khoảng nửa mở bắt đầu < = thời gian < kết thúc, trừ khi bắt đầu và kết thúc đều bình đẳng trong trường hợp này nó đại diện đó là ngay lập tức. Điều này có nghĩa là ví dụ: hai khoảng thời gian chỉ với một điểm cuối chung không trùng lặp.

+0

Chông hoạt động tốt khi tôi biết ngày cuối cùng. Tôi bị kẹt khi tháng cuối cùng là tháng 12. –

+0

Bạn lấy thông số từ đâu? Một ứng dụng? Trang web? Một truy vấn khác? Bạn có chúng như là chuỗi hoặc số nguyên (năm, tháng)? –

+1

Việc chăm sóc phải được thực hiện khi thêm 'khoảng thời gian' thành' ngày'. Kết quả là 'dấu thời gian'. Tuy nhiên, hoạt động trong ví dụ trên. Truyền kết quả đến 'ngày', nếu nó quan trọng. Việc thêm khoảng thời gian 'n tháng' luôn dẫn đến cùng một ngày trong tháng, bất kể số ngày thực tế trong các tháng liên quan - hoặc ngày có sẵn tối đa, khi tháng kết quả có ít ngày hơn. Để thêm số ngày chính xác, hãy thêm 'số nguyên' vào' ngày', kết quả này sẽ là 'ngày'. –

0

Tất cả các câu trả lời ở trên cung cấp một giải pháp làm việc nhưng không đầy đủ bằng cách này hay cách khác. Vì tôi đang tìm kiếm một giải pháp SQL duy nhất (Không có chức năng), tôi kết hợp những lời khuyên tốt nhất từ ​​các giải pháp trên.

Các giải pháp lý tưởng cho các câu hỏi của tôi sẽ là:

SELECT * FROM table 
WHERE date >= '2012-01-01' AND date < date('2013-04-01') + interval '1 month' 

EDIT

Tôi không sử dụng chức năng chồng chéo ở đây bởi vì tôi đang đi qua các giá trị ngày mặc định cho bắt đầu và kết thúc như 'thời đại ' và bây giờ'. Nếu người dùng đã không quy định bất kỳ khoảng thời gian truy vấn trở thành:

SELECT * FROM table 
WHERE date >= 'epoch' AND date < 'now' 

chức năng chồng chéo không thể xử lý 'kỷ nguyên' và 'bây giờ' và đưa ra và SQL lỗi khi mã công việc trên một cách hoàn hảo cho cả các trường hợp.

PS: Tôi đã upvoted tất cả các câu trả lời đúng theo cách và dẫn tôi đến giải pháp này.

+0

Tôi nghĩ bạn cần phải loại bỏ phần '- interval '1 ngày''. Hãy thử nó như nó là, với một ''2013-04-30'' ngày trên bàn. –

+0

Tôi đang tìm dữ liệu hàng tháng. Giả sử tôi cần lấy tất cả dữ liệu từ một năm, tháng tới năm yyyy-mm. Ngày kết thúc của tôi luôn là yyyy-mm-01 mà tôi thêm một tháng và trừ một ngày. –

+1

Chỉ cần kiểm tra truy vấn: 'SELECT * FROM table WHERE (ngày, ngày) OVERLAPS ('2012-01-01', ngày ('2013-04-01') + khoảng thời gian '1 tháng' - khoảng thời gian '1 ngày') 'khi có' ngày' trong bảng có giá trị '2013-04-30'. Nó được trả lại? –

0
SET search_path=tmp; 

DROP TABLE zdates; 
CREATE TABLE zdates 
     (zdate timestamp NOT NULL PRIMARY KEY 
     , val INTEGER NOT NULL 
     ); 
-- some data 
INSERT INTO zdates(zdate,val) 
SELECT s, 0 
FROM generate_series('2012-01-01', '2012-12-31', '1 day'::interval) s 
     ; 

UPDATE zdates 
SET val = 1000 * random(); 

DELETE FROM zdates 
WHERE random() < 0.1; 

-- CTE to round the intervals down/up to the begin/end of the month 
WITH zope AS (
     SELECT date_trunc('month', zdate)::date AS zbegin 
     , date_trunc('month', zdate+interval '1 month')::date AS zend 
     , val AS val 
     FROM zdates 
     ) 
SELECT z.zbegin 
     , z.zend 
     , COUNT(*) AS zcount 
     , SUM(val) AS zval 
FROM zope z 
GROUP BY z.zbegin, z.zend 
ORDER BY z.zbegin, z.zend 
     ; 

KẾT QUẢ:

CREATE TABLE 
INSERT 0 366 
UPDATE 366 
DELETE 52 
    zbegin | zend | zcount | zval 
------------+------------+--------+------- 
2012-01-01 | 2012-02-01 |  28 | 13740 
2012-02-01 | 2012-03-01 |  28 | 14923 
2012-03-01 | 2012-04-01 |  26 | 13775 
2012-04-01 | 2012-05-01 |  25 | 11880 
2012-05-01 | 2012-06-01 |  25 | 12693 
2012-06-01 | 2012-07-01 |  25 | 11082 
2012-07-01 | 2012-08-01 |  26 | 13254 
2012-08-01 | 2012-09-01 |  28 | 13632 
2012-09-01 | 2012-10-01 |  28 | 16461 
2012-10-01 | 2012-11-01 |  23 | 12622 
2012-11-01 | 2012-12-01 |  24 | 12554 
2012-12-01 | 2013-01-01 |  28 | 14563 
(12 rows) 
Các vấn đề liên quan