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 "秒"],[SqlByteString "1",SqlByteString "2",SqlByteString "6",SqlByteString "0.507488888888889",SqlByteString "0.655905050505051",SqlByteString "第四"],[SqlByteString "2",SqlByteString "2",SqlByteString "3",SqlByteString "0.149383838383838",SqlByteString "0.312777777777778",SqlByteString "第二" 2",SqlByteString "2",SqlByteString "6",SqlByteString "0.507488888888889",SqlByteString "0.655905050505051",SqlByteString "第四"],[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 "第四"]]

现在,当我需要在查询中插入几个小的动态部分时,就像这样(抱歉,您必须滚动到字符串的末尾才能看到这个):

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 

[]

为什么第二个查询不返回任何内容(或者实际上是相同的内容)?

有任何想法吗?

编辑:

我对此进行了进一步的调查,认为这可能与懒惰有关。好的,现在已经改造成这样的:

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

不幸的是,这对问题没有帮助(当我 :sprint ghci 中函数的返回值时,它仍然没有被评估)。因此,不知何故,懒惰可能是问题所在,但我不知道如何对此进行全面评估..?请问,有什么想法吗?

4

1 回答 1

3

所以......只是陈述事实:

  • 您的代码确实运行它不会产生任何语法错误或警告(这适用于haskell和由haskell运行的sql)
  • 原始查询确实运行,但没有添加 op 和 pos(已经有动态部分)
  • 你得到一个空集(意思是,查询不返回任何行)......

如果所有这些事情都是真的,它会让我相信这个查询一定是有效的但错误的。检查数据?转储查询,手动运行。让我知道。

要尝试的事情:

  • 尝试回滚更改以查看它是否仍然有效(这样您就知道没有意外更改任何内容并验证数据是否相同)。
  • 您可以尝试使用更简单的查询进行测试吗?
  • 您可以尝试转储查询变量并在数据库中手动运行它(有或没有更改)吗?
  • 您是否要发布几行数据(有些行将返回,有些则不会),以便我可以将其加载到临时表测试中?
  • 尝试仅添加pos到工作查询(使用op硬编码)并查看是否有效
  • 尝试仅添加op到工作查询(使用pos硬编码)并查看是否有效
  • 确保您在任何地方都以正确的顺序列出您的变量

出于某种原因,我一直认为这可能是强制转换或其他什么的数据类型问题,但我从未使用过 Haskell,所以我真的无法猜测还会发生什么。

其他建议:

  • 正确格式化您的查询,使其易于阅读(至少一点,所以它不是一个巨大的字符串)
  • 更新您的问题以包括有关如何设置环境的规范(包含软件/事物和东西的版本)
  • 如果您认为问题与懒惰有关,请尝试强制评估...?但是查询确实已经有动态/可变部分。如果是这种情况,我将不得不假设他们会遇到同样的问题,并且查询一开始就不会起作用。
  • 这很愚蠢,但是您并没有碰巧更改要从中提取的数据库,对吗?

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 回答