Запрос Oracle работает в SQL , но не возвращает строк в VB.NET

#vb.net #oracle #datetime

#vb.net #Oracle #дата и время

Вопрос:

Помогите, есть идеи??

Я использую адаптер OLEDB (Oracle OLEDB) с Oracle 10.2.0.3.0. Мой код генерирует SQL для запроса, затем, когда я выполняю его в OleDbDataReader, значение HasRows равно False . Однако, если я выведу содержимое строки запроса, скопирую и вставлю в SQL (войдя в систему как тот же пользователь с того же клиентского компьютера), он вернет 993 строки. Что дает??

Вот фрагмент моего кода:

 Dim DB As New OleDb.OleDbConnection(String.Format("{0};Password={1}", ConnectionString, DBPassword))
Dim flowQuerySQL As String
'... code to generate query
Debug.Print(flowQuerySQL)
Dim flowQueryCMD As New OleDb.OleDbCommand(flowQuerySQL, DB)
Dim flowQuery As OleDb.OleDbDataReader = flowQueryCMD.ExecuteReader()
While flowQuery.HasRows
    '...handle rows
End While
 

Инструкция debug.print показывает:

 SELECT CLASS_ID, OBJECT_ID FROM TDM_SF_PROCESS WHERE CLASS_ID=853 AND TDM_END_TIME >= '01-Jan-2009' AND TDM_END_TIME < '31-May-2009' AND TDM_STATUS <> 1 AND TDM_STATUS <> 2
 

Комментарии:

1. Есть только один результат? Который вы потребляете, и теперь HasRows является false?

2. Обратите внимание, что следующим оператором после выполнения команды является While flowQuery.HasRows . Тем не менее, я просто попытался удалить даты из запроса, и теперь HasRows есть True . Почему это так? Есть ли что-то в OleDb, что требует даты в формате, отличном от того, что хочет Oracle? Я пробовал даты и время, но это дало мне ORA-01861.

Ответ №1:

Я только недавно начал работать VB.NET против Оракула, а даты темпераментны.

Этот подход работал для меня до сих пор:

 SELECT CLASS_ID, OBJECT_ID 
FROM TDM_SF_PROCESS 
WHERE CLASS_ID=853 
  AND TDM_END_TIME >= TO_DATE('01-Jan-2009', 'DD-Mon-YYYY') 
  AND TDM_END_TIME < TO_DATE('31-May-2009', 'DD-Mon-YYYY') 
  AND TDM_STATUS <> 1 
  AND TDM_STATUS <> 2 
 

Это не сработало бы, пока я не использовал функцию «TO_DATE». Надеюсь, это сработает и для вас.

Я специалист по MS-SQL, поэтому мне пока не удалось выяснить, «почему» почему это работает. Просветление, кто-нибудь?

Комментарии:

1. 1 да, после просмотра некоторых моих старых кодов я тоже использую TO_DATE. По какой-то причине строки не анализируются правильно, если они не в системном формате Oracle. На самом деле я использую это: to_date(‘24.10.2011′,’мм / дд / гггг’) и даже использую его в качестве ключа автозапуска.

2. Черт, я думал, что это пройдет! Вот SQL, который он выполняет: SELECT CLASS_ID, OBJECT_ID, TDM_END_TIME FROM TDM_SF_PROCESS WHERE CLASS_ID=862 AND TDM_END_TIME >= TO_DATE('1-Jan-2009', 'DD-Mon-YYYY') AND TDM_END_TIME < TO_DATE('31-May-2009', 'DD-Mon-YYYY') AND TDM_STATUS <> 1 AND TDM_STATUS <> 2 Все еще имеет значения = False

3. Не обращайте на это внимания! Идентификатор класса 862 не имеет строк. 853, похоже, это сработало. Спасибо!!!!

4. Рад, что это сработало. Я также обновил свой пример для 4-значных лет. Я (позже) понял, что я поднял свой пример из реализации, которая специально требовала 2-значных лет (тьфу!).

5. Насколько я помню, это соглашение OLEDB / ODBC, и фактический драйвер OLEDB не будет преобразован в date, если он не находится внутри #31-May-2009 #. Таким образом, драйвер сообщает базе данных, что это символы, не имеющие даты.

Ответ №2:

1) Вы создаете запрос, который не использует переменные привязки? Или вы вручную заполняете переменные привязки в своем отладочном выводе для нас? Если вы не используете переменные привязки, вы реально создадите серьезную проблему с производительностью в своей базе данных, потому что вы уничтожите общий пул Oracle. Вы собираетесь создать небезопасный код, который уязвим для атак с использованием SQL-инъекций. И в конечном итоге вы потратите много времени на устранение ошибок, связанных с типами данных, экранирующими строками и т. Д..

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

Я не являюсь разработчиком VB. Но если вы используете переменные привязки, вы должны заменить литералы в вашем SQL-операторе заполнителями, т. е.

 AND TDM_END_TIME >= :early_time 
AND TDM_END_TIME <  :late_time 
AND TDM_STATUS <> :status_1 
AND TDM_STATUS <> :status_2
 

Затем вы должны предоставить значения для этих переменных привязки во время выполнения, т.е.

 flowQueryCMD.Parameters.Add( ":early_time", <<your VB date object>> )
flowQueryCMD.Parameters.Add( ":late_time", <<your VB date object>> )
 

Наконец, вы выполняете запрос

2) Если вы не используете переменные привязки, то вы должны убедиться, что ваши типы данных совпадают. ’01-Jan-2009′ — это строка, а не дата, поэтому Oracle должен неявно преобразовать строку в дату. Он делает это с помощью сеанса NLS_DATE_FORMAT . Если у вас NLS_DATE_FORMAT не будет значения ‘ДД-ПН-ГГГГ’, преобразование завершится неудачно (или молча сделает что-то неожиданное), и вы не получите ожидаемых результатов. Поскольку значение NLS_DATE_FORMAT может быть разным для каждого сеанса, вы никогда не должны полагаться на какое-либо конкретное NLS_DATE_FORMAT значение, установленное в каком-либо конкретном сеансе, иначе ваш код может работать для вас и не работать для вашего коллеги, который предпочитает европейские форматы даты. Ваш код может нормально работать в одном сеансе (скажем, SQL * Plus, который получает его NLS_DATE_FORMAT из одного места), а не в другом сеансе (скажем, приложение .Net, которое получает настройки языка, набора символов и формата даты из .Чистый стек)

Есть несколько способов указать литералы даты в Oracle. Первый — использовать синтаксис литерала даты ANSI (или литерала метки времени) (обратите внимание, что литералы даты ANSI всегда указываются как ГГГГ-ММ-ДД)

 AND tdm_end_time >= date '2009-01-01'
AND tdm_end_time <  date '2009-05-31'
 

Второй вариант — выполнить явное преобразование самостоятельно, используя TO_DATE функцию

 AND tdm_end_time >= to_date( '01-Jan-2009', 'DD-MON-YYYY' )
AND tdm_end_time <  to_date( '31-May-2009', 'DD-MON-YYYY' )
 

Комментарии:

1. 1 за подробности. Будучи новичком в Oracle и его терминологии: не могли бы вы опубликовать пример с переменными Bind, пожалуйста?