5

Haskell で SQL クエリを生成し、HDBC を使用して SQLite(3) データベースに送信しています。さて、この関数はクエリを返します:

import Database.HDBC.Sqlite3 
import Database.HDBC
data UmeQuery = UmeQuery String [SqlValue] deriving Show

tRunUmeQuery :: UmeQuery -> FilePath -> IO [[SqlValue]]
tRunUmeQuery (UmeQuery q args) dbFile = do
    conn <- connectSqlite3 dbFile
    stat <- prepare conn q
    s <- execute stat args
    res <- fetchAllRows' stat 
    disconnect conn
    return $ res

selectPos targetlt parentlt op pos = let 
    q= "select TARGET.* from levels tl, labeltypes tlt, segments TARGET, 
    (select TARGET.session_id session_id,SECONDARY.labeltype_id labeltype_id, 
    SECONDARY.label_id label_id,min(TARGET.label_id) min_childlabel_id from 
    levels tl, labeltypes tlt, segments TARGET, segments SECONDARY, labeltypes slt, 
    levels sl where TARGET.session_id = SECONDARY.session_id and ((SECONDARY.start 
    <= TARGET.start and TARGET.end <= SECONDARY.end) or (TARGET.start <= SECONDARY.start 
    and SECONDARY.end <= TARGET.end)) and tl.name = ? and sl.name = ? and SECONDARY.label '
    != '' and tl.id = tlt.level_id and sl.id = slt.level_id and tlt.id = TARGET.labeltype_id 
    and slt.id = SECONDARY.labeltype_id group by TARGET.session_id, TARGET.labeltype_id, 
    SECONDARY.label_id) SUMMARY, segments SECONDARY, labeltypes slt, levels sl where 
    TARGET.session_id = SECONDARY.session_id and TARGET.session_id = SUMMARY.session_id 
    and ((SECONDARY.start <= TARGET.start and TARGET.end <= SECONDARY.end) or (TARGET.start 
    <= SECONDARY.start and SECONDARY.end <= TARGET.end)) and tl.name = ? and sl.name = ? 
    and tl.id = tlt.level_id and tlt.id = TARGET.labeltype_id and SUMMARY.labeltype_id = 
    SECONDARY.labeltype_id and SUMMARY.label_id = SECONDARY.label_id and sl.id = slt.level_id 
    and slt.id = SECONDARY.labeltype_id and (TARGET.label_id - SUMMARY.min_childlabel_id +1) = 2 "
    a = [toSql targetlt, toSql parentlt, toSql targetlt, toSql parentlt ]
    in UmeQuery q a

これをデータベースに適用すると、正しいものが返されます。

> let a =selectPos "Word" "Utterance" "=" 2
> let b = tRunUmeQuery a testdb 
> b

出力:

[[SqlByteString "1",SqlByteString "2",SqlByteString "3",SqlByteString "0.149383838383838",SqlByteString "0.312777777777778",SqlByteString "2"],[SqlByteString "1",SqlByteString "2",SqlByteString "6",SqlByteString "0.507488888888889",SqlByteString "0.655905050505051",SqlByteString "fourth"],[SqlByteString "2",SqlByteString "2",SqlByteString "3",SqlByteString "0.149383838383838",SqlByteString "0.312777777777778",SqlByteString "second"],[SqlByteString " 2",SqlByteString "2",SqlByteString "6",SqlByteString "0.507488888888889",SqlByteString "0.655905050505051",SqlByteString "4"],[SqlByteString "3",SqlByteString "2",SqlByteString "3",SqlByteString "0.149383838383838",SqlByteString "0.312777777777778",SqlByteString "秒"],[SqlByteString "3",SqlByteString "2",SqlByteString "6",SqlByteString " 0.507488888888889",SqlByteString "0.655905050505051",SqlByteString "4番目"]]

ここで、次のようにいくつかの小さな動的パーツをクエリに挿入する必要がある場合 (申し訳ありませんが、これを表示するには文字列の最後までスクロールする必要があります):

selectPos targetlt parentlt op pos = let
    q= "select TARGET.* from levels tl, labeltypes tlt, segments TARGET, 
    (select TARGET.session_id session_id,SECONDARY.labeltype_id labeltype_id,
    SECONDARY.label_id label_id,min(TARGET.label_id) min_childlabel_id from 
    levels tl, labeltypes tlt, segments TARGET, segments SECONDARY, labeltypes slt,
     levels sl where TARGET.session_id = SECONDARY.session_id and ((SECONDARY.start 
     <= TARGET.start and TARGET.end <= SECONDARY.end) or (TARGET.start <= SECONDARY.start 
     and SECONDARY.end <= TARGET.end)) and tl.name = ? and sl.name = ? and SECONDARY.label 
     != '' and tl.id = tlt.level_id and sl.id = slt.level_id and tlt.id = TARGET.labeltype_id 
     and slt.id = SECONDARY.labeltype_id group by TARGET.session_id, TARGET.labeltype_id, 
     SECONDARY.label_id) SUMMARY, segments SECONDARY, labeltypes slt, levels sl where 
     TARGET.session_id = SECONDARY.session_id and TARGET.session_id = SUMMARY.session_id 
     and ((SECONDARY.start <= TARGET.start and TARGET.end <= SECONDARY.end) or (TARGET.start
      <= SECONDARY.start and SECONDARY.end <= TARGET.end)) and tl.name = ? and sl.name = ? 
      and tl.id = tlt.level_id and tlt.id = TARGET.labeltype_id and SUMMARY.labeltype_id = 
      SECONDARY.labeltype_id and SUMMARY.label_id = SECONDARY.label_id and sl.id = slt.level_id 
      and slt.id = SECONDARY.labeltype_id and (TARGET.label_id - SUMMARY.min_childlabel_id +1) " 
      ++ op ++ " ? "
    a = [toSql targetlt, toSql parentlt, toSql targetlt, toSql parentlt , toSql pos]
    in UmeQuery q a

同じことをすると、次のようになります。

> let a =selectPos "Word" "Utterance" "=" 2
> let b = tRunUmeQuery a testdb  
> b 

[]

2 番目のクエリが何も返さない (または、実際には同じことを返す) のはなぜですか?

何か案は?

編集:

これはどういうわけかレイジーに関係しているのではないかと考えて、これをさらに調査しました。OK、 は次のように再形成されました。

selectPos :: String -> String -> String -> Integer -> [[SqlValue]]
selectPos targetlt parentlt op pos = let
    q= foldl' (++)  [] ["select TARGET.* from levels tl, labeltypes tlt, segments TARGET, 
    (select TARGET.session_id session_id,SECONDARY.labeltype_id labeltype_id,SECONDARY.label_id 
    label_id,min(TARGET.label_id) min_childlabel_id from levels tl, labeltypes tlt, segments 
    TARGET, segments SECONDARY, labeltypes slt, levels sl where TARGET.session_id = SECONDARY.session_id "
    ,matchstring , " and tl.name = ? and sl.name = ? and SECONDARY.label != '' and tl.id = tlt.level_id 
    and sl.id = slt.level_id and tlt.id = TARGET.labeltype_id and slt.id = SECONDARY.labeltype_id 
    group by TARGET.session_id, TARGET.labeltype_id, SECONDARY.label_id) SUMMARY, segments SECONDARY, 
    labeltypes slt, levels sl where TARGET.session_id = SECONDARY.session_id and TARGET.session_id = 
    SUMMARY.session_id " , matchstring , " and tl.name = ? and sl.name = ? and tl.id = tlt.level_id 
    and tlt.id = TARGET.labeltype_id and SUMMARY.labeltype_id = SECONDARY.labeltype_id and SUMMARY.label_id
     = SECONDARY.label_id and sl.id = slt.level_id and slt.id = SECONDARY.labeltype_id and 
     (TARGET.label_id - SUMMARY.min_childlabel_id +1) " , op , " ? "]  
    a = [toSql targetlt, toSql parentlt, toSql targetlt, toSql parentlt , toSql (pos :: Integer)]
    in UmeQuery q a

残念ながら、これは問題の解決にはなりません (そして、ghci で関数の戻り値を :sprint すると、まだ評価されません)。だから、怠惰がどういうわけか問題かもしれませんが、これを完全に評価する方法がわかりません..?何かアイデアはありますか?

4

1 に答える 1

3

だから... 事実を述べるだけです:

  • コードは実行され、構文エラーや警告は生成されません(これは、haskell と、haskell によって実行される sql の両方に対するものです)
  • 元のクエリは実行されますが、op と pos が追加されていません (既に動的な部分がありました)。
  • 空のセットが返されます (つまり、クエリは行を返しません)...

これらすべてが真実である場合、クエリは有効だが間違っているに違いないと私は信じるようになります。データを確認しますか?クエリをダンプし、手動で実行します。お知らせ下さい。

試すこと:

  • 変更をロールバックして、まだ機能するかどうかを確認してください (誤って何も変更されていないことがわかり、データが同じであることを確認します)。
  • より単純なクエリでテストしていただけますか?
  • クエリ変数をダンプして、DB で手動で実行してみてください (変更の有無にかかわらず)?
  • データのいくつかの行 (返される行と返されない行) を投稿して、それを使用して一時テーブル テストに読み込むことができますか?
  • (ハードコードされた)pos作業中のクエリに追加してみて、それが機能するかどうかを確認してください。op
  • (ハードコードされた)op作業中のクエリに追加してみて、それが機能するかどうかを確認してください。pos
  • どこでも正しい順序で変数をリストしていることを確認してください

何らかの理由で、キャストなどのデータ型の問題である可能性があると考え続けていますが、Haskell を使用したことがないため、他に何が起こっているのかを実際に推測することはできません.

その他の提案:

  • クエリを適切にフォーマットして、読みやすいようにします (少なくとも少しは、1 つの巨大な文字列ではありません)。
  • 質問を更新して、環境の設定方法に関する仕様を含めます(ソフトウェア/モノのバージョンを使用)
  • 問題が怠惰に関係していると思われる場合は、評価を強制してみてください...? しかし、クエリにはすでに動的/可変部分がありました。もしそうなら、彼らは同じ問題を抱えていると仮定する必要があり、そもそもクエリは機能しなかったでしょう.
  • これはばかげていますが、たまたまプル元の DB を変更していませんか?

sqlite> select * from temp;
temp_id     temp_name
----------  ----------
1           one
2           two
3           three
import Database.HDBC.Sqlite3 
import Database.HDBC

testdb = "C:\\Users\\Kim!\\test.db"

data UmeQuery = UmeQuery String [SqlValue] deriving Show

tRunUmeQuery :: UmeQuery -> FilePath -> IO [[SqlValue]]

tRunUmeQuery (UmeQuery q args) dbFile = do
    conn <- connectSqlite3 dbFile
    stat <- prepare conn q
    s <- execute stat args
    res <- fetchAllRows' stat 
    disconnect conn
    return $ res
     
selectPos temp_id op = let 
   q = "select temp_id, temp_name from temp where temp_id = " ++ op ++ " ?";  
   a = [ toSql temp_id ] 
   in UmeQuery q a
> let a = selectPos (1::Int) "="
> let b = tRunUmeQuery a testdb 
> b
[[SqlByteString "1",SqlByteString "one"]]

> let a = selectPos (1::Int) ">"
> let b = tRunUmeQuery a testdb 
> b
[[SqlByteString "2",SqlByteString "two"],[SqlByteString "3",SqlByteString "three"]] 

簡単なメモ: 今日まで Haskell や SQLite に触れたことはありません。Windows 7 Professional 64ビットで、このSQLite3 - sqlite-dll-win64-x64-201409301904.zipでHaskell Platform 2014.2.0.0を実行しています。

編集:これも機能します...(クエリも少し異なります)

import Data.List

selectPos temp_id op temp_name = let 
   q = foldl' (++)  [] [
       "select temp_id, temp_name        " ++ 
       "from   temp                      " ++
       "where  temp_id " , op , " ? or   " ++
       "       temp_name = ?             "]
   a = [ toSql (temp_id::Int), toSql temp_name ]  
   in UmeQuery q a

> let a = selectPos 1 ">" "one"
> let b = tRunUmeQuery a testdb 
> b
[[SqlByteString "1",SqlByteString "one"],[SqlByteString "2",SqlByteString "two"],[SqlByteString "3",SqlByteString "three"]] 

編集:そしてこれはうまくいきます...

sqlite> insert into temp values (4, "Word"); 
sqlite> insert into temp values (5, "Utterance");

selectPos targetlt parentlt op pos = let 
   q = " select temp_id, temp_name        \
       \ from   temp                      \
       \ where  temp_name = ?  or         \
       \        temp_name = ?  or         \
       \        temp_name = ?  or         \
       \        temp_name = ?  or         \
       \        temp_id "++op++" ?        "
   a = [toSql targetlt, toSql parentlt, 
        toSql targetlt, toSql parentlt, 
        toSql (pos::Int) ]
   in UmeQuery q a

> let a = selectPos "Word" "Utterance" "=" 2
> let b = tRunUmeQuery a testdb 
> b
[[SqlByteString "2",SqlByteString "two"],[SqlByteString "4",SqlByteString "Word"],[SqlByteString "5",SqlByteString "Utterance"]]

そう...質問に投稿したクエリでは...予期しない違いもあります...変数とは関係ありません。一重引用符です。コピーと貼り付けのタイプミスなのか、それとも何なのかわかりません。大量のモックテーブルとデータを考え出すため、クエリをそのまま実行できないことは明らかです...

ここに画像の説明を入力

編集:はぁ…またこれに戻ってきました。selectPos最後の例の上に、私が使用していない余分な行があることに気付きました。それを機能させるには、このようにする必要がありました...[[SqlValue]]またはIO [[SqlValue]]最後の値が機能しなかったためです。エラー(私はただ試しているだけです。これらの値のいずれかが本当に意味があるかどうかはわかりません)。

selectPos :: String -> String -> String -> Integer -> UmeQuery
selectPos targetlt parentlt op pos = let 
   q = " select temp_id, temp_name        \
       \ from   temp                      \
       \ where  temp_name = ?  or         \
       \        temp_name = ?  or         \
       \        temp_name = ?  or         \
       \        temp_name != ?  or        \
       \        temp_id "++op++" ?        "
   a = [toSql targetlt, toSql parentlt, 
        toSql targetlt, toSql parentlt, 
        toSql pos ]
   in UmeQuery q a

> let a = selectPos "Word" "Utterance" "=" 2
> let b = tRunUmeQuery a testdb 
> b
[[SqlByteString "1",SqlByteString "one"],[SqlByteString "2",SqlByteString "two"],[SqlByteString "3",SqlByteString "three"],[SqlByteString "4",SqlByteString "Word"],[SqlByteString "5",SqlByteString "Utterance"]] 

いずれにせよ...今日、初めてのHaskellプログラムを書くことができてうれしく思います...!

于 2014-10-05T20:01:52.180 に答える