Работа с удаленными данными или SQL-серверами

HomeFAQЧасто задаваемые вопросыРабота с удаленными данными или SQL-серверами
Как определить причину ошибки

Вопрос

Выполнение операции с SQL-сервером завершилось неудачей. Как определить факт того, что произошла ошибка, и какая именно ошибка произошла?

Ответ

Тот факт, что операция на сервере закончилась неудачей, определяется в зависимости от используемого способа обращения к серверу.

Если обращение идет через функции SQLExec(), то сама эта функция возвращает значение меньше нуля в случае ошибки.

Если обращение идет через RemoteView или Cursor Adapter, то операция сброса изменений на сервер осуществляется функцией TableUpdate(). В случае неудачи (ошибки) эта функция вернет .F.

Для определения причины возникновения ошибки предназначена функция AERROR(). Использовать можно следующим образом:

  
 * При работе через SQLExec()  
  IF SQLExec(...)<0  
  	LOCAL laError(1)  
  	=AERROR(laError)  
 	* Анализ массива laError для уточнения причины ошибки  
  ENDIF  
    
 * При работе через Remote View или Cursor Adapter  
  IF TableUpdate(.T.,.T.,"MyView")=.F.  
  	LOCAL laError(1)  
  	=AERROR(laError)  
 	* Анализ массива laError для уточнения причины ошибки  
  ENDIF

При работе с удаленными данными первый элемент полученного массива всегда будет иметь значение 1526 - ошибка ODBC-соединения.

Остальные элементы массива будут содержать информацию об ошибке самого сервера. Т.е., какая именно ошибка произошла на сервере.

Как подавить выдачу системного окна настройки соединения в случае ошибки

Вопрос

Я настриваю соединение программно через команду SQLConnect() или команду SQLStringConnect(). В случае ошибки создания соединения возникает системное окно для настройки реквизитов соединения. Как можно подавить выдачу этого окна, просто выдав пользователю сообщение об ошибке?

Ответ

Если Вы используете для создания соединения объект Connection базы данных, то откройте этот объект на редактирование и сделайте следующие настройки:

  1. В разделе "Display ODBC login prompts" установите переключатель в положение "Never"
  2. Убедитесь, что в разделе "Data Procesing" переключатель "Display Warnings" выключен

Если Вы настраиваете соединение исключительно программными средствами, то установить нужные значения этих настроек можно, используя функцию SQLSetProp(). Это настройки DispLogin и DispWarnings.

Однако, как видно из синтаксиса функции SQLSetProp(), она делает настройки для конкретного соединения. Первым параметром идет номер уже созданного соединения. Но ведь соединения еще нет. Его только собираются создать.

Чтобы преодолеть это противоречие, используют соединение с номером 0. Физически - это вообще не соединение. По сути, это будут настройки для любого нового соединения, поскольку номером 0 обозначают первое не занятое значение номера соединения. На уже созданные соединения эти настройки не распространяются.

Получается примерно такой код:

  
 * Сохранение текущих значений настроек  
  LOCAL llDispWarning, lnDispLogin  
  llDispWarning=SQLGetProp(0,'DispWarning')  
  lnDispLogin=SQLGetProp(0,'DispLogin')  
    
 * Установка этих настроек в значения, препятствующие выдаче системных сообщений  
  SQLSetProp(0,'DispWarning',.F.)  
  SQLSetProp(0,'DispLogin',3)  
    
 * Формируем строку соединения для MS SQL сервера  
  LOCAL lcStringConnect  
  lcStringConnect='DRIVER=SQL Server;'+;  
  		'SERVER=Имя_Сервера;'+;  
  		'UID=Логин_Пользователя;'+;  
  		'PWD=Пароль_Пользователя;'+;  
  		'DataBase=Имя_базы_данных'  
    
 * Собственно попытка создания соединения  
  lnNewConnection = SQLStringConnect(m.lcStringConnect)  
  IF m.lnNewConnection <= 0  
 	* Произошла ошибка в момент установки соединения. Выясняем причину  
  	LOCAL laError(1)  
  	=AERROR(laError)  
 	* Анализ массива laError для уточнения причины ошибки  
  ELSE  
 	* Соединение успешно установлено   
 	* и его номер записан в переменную lnNewConnection  
  ENDIF  
    
 * Восстановление исходных настроек  
  SQLSetProp(0,'DispWarning',m.llDispWarning)  
  SQLSetProp(0,'DispLogin',m.lnDispLogin)

В принципе, значение настройки DispWarning и так по умолчанию равно .F. Здесь это делается на всякий случай

Как вызвать хранимую процедуру сервера

Вопрос

Как вызвать хранимую процедуру сервера?

Ответ

В FoxPro для этой цели можно использовать функцию SQLExec(). Стандартный синтаксис для вызова хранимой процедуры любого сервера является команда Call, обрамленная фигурными скобками. Примерно так:

SQLExec(m.lnHandle,"{Call MyProc (par1, par2)}")

Для MS SQL сервера можно использовать "локальный", т.е. пригодный только для сервера MS SQL способ вызова:

SQLExec(m.lnHandle,"EXECUTE MyProc par1, par2")

В данных примерах использовано следующее обозначение:

m.lnHandle - это число, являющееся номером ранее установленного соединения с сервером.
MyProc - имя запускаемой процедуры
par1, par2 - параметры, передаваемые в процедуру

Как передать параметры в хранимую процедуру сервера

Вопрос

В пользовательской форме были сформированы некоторые значения. Как передать эти сформированные значения в качестве параметров в хранимую процедуру сервера?

Ответ

Для передачи значения как параметра в синтаксисе FoxPro используется вопросительный знак.

  
  LOCAL lnPar1, lcPar2  
  lnPar1 = 123.456  
  lcPar2 = "Новое значение"  
  SQLExec(lnHandle,"{Call MyProc (?m.lnPar1, ?m.lnPar2)}")

Все то, что следует за символом вопросительного знака до первого пробела или запятой - это параметр. Конвертация в тип, понятный SQL-серверу произойдет автоматически. Никаких дополнительных преобразований делать не надо.

В качестве параметра можно использовать не только переменные памяти, но и поля таблицы и объекты формы.

Обратите внимание на тот факт, что конвертация в тип, понятный SQL-серверу произойдет автоматически. Однако следует иметь в виду, что при определенных условиях подобной автоматизм может вызвать некоторые проблемы.

Например, для MS SQL сервера переменные памяти с дробной частью будут сконвертировны в тип REAL, что может привести к потере точности. Вместо точного значения 1.1 сервер интерпретирует это значение, как приближенное 1.0999999. В большинстве случаев - ничего страшного. Однако иногда это может иметь принципиальное значение.

Поэтому, если Вам необходимо передать данные "как есть", то придется самостоятельно формировать символьную строку.

  
  LOCAL lnPar1, lcPar2, lcStringSQL  
  lnPar1 = 123.456  
  lcPar2 = "Новое значение"  
  lcStringSQL = "{Call MyProc ("+TRANSFORM(m.lnPar1)+",'"+m.lcPar2+"')}"  
  SQLExec(lnHandle,m.lcStringSQL)

Замечание

Если Вы формируете символьную строку самостоятельно, то предварительно уточните как именно записываются константы разных типов в синтаксисе того или иного сервера.

Например, для MS SQL сервера символьная константа, по умолчанию, обрамляется одинарными кавычками. Двойными кавычками обрамляются имена объектов. Поэтому символьная константа обрамленная двойными кавычками может вызвать сообщение о синтаксической ошибке.

Константа типа Date или DateTime также может записываться разными способами. Хотя, универсальный формат, который понимают большинство серверов - это символьная строка вида 'ГГГГММДД'. Т.е. дата 6 января 2010 года должна быть записана как '20100106'

Как получить значение выходного параметра процедуры сервера

Вопрос

На SQL-сервере создана процедура, имеющая выходной параметр. Т.е. параметр, имеющий признак OUTPUT. Как прочитать возвращаемое значение этого параметра.

Ответ

Синтаксис будет почти такой же, как и при передаче параметра в хранимую процедуру. Подробно это описано в статье "Как передать параметры в хранимую процедуру сервера"

Отличие заключается только в том, что используется не один символ вопросительного знака, а два символа: вопросительный знак и @.

  
  LOCAL lnPar1, lcPar2    
  lnPar1 = 123.456    
  lcPar2 = ""  && В эту переменную будет записано значение параметра OUTPUT  
  SQLExec(lnHandle,"{Call MyProc (?m.lnPar1, ?@m.lnPar2)}")

Разумеется, для конкретного SQL-сервера, кроме общего вызова через CALL, можно использовать вызов через соответствующие синтаксические конструкции конкретного сервера. Например, для MS SQL сервера можно использовать вызов через EXECUTE

  
  LOCAL lnPar1, lcPar2    
  lnPar1 = 123.456    
  lcPar2 = ""  && В эту переменную будет записано значение параметра OUTPUT  
  SQLExec(lnHandle,"Execute MyProc ?m.lnPar1, ?@m.lnPar2")

Замечание

Не используйте в качестве параметра переменные памяти с именем, состоящем из одной буквы. В этом случае такая конструкция может не сработать, поскольку в синтаксисе FoxPro за многими буквами закреплены имена рабочих областей. Например, буква "a" - это первая рабочая область.

Как получить значение функции сервера

Вопрос

Как получить значение, возвращаемое моей функцией, написанной на SQL-сервере?

Ответ

Для этого используют специальную синтаксическую конструкцию

  
  LOCAL lnReturnValue  
  lnReturnValue = 0  
  SQLEXEC(m.lnHandle,"{?@lnReturnValue= Call MyFunction()}")   
  ?lnReturnValue

По аналогии с чтением значения параметров OUTPUT

Замечание

Данный прием не сработает для внутренних функций MS SQL. Т.е. таким образом невозможно будет получить значение, например, функции SCOPE_IDENTITY().

Для внутренних функций MS SQL следует использовать штатный механизм возвращения результатов запроса:

  
  SQLEXEC(m.lnHandle,"SELECT SCOPE_IDENTITY() as LastIdent","tabLastIdent")   
  ?tabLastIdent.LastIdent

 

Почему возвращается только часть числа. Первые символы

Вопрос

При чтении числовых данных с SQL-сервера возвращается только часть числа. Первые символы или только целая часть без дробной части.

Причина

Причина в настройках конкретного соединения с SQL-сервера. Явно или не явно установлены региональные настройки. Обратите внимание, речь идет не о настройках собственно сервера, а о настройках соединения с сервером!

Проблема здесь в том, что числовые данные как правило, конвертируются в поля типа Numeric в среде FoxPro. А поля типа Numeric в FoxPro физически хранятся как символьные данные. Это значит, что в принципе, в такое поле можно записать любой символ. Вот ODBC и записал, например, символ запятой в соответствии с региональной настройкой.

Но при чтении данных для FoxPro символ запятой - это не есть символ, относящийся к числовым данным (символом-разделителем целой и дробной части непосредственно в таблице является точка). Следовательно, будут прочитаны как число только те символы, которые стоят ДО символа запятой.

Решение

Решение зависит от того, каким способом были установлены соответствующие настройки соединения.

MS SQL - сервер

Если Вы использовали для установки соединения предварительно настроенный DSN, то зайдите в настройки DSN-соединения и снимите "птичку" с настройки

"Use regional settings when outputting currency, numbers, dates, and times"

Если Вы использовали для установки соединения строку SQLStringConnect(), то убедитесь, что в ней нет опции Regional или же она имеет значение Regional=No

Oracle - сервер

Здесь придется явным образом менять настройку текущей сессии через прямую команду SQLExec() примерно так

SQLExec(m.nOraConnect,[ALTER SESSION SET NLS_NUMERIC_CHARACTERS='. '])
//////////////// ///////////////
Авторизация
*
*
Генерация пароля