国内予選B
少しSQLの勉強をしてるので,練習に今年の国内予選BをSQLでといてみる.
まず,利用記録は下のようにloginテーブルに格納されているものとする
time(時間) | pid(pc番号) | sid(学生番号) | in(1:ログイン, 0:ログアウト) |
---|---|---|---|
775 | 1 | 1 | 1 |
780 | 4 | 2 | 1 |
790 | 2 | 1 | 1 |
800 | 2 | 1 | 0 |
810 | 3 | 1 | 1 |
820 | 1 | 1 | 0 |
825 | 3 | 1 | 0 |
860 | 1 | 1 | 1 |
870 | 4 | 2 | 0 |
880 | 1 | 1 | 0 |
なお,(time,pid)を主キーとする.問題は期間(t_s,t_e)に学生mがどれだけPCを利用していたかを問い合わせ(t_s,t_e,m)に関して答えろというものなので,まずログインとログアウトをペアにしたビューを作る.
create view loginout AS select L1.time AS login , min(L2.time) AS logout, L1.sid AS sid from (login as L1 , login as L2) where l1.pid = l2.pid and l1.sid = l2.sid and l1.in = 1 and l2.in = 0 and l1.time < l2.time group by l1.time , l1.pid
各loginに対して一番近いlogoutを割り当ててるだけ,上の表に対してviewは下のようになる
login | logout | sid |
---|---|---|
775 | 820 | 1 |
780 | 870 | 2 |
790 | 800 | 1 |
810 | 825 | 1 |
860 | 880 | 1 |
後は,このビューからt_s-t_eに入っている部分の合計を重複なく求めたいのだけど,今回はminuteという540から1260までの連番を入れた補助テーブルを使うことにする(連番の列名はiとする),すると
select count(*) from minute where t_s <= minute.i and minute.i < t_e and exists(select * from loginout where sid=m and login <= minute.i and minute.i < logout)
で問い合わせに対する答えが出る.やってることはt_sからt_eまでの各iについてiが入っているような区間があるかどうか判定しているだけです.