前回は connectPostgreSQL でコネクションを確立して quickQuery'で読み出しましたが、今回は prepare を使った読み出しです。
SQLの「PREPARE で SQL を準備し、EXECUTE によりパラメータのみの実行」に相当します。
-- ghc --make -o sel -LC:\PostgreSQL\8.4\lib select.hs main = do conn <- connectPostgreSQL "host=localhost port=5433 dbname=test" stmt <- prepare conn "SELECT id,code from address" execute stmt [] -- fetchAllRowsAL:この関数を変えると出力されるデータの型が変わる。 results <- fetchAllRowsAL stmt -- この場合、テーブルの件数にかかわらず、読み出されるのは2行。 print $ take 2 results disconnect conn
-- アポストロフィーのない関数は遅延読み込み results <- fetchAllRowsAL stmt -- fetchAllRows stmt のとき [[SqlInteger 1,SqlInteger 1],[SqlInteger 2,SqlInteger 5]] -- sFetchAllRows' stmt [[Just "1",Just "1"],[Just "2",Just "5"]] -- fetchAllRowsAL が返すのはカラム名とデータのタプル。 [[("id",SqlInteger 1),("code",SqlInteger 1)],[("id",SqlInteger 2),("code",SqlInteger 5)]]
- fetchAllRowsAL :カラム名とデータのタプルを返します。
- fetchAllRows :SqlValue のリストを返します。
- 頭に s のついた関数は Maybe 型 を返します。
sFetchAllRows :: Statement -> IO [[Maybe String]] sFetchAllRows' :: Statement -> IO [[Maybe String]] sFetchRow :: Statement -> IO (Maybe [Maybe String]) sRun :: (IConnection conn) => conn -> String -> [Maybe String] -> IO Integer
「遅延読み込み」
同名の関数で末尾にアポストロフィーのあるものとないものがあります。アポストロフィーのないものは遅延読み込みを行い、アポストロフィーのあるものは正格読み込みを行うものです。(foldl と foldl' の違いのようなもの)
- results1 <- fetchAllRowsAL stmt -- この時点で読み込みは行なわれない
- results2 <- fetchAllRowsAL' stmt -- この時点で全部読み込む。
results1 はプログラム上は全データが読み込まれたリストですが、実際は空のリスト。実際にデータが必要なときにDBから読み込まれます。
fetchAllRows :: Statement -> IO [[SqlValue]] fetchAllRows' :: Statement -> IO [[SqlValue]] fetchAllRowsAL :: Statement -> IO [[(String, SqlValue)]] fetchAllRowsAL' :: Statement -> IO [[(String, SqlValue)]] fetchAllRowsMap :: Statement -> IO [Data.Map.Map String SqlValue] fetchAllRowsMap' :: Statement -> IO [Data.Map.Map String SqlValue] fetchRowAL :: Statement -> IO (Maybe [(String, SqlValue)]) fetchRowMap :: Statement -> IO (Maybe (Data.Map.Map String SqlValue)) quickQuery :: (IConnection conn) => conn -> String -> [SqlValue] -> IO [[SqlValue]] quickQuery' :: (IConnection conn) => conn -> String -> [SqlValue] -> IO [[SqlValue]]
>conn <- connectPostgreSQL "host=localhost port=5433 dbname=daname" >stmt <- prepare conn "SELECT * from address" >execute stmt [] > getTables conn -- => ["address","area"] > proxiedClientName conn -- => "postgresql" > dbServerVer conn -- => "80405" > dbTransactionSupport conn -- => True > getColumnNames stmt -- => ["id","code","name","kana","post","uptime","memo"] > originalQuery stmt -- => "SELECT * from address" > results <- fetchAllRowsAL stmt > mapM_ (putStrLn.concat.(map (\(name,dat)->(name++": "++ ( ucs4ToSjis (fromSql dat)::String)++" ")))) results id: 1 code: 1 name: 住所 kana: post: 272-0001 uptime: 2010-12-18 15:28:04.597 +0900 memo: id: 2 code: 5 name: 市川 kana: post: 272-0002 uptime: 2010-12-18 15:28:04.604 +0900 memo: (snip) id: 31 code: 84 name: 千葉 kana: post: 272-1234 uptime: 2010-12-18 15:28:04.705 +0900 memo: -- 二度目の fetchAllRowsAL は空のリストを返す。 > results <- fetchAllRowsAL stmt -- => []
- 巨大なテーブルを1件ずつ遅延読み込みすることが出来ます。
>conn <- connectPostgreSQL "host=localhost port=5433 dbname=daname" >stmt <- prepare conn "SELECT * from address" >execute stmt [] -- => 0 -- fetchRow を実行する度に1件ずつ読み込みます。 > fetchRow stmt -- => Just [SqlInteger 1,SqlInteger 1,SqlByteString "272-0001",SqlZonedTime 2010-12-18 15:28:04.597 +0900] > fetchRow stmt -- => Just [SqlInteger 2,SqlInteger 5,SqlByteString "272-0002",SqlZonedTime 2010-12-18 15:28:04.604 +0900] > fetchRow stmt -- => Just [SqlInteger 3,SqlInteger 12,SqlByteString "272-1234",SqlZonedTime 2010-12-18 15:28:04.617 +0900,] (snip) -- データが空になるとNothingを返します。 > fetchRow stmt -- => Nothing > disconnect conn
これは SQL で DECLARE を宣言し、FETCH で1行ずつ取り出したときと同じ動作です。
>psql -p 5433 dbname psql (8.4.5) "help" でヘルプを表示します. -- トランザクション開始。 dbname=# BEGIN WORK; BEGIN -- カーソル設定。 dbname=# DECLARE stmt SCROLL CURSOR FOR SELECT id, code, post, uptime from jusho_mst; DECLARE CURSOR -- stmt カーソルから最初の1行を取り出す。 dbname=# FETCH FORWARD 1 FROM stmt; id | code | post | uptime ----+------+----------+---------------------------- 1 | 1 | 272-0035 | 2010-12-18 15:28:04.597+09 (1 行) dbname=# FETCH FORWARD 10 FROM stmt; id | code | post | uptime ----+------+----------+---------------------------- 2 | 5 | 272-0035 | 2010-12-18 15:28:04.604+09 3 | 12 | 272-0031 | 2010-12-18 15:28:04.617+09 4 | 31 | 272-0824 | 2010-12-18 15:28:04.619+09 5 | 32 | 272-0824 | 2010-12-18 15:28:04.621+09 6 | 33 | 272-0824 | 2010-12-18 15:28:04.622+09 7 | 34 | 272-0824 | 2010-12-18 15:28:04.624+09 8 | 35 | 272-0824 | 2010-12-18 15:28:04.626+09 9 | 36 | 272-0824 | 2010-12-18 15:28:04.634+09 10 | 41 | 272-0034 | 2010-12-18 15:28:04.636+09 11 | 42 | 272-0034 | 2010-12-18 15:28:04.638+09 (10 行) -- データがなくなると dbname=# FETCH FORWARD 1 FROM stmt; id | code | post | uptime ----+------+------+-------- (0 行) -- カーソルを閉じる。 dbname=# CLOSE stmt; CLOSE CURSOR -- トランザクションを終了。 dbname=# COMMIT WORK; COMMIT