第106回 Oracleのお勉強 GROUP BYを使って取得するものは一度に取得できないか考える

次の表で考えてみよう。

SQL> select * from (
  2    select sysdate   日付,  1 管理番号 from dual union
  3    select sysdate   日付,  2 管理番号 from dual union
  4    select sysdate   日付,  3 管理番号 from dual union
  5    select sysdate+1 日付,  1 管理番号 from dual union
  6    select sysdate+1 日付,  2 管理番号 from dual union
  7    select sysdate+1 日付,  3 管理番号 from dual
  8  );

日付       管理番号
-------- ----------
09-05-30          1
09-05-30          2
09-05-30          3
09-05-31          1
09-05-31          2
09-05-31          3

6行が選択されました。

日付ごとに最小管理番号、最大管理番号を求める


まず、効率の悪い例

最小を求める
SQL> select
  2    to_char(t1.日付, 'YYYY/MM/DD')    日付,
  3    min(t1.管理番号)                  最小管理番号
  4  from (
  5    select sysdate   日付,  1 管理番号 from dual union
  6    select sysdate   日付,  2 管理番号 from dual union
  7    select sysdate   日付,  3 管理番号 from dual union
  8    select sysdate+1 日付,  1 管理番号 from dual union
  9    select sysdate+1 日付,  2 管理番号 from dual union
 10    select sysdate+1 日付,  3 管理番号 from dual
 11  ) t1
 12  group by
 13    to_char(t1.日付, 'YYYY/MM/DD')
 14  order by
 15    to_char(t1.日付, 'YYYY/MM/DD')
 16  ;

日付                 最小管理番号
-------------------- ------------
2009/05/30                      1
2009/05/31                      1
最大を求める
SQL> select
  2    to_char(t2.日付, 'YYYY/MM/DD')    日付,
  3    max(t2.管理番号)                  最大管理番号
  4  from (
  5    select sysdate   日付,  1 管理番号 from dual union
  6    select sysdate   日付,  2 管理番号 from dual union
  7    select sysdate   日付,  3 管理番号 from dual union
  8    select sysdate+1 日付,  1 管理番号 from dual union
  9    select sysdate+1 日付,  2 管理番号 from dual union
 10    select sysdate+1 日付,  3 管理番号 from dual
 11  ) t2
 12  group by
 13    to_char(t2.日付, 'YYYY/MM/DD')
 14  order by
 15    to_char(t2.日付, 'YYYY/MM/DD')
 16  ;

日付                 最大管理番号
-------------------- ------------
2009/05/30                      3
2009/05/31                      3

最小と最大を日付ごとに結合

SQL> select
  2    t1_min.日付           日付,
  3    t1_min.最小管理番号   最小管理番号,
  4    t2_max.最大管理番号   最大管理番号
  5  from
  6    (
  7      select
  8        to_char(t1.日付, 'YYYY/MM/DD')    日付,
  9        min(t1.管理番号)                  最小管理番号
 10      from (
 11        select sysdate   日付,  1 管理番号 from dual union
 12        select sysdate   日付,  2 管理番号 from dual union
 13        select sysdate   日付,  3 管理番号 from dual union
 14        select sysdate+1 日付,  1 管理番号 from dual union
 15        select sysdate+1 日付,  2 管理番号 from dual union
 16        select sysdate+1 日付,  3 管理番号 from dual
 17      ) t1
 18      group by
 19      to_char(t1.日付, 'YYYY/MM/DD')
 20    ) t1_min,
 21    (
 22      select
 23        to_char(t2.日付, 'YYYY/MM/DD')    日付,
 24        max(t2.管理番号)                  最大管理番号
 25      from (
 26        select sysdate   日付,  1 管理番号 from dual union
 27        select sysdate   日付,  2 管理番号 from dual union
 28        select sysdate   日付,  3 管理番号 from dual union
 29        select sysdate+1 日付,  1 管理番号 from dual union
 30        select sysdate+1 日付,  2 管理番号 from dual union
 31        select sysdate+1 日付,  3 管理番号 from dual
 32      ) t2
 33      group by
 34      to_char(t2.日付, 'YYYY/MM/DD')
 35    ) t2_max
 36    where
 37      t1_min.日付     =     t2_max.日付
 38    order by
 39      t1_min.日付
 40  ;

日付                 最小管理番号 最大管理番号
-------------------- ------------ ------------
2009/05/30                      1            3
2009/05/31                      1            3

しかし、これはやはりかなり非効率だ。




効率的なやり方  (1度のGROUP BYで最小と最大はとれる)

SQL> select
  2    to_char(t1.日付, 'YYYY/MM/DD')    日付,
  3    min(t1.管理番号)                  最小管理番号,
  4    max(t1.管理番号)                  最大管理番号
  5  from (
  6    select sysdate   日付,  1 管理番号 from dual union
  7    select sysdate   日付,  2 管理番号 from dual union
  8    select sysdate   日付,  3 管理番号 from dual union
  9    select sysdate+1 日付,  1 管理番号 from dual union
 10    select sysdate+1 日付,  2 管理番号 from dual union
 11    select sysdate+1 日付,  3 管理番号 from dual
 12  ) t1
 13  group by
 14    to_char(t1.日付, 'YYYY/MM/DD')
 15  order by
 16    to_char(t1.日付, 'YYYY/MM/DD')
 17  ;

日付                 最小管理番号 最大管理番号
-------------------- ------------ ------------
2009/05/30                      1            3
2009/05/31                      1            3

これが効率のよいやり方(というか普通はこうする)。