【问题标题】:Delphi: “Parameter object is improperly defined. Inconsistent or incomplete information was provided.”Delphi:“参数对象定义不正确。提供的信息不一致或不完整。”
【发布时间】:2009-08-21 13:40:03
【问题描述】:

这是一个执行以下操作的函数:

  • 创建一个长度为 8 的随机 Token
  • 将该令牌插入数据库
  • 如果用户已有令牌,请更新它。

  • 如果用户没有令牌,则插入它。

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

知道如何修复第一个代码块以使其正常工作吗?

【问题讨论】:

  • 尝试使用sql profiler,分析sql查询。
  • 参数名称是否区分大小写?因为您在设置参数时使用了 paramBENU_NR,但在代码中使用了 paramBenu_NR - 如果它们区分大小写,则可能会抱怨,因为您没有在查询中为参数指定值。

标签: delphi parameters ado


【解决方案1】:

要完成这项工作,您必须在 SQL 子句中只使用每个参数一次。要多次使用相同的参数,只需用新名称声明它。我不知道为什么会这样,但我知道这很烦人。

【讨论】:

  • 啊...(几乎)总是使用存储过程来执行“更新/插入”查询的小优点之一。
【解决方案2】:

我有机会用编译器尝试这个:) 有时我必须在家里安装一个。

虽然我仍然发现您对 WITH 的使用不寻常,但它似乎工作正常。

我在几种情况下看到了您遇到的错误:

  1. 尝试运行多个查询 立即针对连接(由于线程或计时器 + processMessages)
  2. 当 ProcedureName 不正确时使用 TADStoredProc
  3. 有时如果 ADO 无法解析查询 - 如果没有您的数据库架构,则无法对此进行测试。

请注意,在 SQL Server 中不需要显式定义参数类型。这些由附加到 SQL TStringList 对象的 OnChanged 事件自动分配。

因此,最好分配 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;

最好不要使用WITH - 这是自找麻烦。

还要注意,对象创建应该在尝试之前..最后。如所写,您将收到编译器警告。不要忽略这些 - 它们可以帮助您编写更好的代码。

【讨论】:

  • AQ_Query 可能是在表单设计器中创建的数据集。
  • 它在过程 CreateToken 的 var 子句中。即使是,重新分配它 [ AQ_Query := TADOQuery.Create(nil); ] 是不正确的。在 TRY 之后创建对象也是不正确的用法,除非它已被分配到 nil - 您将在“finally Free; end;”中收到“可能未分配”警告部分。
  • @Mason Wheeler:不,该函数是完全动态创建的。没有设计师。
【解决方案3】:

虽然此错误具有挑战性,但您可以对其进行足够的诊断以确认您的查询有效。问题出在您的参数中。找到真正问题的最佳方法是让 SQL Server Profiler 在查询进入时跟踪您的数据库。它将向您展示如何解释参数。将该查询复制到文本编辑器中以查看问题出在哪里。

如果您无法使用 SQL Server Profiler,您应该将值“BenuNr”和“strToken”输出到屏幕或控制台,以便您可以真正看到作为参数传入的内容。

【讨论】:

    【解决方案4】:

    直到现在,我还没有解决这个问题。

    但我想问题出在参数内部的某个地方以及如何访问它们。编译器通过索引而不是我假设的名称来选择它们。

    仔细查看 OI 中的 TADOQuery 组件(尤其是在 TParameters 部分),您可以看到该索引。

    最后它可能需要首先添加一个参数,为其分配一个名称,然后插入一个值,或类似的东西。

    【讨论】:

    • 为了它的价值:我刚刚遇到了这个错误并修复了它。我追查到我正在分配一个参数的Value,其变量值为VarType varEmpty。改用Null 修复它。
    【解决方案5】:

    关闭准备。在 ADO 组件上设置prepared=False。似乎服务器在两个参数存在之前就看到了它,并准备(编译)它。当你使用两个参数执行它时,参数列表与准备好的语句不匹配。

    【讨论】:

      【解决方案6】:

      哎哟。您刚刚遇到了我们的系统架构师在工作中所说的“有史以来最严重的错误”。这是一个非常普遍的错误,可能意味着各种各样的事情。但我只在 INSERT 语句中看到过它,所以试着看看那里。这是由于 ADO 以某种方式与数据库架构不匹配的定义造成的。

      尝试减少您的查询,只执行插入操作,并使用 SQL Management Studio 中包含的 SQL 分析器来观察 ADO 在触发时正在做什么。它很可能会询问您的表结构,将其与您的语句结构进行比较,最终不喜欢它找到的内容,并且从未真正将 INSERT 命令发送到数据库。

      确保您的字段具有正确的数据类型,并且您可以仅使用这两个值成功地将 INSERT 运行到该表中。这可能行不通——毕竟,这有史以来最糟糕的错误——但它应该给你一个起点。

      【讨论】:

        【解决方案7】:

        您无需指定DataType。成功调用Prepare; 后,应根据服务器表定义正确配置参数。

        我的猜测是,通过分配DataType,可能会重置参数并且缺少一些信息,例如,ParamType 应该是ptInput 但被重置为ptUnknown 或类似的东西。

        尝试删除您设置 DataType 的那些行,看看是否有帮助。

        【讨论】:

          【解决方案8】:

          对我来说,当(否则完全有效的)SQL 语句在带引号的子文本中包含带有冒号 (:) 的 varchar 文字值时,这个错误在没有参数的情况下出现。例如

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

          解决方案可以是从丑陋的嵌入式文字切换到参数。我还没有找到其他合适的解决方法。

          【讨论】:

          • 你试过简单地设置ParamCheck=False吗?
          • @RemyLebeau 老实说,在收到您的评论通知之前,我什至不记得我在 2012 年做过 Delphi... ;-)
          猜你喜欢
          • 2016-06-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-03-12
          • 1970-01-01
          • 2020-03-08
          • 1970-01-01
          相关资源
          最近更新 更多