Почему моя хранимая процедура получает нулевой параметр? - PullRequest
2 голосов
/ 26 сентября 2008

Хорошо, это кудрявый. Я работаю над кодом Delphi, который не написал, и столкнулся с очень странной проблемой. Один из параметров моих хранимых процедур отображается как null, хотя он определенно отправляется 1.

Код Delphi использует TADOQuery для выполнения хранимой процедуры (анонимно):

 ADOQuery1.SQL.Text := "exec MyStoredProcedure :Foo,:Bar,:Baz,:Qux,:Smang,:Jimmy";
 ADOQuery1.Parameters.ParamByName("Foo").Value := Integer(someFunction()); 
 // other parameters all set similarly
 ADOQuery1.ExecSQL;

Integer(SomeFunction()) в настоящее время всегда возвращает 1 - я проверил с помощью отладчика.

Тем не менее, в моем хранимом процессе (изменено в целях отладки):

create procedure MyStoredProcedure (
    @Foo int, @Bar int, @Baz int,
    @Qux int, @Smang int, @Jimmy varchar(20) 
) as begin
    -- temp debug
    if ( @Foo is null ) begin
        insert into TempLog values ( "oh crap" )
    end
    -- do the rest of the stuff here..
end

TempLog действительно заканчивается «о, дерьмо» (дополнительный вопрос: должен быть лучший способ отладки хранимых процедур: что это?).

Вот пример трассировки от профилировщика:

exec [MYDB]..sp_procedure_params_rowset N'MyStoredProcedure',1,NULL,NULL

declare @p3 int
set @p3=NULL
exec sp_executesql 
    N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
    N'@P1 int OUTPUT,@P2 int,@P3 int,@P4 int,@P5 int,@P6 int',
    @p3 output,1,1,1,0,200
select @p3

Это выглядит немного странно для меня. Обратите внимание, что он использует @ p3 и @ P3 - может ли это быть причиной моей проблемы?

Другая странная вещь заключается в том, что, похоже, это зависит от того, какой TADOConnection я использую.

Проект представляет собой dll, которому передается TADOConnection из другого приложения. Вызывает все хранимые процедуры, использующие это соединение.

Если вместо использования этого соединения я сначала сделаю это:

ConnectionNew := TADOQuery.Create(ConnectionOld.Owner);
ConnectionNew.ConnectionString := ConnectionOld.ConnectionString;
TADOQuery1.Connection := ConnectionNew;

Тогда проблема не возникает! След от этой ситуации таков:

exec [MYDB]..sp_procedure_params_rowset N'MyStoredProcedure',1,NULL,NULL

declare @p1 int
set @p1=64
exec sp_prepare @p1 output,
    N'@P1 int,@P2 int,@P3 int,@P4 int,@P5 int,@P6 varchar(20)',
    N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
    1
select @p1

SET FMTONLY ON exec sp_execute 64,0,0,0,0,0,' ' SET FMTONLY OFF

exec sp_unprepare 64

SET NO_BROWSETABLE OFF

exec sp_executesql 
    N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
    N'@P1 int,@P2 int,@P3 int,@P4 int,@P5 int,@P6 varchar(20)',
    1,1,1,3,0,'400.00'

К сожалению, это немного для меня. Какие параметры TADOConnection могут влиять на это?

У кого-нибудь есть идеи?

Редактировать: Обновление ниже (больше не хотел задавать этот вопрос: P)

Ответы [ 8 ]

1 голос
/ 26 сентября 2008

В моих программах у меня есть много кода, очень похожего на ваш первый фрагмент, и я не сталкивался с этой проблемой.

Это на самом деле ваш код, или это то, как вы представляли проблему, которую мы должны понять? Текст для SQL хранится в вашем DFM или заполняется динамически?

Мне было интересно, если, возможно, каким-то образом свойство Params запроса уже получило список параметров, определенных / кэшированных в IDE, и это могло бы объяснить, почему P1 рассматривается как вывод (который почти наверняка вызывает проблему NULL). ).

Непосредственно перед тем, как установить ParamByName.Value, попробуйте

ParamByName("Foo").ParamType=ptInput;

Я не уверен, почему изменение строки подключения также исправит это, если только это не сбрасывает внутренний смысл параметров для этого запроса.

В TSQLQuery свойство Params запроса сбрасывается / воссоздается всякий раз, когда изменяется значение SQL.Text (я не уверен, верно ли это для TADOQuery), так что ваш первый фрагмент должен был вызвать любая существующая информация Params, которая должна быть удалена.

Если приведенное выше предложение «ParamByname.ParamType» исправляет это для вас, то, безусловно, что-то происходит с запросом в другом месте (во время создания? В форме?), Что заставляет его думать, что Foo является выходным параметром. ..

это помогает? : -)

0 голосов
/ 03 декабря 2018

У меня была очень похожая проблема с использованием TADOQuery для получения некоторой информации LDAP, в функции TParameter.InternalRefresh есть ошибка, которая вызывает нарушение доступа, даже если у вашего запроса нет параметров.

Чтобы решить эту проблему, просто установите TADOQuery.ParamCheck в false.

0 голосов
/ 07 октября 2008

Хорошо, прогресс достигнут .. вроде.

@ Робсофт был прав, установка направления параметра на pdInput устранила проблему.

Я проследил в код VCL, и он снизился до TParameters.InternalRefresh.RefreshFromOleDB. Эта функция вызывается, когда я устанавливаю SQL.Text. Вот (сокращенный) код:

function TParameters.InternalRefresh: Boolean;
  procedure RefreshFromOleDB;
    // ..
        if OLEDBParameters.GetParameterInfo(ParamCount, PDBPARAMINFO(ParamInfo), @NamesBuffer) = S_OK then
          for I := 0 to ParamCount - 1 do
            with ParamInfo[I] do
            begin
              // ..
              Direction := dwFlags and $F;       // here's where the wrong value comes from
              // ..
            end;
     // ..
  end;
  // ..
end;

Итак, OLEDBParameters.GetParameterInfo по какой-то причине возвращает неправильные флаги.

Я подтвердил, что при исходном соединении (dwFlags and $F) равно 2 (DBPARAMFLAGS_ISOUTPUT), а при новом соединении - 1 (DBPARAMFLAGS_ISINPUT).

Я не совсем уверен, что хочу копать глубже, пока, по крайней мере.

Пока у меня не будет больше времени и уклона, я просто проверю все параметры на pdInput, прежде чем открыть запрос.
Если у кого-то нет больше ярких идей сейчас ..?

В любом случае, спасибо всем за ваши предложения.

0 голосов
/ 30 сентября 2008

Единственный раз, когда у меня возникала такая проблема, это когда поставщик БД не мог различить параметры Output (всегда устанавливает его в null) и InputOutput (использует то, что вы предоставили).

0 голосов
/ 30 сентября 2008

@ Constantin

Это должна быть опечатка от автора вопроса.

@ Blorgbeard

Хммм ... Когда вы меняете SQL TADOQuery, хорошо использовать очистите параметры и создайте заново, используя CreateParameter. Я бы не стал полагаться на ParamCheck во время выполнения - так как он уходит свойства параметров в основном не определены. У меня была такая проблема, когда я полагался на ParamCheck автозаполнение параметров - редко, но встречается. Ах, если вы идете по маршруту CreateParameter, создайте как первый параметр @RETURN_VALUE, так как он будет перехватывать возвращенные значение MSSQL SP.

0 голосов
/ 30 сентября 2008
 ADOQuery1.Parameters.ParamByName("Foo").Value = Integer(someFunction()); 

Разве они не используют := для назначения в Object Pascal?

0 голосов
/ 30 сентября 2008

Я подозреваю, что у вас осталось некоторое несоответствие параметров от предыдущего использования ADOQuery.

Вы пытались сбросить параметры после изменения SQL.Text:

  ADOQuery1.Parameters.Refresh;

Также вы можете попытаться очистить параметры и явно воссоздать их:

  ADOQuery1.Parameters.Clear;
  ADOQuery1.Parameters.CreateParameter('Foo', ftInteger, pdInput, 0, 1);
  [...]

Я думаю, что изменение соединения на самом деле вызывает InternalRefresh параметров.

0 голосов
/ 26 сентября 2008

предостережение: я не знаю delphi, но этот вопрос звучит слабым звонком, и поэтому я заинтересован в нем

получаете ли вы тот же результат, если используете TADOStoredProc вместо TADOQuery? см. Руководство разработчика Delphi 5

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

Вы также можете попробовать заменить вызов функции константой 1

удачи, и, пожалуйста, дайте нам знать, что вы узнали!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...