Delphi: «Объект параметра неправильно определен. Непоследовательная или неполная информация была предоставлена ​​». - PullRequest
6 голосов
/ 21 августа 2009

Это функция, которая выполняет следующие действия:

  • Создать случайный токен длиной 8
  • Вставьте этот токен в базу данных
  • Если у пользователя уже есть токен, обновите его.

  • Если у пользователя нет токена, вставьте его.

procedure createToken(BenuNr: integer);
var
  AQ_Query:       TADOQuery;
  strToken:       string;
  intZaehler:     integer;
  const cCharSet: string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
begin
    //Random String as Token
    SetLength(strToken, 8);
  for intZaehler := 1 to 8 do
  begin
    strToken[intZaehler] := cCharSet[1+Random(Length(cCharSet))];
  end;
  //Inserts the Token into the Database
  with AQ_Query do
  begin
    try
      AQ_Query := TADOQuery.Create(nil);
      ConnectionString := strConnectionString;
      SQL.Text := 'if EXISTS(select * from TOKN where BENU_NR = :paramBenu_NR) begin update TOKN set TOKEN = :paramTOKEN where BENU_NR = :paramBenu_NR end else insert into TOKN (BENU_NR, TOKEN) values (:paramBENU_NR,:paramTOKEN)';
      Prepared := true;
      Parameters.ParamByName('paramBENU_NR').DataType := ftInteger;
      Parameters.ParamByName('paramTOKEN').DataType := ftString;
      Parameters.ParamByName('paramBENU_NR').Value := BenuNr;
      Parameters.ParamByName('paramTOKEN').Value := strToken;
      ExecSQL;    //<< Exception as stated in the title
    finally
      Free;
    end;
  end;
end;

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

procedure createToken();
var
  AQ_Query:     TADOQuery;
  strToken:     string;
  intZaehler:      integer;
  const cCharSet:  string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
begin
    //Random String as Token
    SetLength(strToken, 8);
  for intZaehler := 1 to 8 do
  begin
    strToken[intZaehler] := cCharSet[1+Random(Length(cCharSet))];
  end;
  //Inserts the Token into the Database
  with AQ_Query do
  begin
    try
      AQ_Query := TADOQuery.Create(nil);
      ConnectionString := strConnectionString;
      SQL.Text := 'update TOKN set TOKEN = :paramTOKEN where BENU_NR = 1';
      Prepared := true;
      Parameters.ParamByName('paramTOKEN').DataType := ftString;
      Parameters.ParamByName('paramTOKEN').Value := strToken;
      ExecSQL;   //<< No more exception
    finally
      Free;
    end;
  end;
end;

Похоже, что на SQL допускается только 1 параметр. Я использую Delphi 7 и MSSQL Server 2005

Есть идеи, как исправить первый блок кода, чтобы он работал?

Ответы [ 8 ]

11 голосов
/ 28 августа 2009

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

2 голосов
/ 21 августа 2009

У меня была возможность попробовать это с компилятором :) Я должен когда-нибудь установить его дома.

Хотя я все еще нахожу ваше использование WITH необычным, похоже, оно работает нормально.

Я видел ошибку, которую вы получаете в нескольких случаях:

  1. Попытка выполнить более одного запроса против соединения сразу (из-за многопоточности или таймера + processMessages)
  2. С TADOStoredProc, когда имя_процесса неверно
  3. Иногда, если ADO не может разобрать запрос - не может проверить это без вашей схемы БД.

Обратите внимание, что в SQL Server нет необходимости явно определять тип параметра. Они автоматически назначаются событием OnChanged, прикрепленным к объекту SQL TStringList.

В результате лучше либо назначить свойство SQL.Text (как вы это делаете), либо, если используете .Add ('SELECT ...'), использовать пару SQL.BeginUpdate / SQL.EndUpdate.

Оригинальный ответ:

  with AQ_Query do
  begin
    try
      AQ_Query := TADOQuery.Create(nil);
      ConnectionString := strConnectionString;

Хотя это, кажется, работает, кажется немного странным ссылаться на объект, прежде чем создавать его экземпляр.

AQ_Query должен быть создан перед оператором with:

  AQ_Query := TADOQuery.Create(nil);
  with AQ_Query do
  begin
    try
      ConnectionString := strConnectionString;

Лучше, но не используйте С - Это напрашивается на неприятности.

Также обратите внимание, что создание объекта должно быть ДО попытки .. окончательно. Как написано, у вас будет предупреждение компилятора. Не игнорируйте их - они помогут вам написать лучший код.

1 голос
/ 13 февраля 2012

Выключите приготовление. Установите подготовленный = False для компонента ADO. Похоже, что сервер видит его до того, как оба параметра существуют, и готовит (компилирует) его. Когда вы выполняете его с двумя параметрами, список параметров не совпадает с подготовленным оператором.

1 голос
/ 26 августа 2009

До сих пор я не решал проблему.

Но я предполагаю, что проблема лежит где-то внутри параметров и того, как они доступны. Компилятор выбирает их по индексу, а не по имени, как я предполагал.

При более детальном рассмотрении компонента TADOQuery в OI (особенно в разделе TParameters) вы можете увидеть эти индексы.

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

1 голос
/ 21 августа 2009

Хотя эта ошибка является сложной, вы можете диагностировать ее достаточно, чтобы убедиться, что ваш запрос действителен. Проблема в ваших параметрах. Лучший способ найти истинную проблему состоит в том, чтобы SQL Server Profiler отслеживал вашу базу данных при поступлении запроса. Он покажет вам, как были интерпретированы параметры. Скопируйте этот запрос в текстовый редактор, чтобы увидеть, в чем ваша проблема.

Если вы не можете использовать SQL Server Profiler, вам просто нужно вывести значения «BenuNr» и «strToken» на экран или консоль, чтобы вы могли действительно видеть, что вы передаете в качестве параметров.

0 голосов
/ 12 марта 2012

Для меня эта ошибка произошла без параметров, когда (в противном случае полностью допустимый) SQL-оператор содержал буквальное значение varchar с двоеточием (:) внутри кавычки. Э.Г.

UPDATE ... SET myfield = 'foo " :bar "'

Решением может быть переключение на параметры из уродливых встроенных литералов. Я не нашел другого правильного обходного пути.

0 голосов
/ 21 августа 2009

Вам не нужно указывать DataType. После успешного вызова Prepare; параметры должны быть правильно настроены на основе определения таблицы сервера.

Я предполагаю, что при присвоении DataType параметр, вероятно, сбрасывается, а некоторая информация отсутствует, например, ParamType должно быть ptInput, но сбрасывается на ptUnknown или что-то в этом роде.

Попробуйте удалить те строки, в которых вы указали тип данных, и посмотрите, поможет ли это.

0 голосов
/ 21 августа 2009

Уч. Вы просто столкнулись с тем, что наш системный архитектор на работе называет «худшей ошибкой за всю историю». Это довольно общая ошибка и может означать все виды вещей. Но я только когда-либо видел это в заявлениях INSERT, поэтому попробуйте поискать там. Это связано с определением, что ADO каким-либо образом не соответствует схеме базы данных.

Попробуйте сократить запрос, чтобы выполнить вставку, и использовать профилировщик SQL, входящий в состав SQL Management Studio, чтобы посмотреть, что делает ADO при его запуске. Скорее всего, он запросит структуру вашей таблицы, сравнит ее со структурой вашего оператора, и в итоге ему не понравится то, что он находит, и он никогда не отправит команду INSERT в базу данных.

Убедитесь, что у вас есть правильные типы данных в полях, и что вы можете успешно запустить INSERT в эту таблицу, используя только эти два значения. Это может не сработать - это это худшая ошибка за всю историю - но это должно дать вам отправную точку.

...