0

many queries are by week, month or quarter when the base table date is either date or timestamp.

in general, in group by queries, does it matter whether using - functions on the date - a day table that has extraction pre-calculated

note: similar question as DATE lookup table (1990/01/01:2041/12/31)

for example, in postgresql

create table sale(
  tran_id   serial       primary key,
  tran_dt   date         not null default current_date,
  sale_amt  decimal(8,2) not null,
  ...
);

create table days(
  day       date      primary key,
  week      date      not null,
  month     date      not null,
  quarter   date      non null
);

-- week query 1: group using funcs
select
  date_trunc('week',tran_dt)::date - 1 as week,
  count(1) as sale_ct,
  sum(sale_amt) as sale_amt
from sale
where date_trunc('week',tran_dt)::date - 1 between '2012-1-1' and '2011-12-31'
group by date_trunc('week',tran_dt)::date - 1
order by 1;

-- query 2: group using days
select
  days.week,
  count(1) as sale_ct,
  sum(sale_amt) as sale_amt
from sale
join days on( days.day = sale.tran_dt )
where week between '2011-1-1'::date and '2011-12-31'::date
group by week
order by week;

to me, whereas the date_trunc() function seems more organic, the the days table is easier to use.

is there anything here more than a matter of taste?

4

3 に答える 3

2
-- query 3: group using instant "immediate" calendar table
WITH calender AS (
        SELECT  ser::date AS dd
        , date_trunc('week', ser)::date AS wk
        -- , date_trunc('month', ser)::date AS mon
        -- , date_trunc('quarter', ser)::date AS qq
        FROM generate_series( '2012-1-1' , '2012-12-31', '1 day'::interval) ser
        )
SELECT
  cal.wk
  , count(1) as sale_ct
  , sum(sa.sale_amt) as sale_amt
FROM sale sa
JOIN calender cal ON cal.dd = sa.tran_dt
-- WHERE week between '2012-1-1' and '2011-12-31'
GROUP BY cal.wk
ORDER BY cal.wk
        ;

注: BETWEEN 範囲の明らかなタイプミスを修正しました。

更新: Erwin の再帰 CTE を使用して、重複した date_trunc() を絞り出しました。ネストされた CTE 豊富:

WITH calendar AS (
        WITH RECURSIVE montag AS (
        SELECT '2011-01-01'::date  AS dd
        UNION ALL
        SELECT dd + 1 AS dd
        FROM   montag
        WHERE  dd < '2012-1-1'::date
        )
    SELECT mo.dd, date_trunc('week', mo.dd + 1)::date AS wk
    FROM montag mo
    )
SELECT
  cal.wk
  , count(1) as sale_ct
  , sum(sa.sale_amt) as sale_amt
FROM sale sa
JOIN calendar cal ON cal.dd = sa.tran_dt
-- WHERE week between '2012-1-1' and '2011-12-31'
GROUP BY cal.wk
ORDER BY cal.wk
        ;
于 2012-06-02T17:24:15.467 に答える
1

1.あなたの表現:

... '2012-1-1' と '2011-12-31' の間

動作しません。BasicBETWEENでは、左の引数が右の引数以下である必要があります。次のようにする必要があります。

... BETWEEN SYMMETRIC '2012-1-1' and '2011-12-31'

または、それは単なるタイプミスであり、次のような意味です。

... BETWEEN '2011-1-1' and '2011-12-31'

あなたのクエリが何を取得することになっているのか、私にはわかりません。この回答の残りの部分では、2011 年に始まるすべての週 (月曜日から日曜日) が必要であると仮定します。この式は、最新のハードウェアで正確に 1 マイクロ秒未満でそれを生成します (どの年でも機能します)。

SELECT generate_series(
        date_trunc('week','2010-12-31'::date) + interval '7d'
       ,date_trunc('week','2011-12-31'::date) + interval '6d'
       , '1d')::date

*「年の最初の週」のISO 8601 定義は若干異なることに注意してください。

2. 2 番目のクエリがまったく機能しません。いいえGROUP BY

3.リンク先の質問は、日付/タイムスタンプのサポートが優れているPostgreSQLを扱っていません。そして、generate_series()上で示したように、ほとんどの場合、別の「日」テーブルの必要性をなくすことができます。クエリは次のようになります。

それまでの間、@wildplasser は、ここにあるはずのサンプル クエリを提供しました。

人気のある* 要求により、再帰的な CTE バージョン - 実際には、深刻な代替手段とはほど遠いものではありません!
* 「人気」とは、@wildplasser の非常に深刻な要求を意味します。

WITH RECURSIVE days AS (
    SELECT '2011-01-01'::date  AS dd
          ,date_trunc('week', '2011-01-01'::date )::date AS wk

    UNION ALL
    SELECT dd + 1
          ,date_trunc('week', dd + 1)::date AS wk
    FROM   days
    WHERE  dd < '2011-12-31'::date
    )
SELECT d.wk
      ,count(*) AS sale_ct
      ,sum(s.sale_amt) AS sale_amt
FROM days d
JOIN sale s ON s.tran_dt = d.dd
-- WHERE d.wk between '2011-01-01' and '2011-12-31'
GROUP BY 1
ORDER BY 1;

次のように書くこともできます ( @wildplasser のバージョンと比較してください):

WITH RECURSIVE d AS (
    SELECT '2011-01-01'::date AS dd
    UNION ALL
    SELECT dd + 1 FROM d WHERE dd < '2011-12-31'::date
    ), days AS (
    SELECT dd, date_trunc('week', dd + 1)::date AS wk
    FROM d
    )
SELECT ...

4.パフォーマンスが重要な場合は、テーブルの値に関数や計算を適用しないようにしてください。これにより、インデックスの使用が禁止され、すべての行を処理する必要があるため、通常は非常に遅くなります。それが、最初のクエリが大きなテーブルでうまくいかない理由です。可能であれば、代わりに、フィルターに使用する値に計算を適用してください。

式のインデックスは、これを回避する 1 つの方法です。次のようなインデックスがある場合

CREATE INDEX sale_tran_dt_week_idx ON sale (date_trunc('week', tran_dt)::date);

.. 最初のクエリは再び非常に高速になる可能性があります。インデックスのメンテナンスのための書き込み操作に多少のコストがかかります。

于 2012-06-02T17:37:03.640 に答える
1

はい、それは好みの問題ではありません。クエリのパフォーマンスはメソッドによって異なります。

最初の概算として、関数はより高速になるはずです。結合を必要とせず、単一のテーブル スキャンで読み取りを行います。

ただし、優れたオプティマイザーはルックアップ テーブルを効果的に使用できます。目標値の分布がわかります。また、メモリ内結合は非常に高速になる可能性があります。

データベースの設計として、カレンダー テーブルがあると非常に便利だと思います。休日などの一部の情報は、関数として機能しません。ただし、ほとんどのアドホック クエリでは、日付関数で問題ありません。

于 2012-06-02T03:33:13.220 に答える