Life Goes On

まあまあだけど楽しんでる方です

MS Accessの最適化のナゾ

以前、ちょっと遅くて困っていたSQLがありました。
元はこれ。

-- Ver. 1

select id, max(time)
from (
  select
    id,
    time,
    (select count(*) from log where id = t1.id and time < t1.time) as count,
    (select count(*) from log where id = t1.id group by id) as total
  from log t1)
where count < total * 0.9
group by id;

会社の人に聞いてまわって、教えてもらったのがこれ。
totalを求める副問合せを外に出して、結合しています。

-- Ver. 2

select t1.id, max(t1.time)
from (
  select
    id,
    time,
    (select count(*) from log where id = t2.id and time < t2.time) as count
  from log t2) t1
inner join (select id, count(*) as total from log group by id) t3
  on t1.id = t3.id
where t1.count < t3.total * 0.9
group by t1.id;

最近「副問合せ要らないんじゃない?」と言われて、もう一度見直したのがこれ。
確かにjoinで書けますね。

-- Ver. 3

select t3.id, max(t3.time)
from (
  select t1.id, t1.time, count(*) as count
  from log t1
  left join log t2
    on t1.id = t2.id and t1.time > t2.time
  group by t1.id, t1.time) t3
inner join (select id, count(*) as total from log group by id) t4
  on t3.id = t4.id
where t3.count < t4.total * 0.9
group by t3.id;

で、こいつらの性能を測定してみたところ、Accessの場合とOracleの場合とで、だいぶ振る舞いが違いました。

AccessOracle
1
2
3
全部joinで展開した方が速いというOracleの動作は分かるのですが、Accessの動作は不思議。
どういう最適化をしているのか。謎です。