8

「YYYY-MM-DD」形式の日付として列の1つを持つテーブルがあります。select を使用して、月単位の範囲のすべてのデータを取得できますか? 2012-01-xx から 2013-04-xx までのすべてのデータが必要だとします。だから私は基本的に以下のようなSQLクエリを探しています:

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

毎月 '01' で始まるため、上記のクエリを変更して開始条件を調整できます。

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

今、問題は終了日に付属しています。指定された日付が無効な場合、クエリが失敗するため、月の長さ、うるう年などのすべての要因を考慮して、指定された月の最後の日付を手動で計算する必要があります。だから現在、私はこのようなことをしています:

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

この有効な終了日の計算を回避する方法があるかどうかを知りたいですか?

明確化

私は翌月の最初の日以上を考えましたが、それでも12月なら翌月は来年の1月になるという論理を適用する必要があります。SQL のみのソリューションが可能かどうか知りたいですか?

4

5 に答える 5

14

BETWEEN日付範囲の比較は避けることをお勧めします。より適切に使用し、日付および日時の列/値>=<同様に機能します。

1 つの方法 (外部で日付を作成できる場合):

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

日付演算も使用できます。

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

またはOVERLAPSオペレーター:

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

Postgres のドキュメント: Date/Time Functions and Operatorsも読む必要があります。

マニュアルでは、なぜOVERLAPSこのように機能するのかを説明しています。

各期間は、開始と終了が等しい場合を除き、半分開いた間隔の開始 <= 時間 < 終了を表すと見なされます。これは、たとえば、終点のみが共通する 2 つの期間が重複しないことを意味します。

于 2012-07-02T00:13:17.220 に答える
5

これは、レポート環境では非常に一般的なニーズです。これらの日付操作に対応するためにいくつかの関数を作成しました

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;

次に、使用できます...

WHERE date >= '2012-01-01' 
  AND date < fn_getlastofmonth('2013-04-01') 
于 2012-07-02T00:20:59.817 に答える
2

これを試したことはありませんが、試してみる価値があります

SELECT * 
FROM some_table 
WHERE some_date 
BETWEEN '2012-01-01' AND date('2013-04-01') - integer '1'

http://www.postgresql.org/docs/9.1/static/functions-datetime.html

于 2012-07-02T00:15:01.843 に答える
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
        ;

結果:

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)
于 2012-07-02T10:56:00.687 に答える
0

上記のすべての回答は実用的な解決策を提供しますが、何らかの形で不完全です。SQL のみのソリューション (機能なし) を探していたので、上記のソリューションからの最良のヒントを組み合わせています。

私の質問の理想的な解決策は次のとおりです。

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

編集

開始と終了のデフォルトの日付値を「エポック」と「今」として渡しているため、ここではオーバーラップ関数を使用していません。ユーザーが時間範囲を指定していない場合、クエリは次のようになります。

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

オーバーラップ関数は「エポック」と「現在」を処理できず、上記のコードは両方のケースで完全に機能しますが、SQL エラーが発生します。

PS:ある意味で正しいすべての回答に賛成し、この解決策に至りました。

于 2012-07-02T00:52:28.870 に答える