CODE VS 2.1 をSQLでやるとどうなる7
前回、こんな「落ちゲー」の
ブロックを一つ一つ加算していって、合計が10になる組合せをSQLで探しました。
今回は消すお邪魔ブロック(最初から10以上(= 11)のブロック)を探すSQLを書きます。
繋げるか、一旦テーブルに書き出すか。
前回でも充分な長さのある SQL になりました。
更に繋げるのが良いか悪いかは微妙で、当初の設計では、Deletedテーブルに一旦保存するつもりでした。
しかし、繋げた方がパフォーマンスが出そうなので繋げることにします。
嫌な人は、前回の結果をテーブルに書き出してから続けましょう。
前回は4方向に加算していきましたが、今回は消えるブロックの周りにあるお邪魔ブロックを探すことになりますので、1回だけですみます(再帰なし)また、お邪魔ブロックからは逆向きに探してこないため、4方向ではなく8方向を調査することになります。
4方向用のDirection4テーブルと、8方向用のDirection8テーブルの2つ作っているのは、単純にサブクエリーが増えるのを避けたかったからだけで、サブクエリーでも良いと思う人は、Direction8に区分を追加してサブクエリーでやってください。
ちと長い……。
WITH cb (GameID, SimulationID, Turn, Row, Col , ERow, ECol, Num, Amount, Cnt, Angle, Memo) -- 4方向に合算された結果 AS( SELECT GameID , SimulationID , Turn , Row , Col , Row AS ERow , Col AS ECol , Num , Num AS amount , 1 AS cnt , 0 --調べている位置 , CAST(N'調査' AS nvarchar(8)) AS Memo FROM Board WHERE GameID =-1 AND Turn = 0 AND Num < 10 UNION ALL SELECT cb.GameID , cb.SimulationID , cb.Turn , cb.Row -- 調査元の行 , cb.Col -- 調査元の列 , bd.Row -- 調査先の行 , bd.Col -- 調査先の列 , bd.Num , cb.Amount + bd.Num , Cnt + 1 , dr.Angle , dr.Memo FROM Board AS bd INNER JOIN (cb CROSS JOIN Direction4 AS dr) ON bd.Row = cb.Row + (dr.RowOffset * Cnt) AND bd.Col = cb.Col + (dr.ColOffset * Cnt) AND bd.GameID = cb.GameID AND bd.SimulationID = cb.SimulationID AND bd.turn = cb.turn WHERE cb.amount + bd.num <= 10 ) -- SELECT * FROM cb; -- として、以下をコメントアウトすれば4方向に合算した結果が確認できる , eb (GameID, SimulationID, Turn, Row, Col , ERow, ECol, Num, Amount, Cnt, Angle, Memo) -- 消すブロック AS( SELECT * FROM cb AS bd WHERE EXISTS ( SELECT * FROM cb WHERE bd.row = cb.row AND bd.col = cb.col AND (bd.angle = 0 OR bd.angle = cb.angle) AND cb.amount = 10 ) ) -- WITHの終わり -- SELECT * FROM cb; -- として、以下をコメントアウトすれば4方向に合算した結果が確認できる -- SELECT * FROM eb; -- として、以下をコメントアウトすれば消すべきブロックを確認できる , ob (Row, Col, ERow, ECol, Num, Angle, Memo) -- お邪魔ブロック AS( SELECT eb.ERow -- 調査元の行 , eb.ECol -- 調査元の列 , bd.Row -- 調査先の行 , bd.Col -- 調査先の列 , bd.Num , dr.Angle , dr.Memo FROM eb -- 上のSQLで作成した消すべきブロックの一覧 INNER JOIN (Board AS bd CROSS JOIN Direction8 AS dr) ON eb.ERow + dr.RowOffset = bd.Row AND eb.ECol + dr.ColOffset = bd.Col WHERE bd.GameID = -1 AND bd.Turn = 0 AND bd.Num > 10 ) -- SELECT * FROM cb; --として、以下をコメントアウトすれば4方向に合計していったときの結果が確認できる -- SELECT * FROM eb; --として、以下をコメントアウトすれば消すべきブロックを確認できる -- SELECT * FROM ob; --として、以下をコメントアウトすれば消すべきお邪魔ブロックが確認できる , curEb (ERow, ECol) -- このチェインで消すブロック(重複あり) AS ( SELECT ERow, ECol FROM eb -- 通常のブロックは重複あり UNION ALL SELECT ERow, ECol FROM ob GROUP BY ERow, ECol -- お邪魔ブロックは重複なし ) -- SELECT * FROM cb; --として、以下をコメントアウトすれば4方向に合計していったときの結果が確認できる -- SELECT * FROM eb; --として、以下をコメントアウトすれば消すべきブロックが確認できる -- SELECT * FROM ob; --として、以下をコメントアウトすれば消すべきお邪魔ブロックが確認できる SELECT * FROM curEb; --今回のチェインで消すべきブロックの数が確認出来る。
つづく