1

これまでのところ、SQLに関しては独学で学んでいますが、自分で教えることの問題は、修正をハックすることはよくありますが、目標を達成するためのベストプラクティスではない可能性があることです。次のコードは、これらのハックの1つです。

select breed, sum(mar_11) as mar_11, sum(apr_11) as apr_11
from (
  select d.breed, d.qty as mar_11, 0 as apr_11
  from dogs d
  where d.entry_date between '01-MAR-11' and '31-MAR-11'
  union all
  select d.breed, 0 as mar_11, d.qty as apr_11
  from dogs d
  where d.entry_date between '01-APR-11' and '30-APR-11'
) t
group by breed;

上記のコードの達成された目標は、3月と4月の両方で生まれた犬の総数を示すフォーマットされたレポートを提供することです。これは、いくつかの期間を扱っている場合は素晴らしいですが、20〜30の異なる期間に移動すると、コードは面倒で反復的になります。

私が考案した2番目のハックは、レポートを動的にし、毎月再コーディングすることなく過去6か月を表示することです。これは、コマンドプロンプトを介して変数を渡すbashスクリプトとGNUDateを使用して実現しました。

(e.g.)
sqlplus64 ilove/dogs@dogville @myreport var1 var2 var3 var4

私の質問は次のとおりです。

1)SQLステートメントをより効率的にするために(つまり、日付を変更して同じコードを何度も繰り返さないようにするために)どのツールを使用できますか?

2)私が達成しようとしていることは、良い習慣と見なされていても(または、ピボットテーブルを介して後処理を行う必要があります)?

3)bashやGNU Dateを必要とせずに、SQLステートメントを動的にするにはどうすればよいですか?

4

6 に答える 6

2
select
  breed,
  SUM(CASE WHEN entry_date BETWEEN '01-Mar-11' AND '31-Mar-11' THEN qty ELSE 0 END) AS mar_11,
  SUM(CASE WHEN entry_date BETWEEN '01-APR-11' AND '30-APR-11' THEN qty ELSE 0 END) AS apr_11
FROM
  dogs
WHERE
  entry_date BETWEEN '01-Mar-11' AND '30-Apr-11'
GROUP BY
  breed

編集:今月と過去5か月

select
  breed,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -5)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -4)
           THEN qty ELSE 0 END)                                      AS month_1,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -4)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3)
           THEN qty ELSE 0 END)                                      AS month_2,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -3)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -2)
           THEN qty ELSE 0 END)                                      AS month_3,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -2)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
           THEN qty ELSE 0 END)                                      AS month_4,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -1)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'),  0)
           THEN qty ELSE 0 END)                                      AS month_5,
  SUM(CASE WHEN entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'),  0)
            AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'),  1)
           THEN qty ELSE 0 END)                                      AS month_6
FROM
  dogs
WHERE
      entry_date >= ADD_MONTHS(TRUNC(SYSDATE, 'MM'), -5)
  AND entry_date <  ADD_MONTHS(TRUNC(SYSDATE, 'MM'),  1)
GROUP BY
  breed
于 2012-08-08T13:19:06.963 に答える
2

@Dems の 6 か月スパンのクエリ (コメントには大きすぎる) へのマイナーな (軽薄ではないにしても) 追加として、SQL*Plus を使用しているため、そのnew_value句と 置換変数を使用して、列見出しを一致するように設定できます。月。

同じadd_months(trunc(sysdate, 'MM'), n)方法を使用して、過去 6 か月のそれぞれに一致するテキスト ラベルを生成できます。好きな形式を使用でき'Mon RR'ます。元のハードコードされた列名に近づけるために行ってきました。生成された値のそれぞれは、x_month_1などのようにエイリアスされます。このcolumn ... new_value ...句は、スクリプトの後半で、 として参照できる置換変数として使用できるようにし&y_month_1ます。

column x_month_1 new_value y_month_1;
column x_month_2 new_value y_month_2;
column x_month_3 new_value y_month_3;
column x_month_4 new_value y_month_4;
column x_month_5 new_value y_month_5;
column x_month_6 new_value y_month_6;

set termout off

select to_char(add_months(trunc(sysdate, 'MM'), -5), 'Mon RR') as x_month_1,
       to_char(add_months(trunc(sysdate, 'MM'), -4), 'Mon RR') as x_month_2,
       to_char(add_months(trunc(sysdate, 'MM'), -3), 'Mon RR') as x_month_3,
       to_char(add_months(trunc(sysdate, 'MM'), -2), 'Mon RR') as x_month_4,
       to_char(add_months(trunc(sysdate, 'MM'), -1), 'Mon RR') as x_month_5,
       to_char(trunc(sysdate, 'MM'), 'Mon RR') as x_month_6
from dual;

set termout on
set verify off

termout off/onは、これが出力に表示されるのを停止verify offし、置換変数が使用されている場合は通知を停止します。

次にAS、Dem のクエリのエイリアス句を変更するだけです。代わりにAS month_1使用AS "&y_month_1"。スペースを含む日付形式を選択したため、二重引用符が必要です。これにより、エラーが発生しなくなります。

または、プレゼンテーションとクエリを分けておきたい場合は、エイリアスをそのままにして、column代わりに別の句を使用してこれを実現します。

column month_6 heading "&y_month_6"

いずれにせよ、次のような結果が得られます。

BREED          Mar 12     Apr 12     May 12     Jun 12     Jul 12     Aug 12
---------- ---------- ---------- ---------- ---------- ---------- ----------
Beagle              1          4          0          0          0          0
...
于 2012-08-08T14:13:31.663 に答える
1

group byを使用して、犬を月と年にグループ化できます。

SELECT 
    breed, EXTRACT(MONTH FROM entry_date) AS month, EXTRACT(YEAR FROM entry_date) AS year, sum(qty) AS number_of_dogs
FROM
    dogs
GROUP BY 
    breed, EXTRACT(MONTH FROM entry_date), EXTRACT(YEAR FROM entry_date)

次に、特定の時間枠に結果をフィルタリングするロジックを追加できます。例えば:

SELECT *
FROM
(
    SELECT 
        breed, EXTRACT(MONTH FROM entry_date) AS month, EXTRACT(YEAR FROM entry_date) AS year, sum(qty) AS number_of_dogs
    FROM
        dogs
    GROUP BY 
        breed, EXTRACT(MONTH FROM entry_date), EXTRACT(YEAR FROM entry_date)
)
WHERE
    year = 2012 AND month < 8

今年1月から7月に生まれた犬の数を教えてください

于 2012-08-08T13:19:00.787 に答える
1

私はあなたが望むことをしていると思います:

select to_char(d.entry_date, 'YYYY-MM') as yyyymm, breed,
       sum(d_qty)
from dogs d
group by to_char(d.entry_date, 'YYYY-MM'), breed
order by 1, 2

これにより、月ごとに個別の行が作成され、繁殖します。これらを1行で取得する必要がある場合は、PIVOT構文を使用するか、Excelでピボットを実行します。

Excelでのピボットで十分である可能性は十分にあります。明示的にすることでピボットすることもできます(他のレスポンダーが提案しているように):

select breed,
       sum(case when to_char(d.entry_date, 'YYYY-MM') = '2012-06' then d_qty end) as qty_201206,
       sum(case when to_char(d.entry_date, 'YYYY-MM') = '2012-05' then d_qty end) as qty_201205,
       ...
from dogs d
group by breed
order by 1
于 2012-08-08T13:19:50.020 に答える
0

見てPIVOT

http://www.orafaq.com/wiki/PIVOT

またEXTRACT、またはTO_CHAR日付をフィルタリングするには

于 2012-08-08T13:18:35.073 に答える
0

Oracle では、このようにピボットするために一般的に使用されるイディオムがあり、通常はSUM(DECODE(...)))orを含みSUM(CASE WHEN...THEN...)ます。

select breed
,      sum(decode(TO_CHAR(entry_date,'YYYYMM'),'201103',qty,0) mar_11
,      sum(decode(TO_CHAR(entry_date,'YYYYMM'),'201104',qty,0) apr_11
from dogs d
group by breed;

これが役立つことを願っています。

于 2012-08-08T13:17:51.457 に答える