Сгенерированный SQL-запрос не возвращает то же самое, что и соответствующий статический запрос в sqlite3 HDBC

Я генерирую SQL-запросы в Haskell и отправляю их в базу данных SQLite(3) с помощью HDBC. Теперь эта функция возвращает запрос:

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", "0,507488888888889", SqlByteString "0.655905050505051", SqlByteString "четвертое"], [SqlByteString "2", SqlByteString "2", SqlByteString "3", SqlByteString "0.149383838383838", SqlByteString "0.312777777777778", SqlByteString "второй"], [SqlByteString " 2", SqlByteString "2", SqlByteString "6", SqlByteString "0,507488888888889", SqlByteString "0,655905050505051", SqlByteString "четвертый"], [SqlByteString "3", SqlByteString "2", SqlByteString "3", Sql838String "808.38String" SqlByteString "0.31277777777778", SqlByteString "второй"], [SqlByteString "3", SqlByteString "2", SqlByteString "6", SqlByteString "0.507488888888889", SqlByteString "0.655905050505051", SqlByteString] "fourth"

Теперь, когда мне нужно вставить в запрос пару небольших динамических частей, например так (извините, вам нужно прокрутить строку до конца, чтобы увидеть это):

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, оно все еще не оценивается). Итак, лень может быть проблемой, но я не знаю, как это полностью оценить..? Пожалуйста, есть идеи?


person Fredrik Karlsson    schedule 30.09.2014    source источник
comment
Возможно, вы захотите использовать новые строки, чтобы команда SQL стала читаемой. Также покажите сгенерированный SQL.   -  person CL.    schedule 03.10.2014
comment
Готово! Пожалуйста, помогите мне понять, почему эти два запроса не выполняются одинаково.   -  person Fredrik Karlsson    schedule 03.10.2014
comment
Взгляните на мой ответ и дайте мне знать, если я настрою что-то, что значительно отличается от того, на чем вы работаете. Я честно не могу думать ни о чем другом. Я не могу воспроизвести пустой набор, объединив эти строки (по сути, это все, что вы меняете).   -  person gloomy.penguin    schedule 06.10.2014


Ответы (1)


Итак... просто констатирую факты:

  • ваш код работает, он не выдает каких-либо синтаксических ошибок или предупреждений (и это относится как к haskell, так и к sql, запускаемому haskell)
  • исходный запрос выполняется, но не с добавленными 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. Я использую платформу Haskell 2014.2.0.0 с этим SQLite3 - sqlite-dll-win64-x64-201409301904.zip в 64-разрядной версии Windows 7 Профессиональная.

изменить: это тоже работает... (запрос тоже немного отличается)

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...!

person gloomy.penguin    schedule 05.10.2014
comment
Спасибо за ваши усилия. Я не уверен, что вставило дополнительный 'char во второй оператор SQL, но я не думаю, что это он. Я получаю ответ SqlInt64 2 от toSql. Может в этом проблема..? - person Fredrik Karlsson; 07.10.2014