Сгенерированный 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 "second", SqlByString "2" String "2" String "2-й" "0.507488888888889",SqlByteString "0.655905050505051", SqlByteString "четвертый"], [SqlByteString "2", SqlByteString "2", SqlByteString "3", SqlByteString "0,1493877StString" 0,34938B2S77_37BTS37_37BTS37_37B) 2 ", SqlByteString" 2 ", SqlByteString" 6 ", SqlByteString" 0.507488888888889 ", SqlByteString" 0.655905050505051 ", SqlByteString" четвертый "], [SqlByteString" 3 ", Sqlt383838" S38B3838 "S38B3838" S38BY3838 "SID SqlByteString "0.312777777777778",SqlByteString "second"],[SqlByteString "3",SqlByteString "2",SqlByteString "6",SqlByteString "0,5048488888888889",SqlByb505050String "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
К сожалению, это не помогает решить проблему (и когда я: возвращаю возвращаемое значение функции в ghci, она все еще не оценена). Таким образом, лень может быть проблемой, так или иначе, но я не знаю, как сделать это полностью оцененным..? Пожалуйста, есть идеи?
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 Professional.
редактировать: это тоже работает... (запрос тоже немного другой)
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...!