ошибка в cmd.CreateParameter

Программирование на Visual Basic, главный форум. Обсуждение тем программирования на VB 1—6.
Даже если вы плохо разбираетесь в VB и программировании вообще — тут вам помогут. В разумных пределах, конечно.
Правила форума
Темы, в которых будет сначала написано «что нужно сделать», а затем просьба «помогите», будут закрыты.
Читайте требования к создаваемым темам.
Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

ошибка в cmd.CreateParameter

Сообщение Wasup! » 15.12.2006 (Пт) 13:39

Здравствуйте, в программе формирующей отчет Excel на основе данных из Oracle после смены раннего связывания на позднее стала появляться ошибка при выполнении запроса - "-2147467259 Тип параметра не поддерживается".
Код: Выделить всё

Function ExecSql(ByVal strSqlQuery As String, ByVal cnn As Object, ParamArray Param()) As Object
Dim cmd                 As Object 'New ADODB.Command
Dim Prm                 As Object 'New ADODB.Parameter
Dim i                   As Integer

On Error GoTo exception:
Set cmd = CreateObject("ADODB.Command")
Set Prm = CreateObject("ADODB.Parameter")

Set cmd.ActiveConnection = cnn
cmd.CommandText = strSqlQuery
cmd.CommandTimeout = 0

For i = LBound(Param) To UBound(Param)
    Set Prm = cmd.CreateParameter("Prm", VarType(Param(i)), adParamInput, , Param(i))
Next

'выполнение запроса
Set ExecSql = cmd.Execute  'Ошибка возникает здесь

Exit Function
exception:
    Set ExecSql = Nothing
    MsgBox strSqlQuery & vbCrLf & "ExecSql() " & Err.Number & " " & Err.Description _
    , vbCritical + vbSystemModal, PROGNAME & " Ошибка выполнения Sql запроса."
    Set Prm = Nothing
    Set cmd = Nothing
End Function

'В программе функция используется следующим образом

Public cnn                  As Object 'New ADODB.Connection
Public rs                   As Object 'ADODB.Recordset

Sub Main()               
...
Set cnn = CreateObject("adodb.connection")
Set rs = CreateObject("adodb.recordset")
...
Set rs = ExecSql("select * from my_table where id=? and value < ?", cnn, intSearchedId,intSearchedValue)
PrintRs rs 'вывод результата на рабочий лист
ExecSql "{CALL pkg_log.write_log( ? )}", cnn, "Выборка завершена"
...
CloseRs rs
cnn.Close
Set cnn = Nothing
Exit Sub

exception:
MsgBox "Ошибка при формировании отчета." _
& vbCrLf & Err.Number & " " & Err.Description _
, vbCritical + vbSystemModal, PROGNAME
Resume NextLine
NextLine:
    On Error Resume Next
    ExecSql ExecSql "{CALL pkg_log.write_log( ? )}", cnn, "Ошибка при формировании отчета."
    CloseRs rs
    cnn.Close
    Set cnn = Nothing
    Unload Form1
End Sub


Экспериментально выяснил, что дело в строчке
Код: Выделить всё
Set Prm = cmd.CreateParameter("Prm", VarType(Param(i)), adParamInput, , Param(i))
Если сделать явное приведение типа (Cint(Param(i)), то ошибки не возникает.
Или если создать временную переменную и передавать через нее, то же ошибки не будет.
Код: Выделить всё
dim varTmp
varTmp= Param(i)
Set Prm = cmd.CreateParameter("Prm", VarType(varTmp), adParamInput, , varTmp)
Предполагаю, что дело в передаче параметров через ParamArray.
После попыток решить эту проблему возник вопрос, а насколько вообще правильно делать вот такую функцию для выполнения запросов и насколько правильно я ее сделал?

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 15.12.2006 (Пт) 16:06

adParamInput
Заменяй константы их числовыми значениями.
Лучший способ понять что-то самому — объяснить это другому.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 15.12.2006 (Пт) 17:51

Все используемые константы описаны явно (public const adParamInput as long = 1) и option explicit тоже есть. Что-то еще?

Antonariy
Повелитель Internet Explorer
Повелитель Internet Explorer
Аватара пользователя
 
Сообщения: 4824
Зарегистрирован: 28.04.2005 (Чт) 14:33
Откуда: Мимо проходил

Сообщение Antonariy » 15.12.2006 (Пт) 18:10

VarType(Param(i)) - вас ист дас VarType?
Лучший способ понять что-то самому — объяснить это другому.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 15.12.2006 (Пт) 18:21

Даст исn функция VB.
Которая совпадает по возвращаемым значениям с энумом ADODB.DataTypeEnum.
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 18.12.2006 (Пн) 14:07

Помогла замена
Код: Выделить всё
Set Prm = cmd.CreateParameter("Prm", VarType(Param(i)), adParamInput, , tmpC)
на
Код: Выделить всё
Set Prm = cmd.CreateParameter("Prm")
Prm.Direction = adParamInput
Prm.Type = VarType(Param(i))
Prm.Value = Param(i)
ошибка исчезла.

Но просьба еще раз посмотреть саму процедуру и ее использование, нет ли каких-то подводных камней с тем, что я по несколько раз использую один и тот же рекордсет, в main.
И стоит ли так же вынести в main
Код: Выделить всё
Set cmd = CreateObject("ADODB.Command")
Set Prm = CreateObject("ADODB.Parameter")
чтобы не создавать их каждый раз при вызове процедуры.

alibek
Большой Человек
Большой Человек
 
Сообщения: 14205
Зарегистрирован: 19.04.2002 (Пт) 11:40
Откуда: Russia

Сообщение alibek » 18.12.2006 (Пн) 14:26

А cmd.Parameters.Refresh не удобнее будет?
Lasciate ogni speranza, voi ch'entrate.

GSerg
Шаман
Шаман
 
Сообщения: 14286
Зарегистрирован: 14.12.2002 (Сб) 5:25
Откуда: Магадан

Сообщение GSerg » 18.12.2006 (Пн) 14:33

С учётом того, что текст запроса, содержащий знаки вопроса, передаётся в эту же процедуру, наверное, не будет...
Как только вы переберёте все варианты решения и не найдёте нужного, тут же обнаружится решение, простое и очевидное для всех, кроме вас

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 21.12.2006 (Чт) 14:21

Чтобы не заводить новую тему продолжу здесь.
При использовании провайдеров MSDAORA и OraOLEDB у меня не получается выбрать из базы числа больше 29 знаков.

Код: Выделить всё
select to_number('123,45E30') as nmbr from dual


вызовет при вставке ошибку
"-2147217887 Произошли ошибки при выполнении многошаговых операций. Проверьте значения всех состояний."
в locals у этого значения тип variant/ decimal

А при использовании odbc провайдера (MSDASQL)
у этого же запроса тип значения уже variant/double и ошибок не возникает.

Вопрос, а можно ли как-то управлять типом столбца в рекордсете? Чтобы поставить double и избежать ошибки с переполнением decimal

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 26.12.2006 (Вт) 17:02

Совсем не понимаю. При выводе этого же самого рекордсета в MSHflexGrid никакой ошибки не возникает. А при выводе в debug.print ошибка.

Пример:
Код: Выделить всё
Set rs = ExecSql("select id, value from numtest where id=?", cnn, 7)
'выбирает одну строку с числом 123456789012345678901234567890,123456  - 38 знаков, больше чем позволяют double и decimal.
'Вот такой код вызывает ошибку
Do While Not rs.EOF
  Debug.Print rs.fields(0), rs.fields(1)
Loop
'в locals можно увидеть, что rs.fields item2 имеет тип variant/decimal, а в поле value находится строка с ошибкой "Произошли ошибки при выполнении многошаговых операций."

'А здесь присваивание проходит нормально! Причем в гриде результат виден, все 38 разрядов числа.     
Set Form2.MSHFlexGrid1.DataSource = rs
Form2.Show

Как такое может происходить ведь используется один и тот же рекордсет, который не может отобразить число такой разрядности?

Konst_One
Член-корреспондент академии VBStreets
Член-корреспондент академии VBStreets
Аватара пользователя
 
Сообщения: 3041
Зарегистрирован: 09.04.2004 (Пт) 13:47
Откуда: Химки

Сообщение Konst_One » 26.12.2006 (Вт) 17:19

дебаг неявно преобразование в строку делает, поэтому и ошибка

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 27.12.2006 (Ср) 9:40

А как можно получить ошибку преобразуя число в строку? Наоборот, строку в число, понятно.

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 19.01.2007 (Пт) 14:12

Решил вернуться к вопросу про получение большого числа из рекордсета.
Сделал тест, который получает из Oracle число 1234E35 и пытается достать его из рекордсета. Тип этого числа в рекордсете определяется, как adVarNumeric, что соответствует Variant/Decimal. Этот тип по определению не может сохранить число 1234E35, оно слишком большое. Но!
При вставке в грид и использовании метода rs.save число отображается нормально!
Как такое возможно?! Ведь, если в рекордсете такое число не может храниться, откуда его получают MSHFlexGrid и метод Save?
Сможете объяснить такое поведение рекордсета?

Код для VBA
Код: Выделить всё
Option Explicit

Public cnn                  As Object
Public rs                   As Object

Const adUseClient           As Long = 3
Const adOpenStatic          As Long = 3
Const adLockReadOnly        As Long = 1
Const adPersistXML          As Long = 1

Sub Main()
Dim strCnnStr               As String
Dim dblTmp                  As Double
Dim avarTmp()               As Variant
Dim strTmp                  As String

Set cnn = CreateObject("adodb.connection")
Set rs = CreateObject("adodb.recordset")
'strCnnStr = "Provider=OraOLEDB.Oracle;Data Source=;User ID=;Password="
strCnnStr = "Provider=MSDAORA;Data Source=;User ID=;Password="

cnn.CursorLocation = adUseClient
cnn.Open strCnnStr

rs.Open "select 1234E35+1 as num,to_char(1234E35+1,'TM') as chr from dual", cnn, adOpenStatic, adLockReadOnly

'Поле num будет пропущено
ThisWorkbook.Worksheets(1).Cells(1, 1).CopyFromRecordset rs

'А в гриде оно видно!
Set Form1.MSHFlexGrid1.DataSource = rs
Form1.Show

'В массиве поле num имеет значение Empty
rs.MoveFirst
avarTmp = rs.GetRows
'тоже пропускает поле num
rs.MoveFirst
strTmp = rs.GetString
'А в файл это поле сохраняется правильно!
On Error Resume Next
rs.Save "c:\numtestrs.xml", adPersistXML
'напрямую присваивание тоже не проходит
rs.MoveFirst
dblTmp = rs.Fields(0).Value
Debug.Print dblTmp, "И почему в одном случае из рекордсета можно получить значение, а в другом нет?"

rs.Close
cnn.Close
Set rs = Nothing
Set cnn = Nothing

End Sub

Wasup!
Продвинутый пользователь
Продвинутый пользователь
 
Сообщения: 120
Зарегистрирован: 21.06.2005 (Вт) 11:09

Сообщение Wasup! » 23.01.2007 (Вт) 10:53

Интересно стало, это у меня вопросы дурацкие, или никто не знает.
Или все давно перешли с АДО на что-то еще?


Вернуться в Visual Basic 1–6

Кто сейчас на конференции

Сейчас этот форум просматривают: AhrefsBot, Google-бот, YaCy [Bot] и гости: 104

    TopList