【问题标题】:SQL conversion error / how to insert parameter into select statement?SQL 转换错误/如何在 select 语句中插入参数?
【发布时间】:2014-10-20 17:43:56
【问题描述】:

我正在运行一个通过 FireDAC 连接的 IB 数据库。

以下动态查询有效:

INSERT INTO RELATIONS (C_ID, M_ID, A_ID)
SELECT c.C_ID, 0, a.A_ID
FROM CATEGORIES c, ACTIONS a
WHERE c.NAME = :CATEGORY AND a.NAME = :ACTION

我在 RELATIONS A_INDEX(Integer) 中添加一列。对于这个专栏,我想提供一个参数,所以我这样做:

INSERT INTO RELATIONS (C_ID, M_ID, A_ID, A_INDEX)
SELECT c.C_ID, 0, a.A_ID, :A_INDEX
FROM CATEGORIES c, ACTIONS a
WHERE c.NAME = :CATEGORY AND a.NAME = :ACTION

但这不起作用。出于某种原因,当我执行查询时,它抱怨CATEGORY 参数存在转换错误。

这是此查询操作的完整代码:

  procedure Test;
  var
    Query: TFDQuery;
  begin
    Query := TFDQuery.Create(nil);
    try
      Query.Connection := DBDataModule.dbMain;
      Query.ResourceOptions.ParamCreate := False;

      Query.SQL.BeginUpdate;

      Query.SQL.Add('INSERT INTO RELATIONS (C_ID, M_ID, A_ID, A_INDEX)');
      Query.SQL.Add('SELECT c.C_ID, 0, a.A_ID,:A_INDEX');
      Query.SQL.Add('FROM CATEGORIES c, ACTIONS a');
      Query.SQL.Add('WHERE c.NAME = :CATEGORY AND a.NAME = :ACTION');

      Query.SQL.EndUpdate;

      Query.Params.CreateParam(TFieldType.ftInteger, 'A_INDEX', ptInput);
      Query.Params.CreateParam(TFieldType.ftFixedWideChar, 'CATEGORY', ptInput);
      Query.Params.CreateParam(TFieldType.ftFixedWideChar, 'ACTION', ptInput);
      Query.ParamByName('CATEGORY').Size := 255;
      Query.ParamByName('ACTION').Size := 255;

      Query.Prepare;

      Query.ParamByName('A_INDEX').Value := 0;
      Query.ParamByName('CATEGORY').Value := 'Foo';
      Query.ParamByName('ACTION').Value := 'Foo';

      Query.ExecSQL; // <-- Exception
    finally
      Query.Free;
    end;    
  end;

我还在学习 SQL、数据库和 FireDAC,所以我真的不明白为什么它允许我在 select 语句中输入直接值,但参数是不行的。

我还能如何使用第一个查询将参数动态插入A_INDEX 列?

【问题讨论】:

  • 您是手动创建参数还是使用“Query.ResourceOptions.ParamCreate := True;” ?
  • @valex,当你问我这个问题时,我的参数是自动创建的,Vitor 建议我手动做,所以我做了,没有改变。我已经用完整的代码更新了我的问题。
  • 我现在无法检查这个,但我想这里的问题是:A_INDEX 被放置在 SELECT 列表中。尝试将您的查询更改为例如INSERT INTO RELATIONS (C_ID, M_ID, A_ID, A_INDEX) SELECT c.C_ID, 0, a.A_ID, 0 FROM CATEGORIES c, ACTIONS a WHERE c_id=:A_INDEX AND c.NAME = :CATEGORY AND a.NAME = :ACTION,我认为它会运行良好而不会出现任何错误。
  • @valex,我认为你不明白我的问题。我需要动态更改A_INDEX 列的值,我不能让它为0。还有c_id = :A_INDEX 是错误的。 c_ID 是表 Relations 中的外键,并绑定到表 Categories 中的主键 c_ID。我的问题是,为什么我可以在 Select 语句中为列设置一个值,但我显然不能在其中放置一个参数?
  • 我了解您的问题,我认为这是一个错误,:A_INDEX 未处理,因为它在 SELECT 列表中。因此,我认为您应该尝试在 WHERE 部分中的所有参数的位置做出此声明,并且我认为它将运行而不会出错。在这种情况下,将使用相同的参数集和相同的顺序 (:A_INDEX,:CATEGORY,:ACTION),因此您将找到此问题的根源。

标签: sql delphi delphi-xe6 interbase firedac


【解决方案1】:

您不能像这样对查询进行参数化:

INSERT INTO RELATIONS (C_ID, M_ID, A_ID, A_INDEX)
SELECT c.C_ID, 0, a.A_ID, :A_INDEX
FROM CATEGORIES c, ACTIONS a
WHERE c.NAME = :CATEGORY AND a.NAME = :ACTION

特别是它的 SELECT 表达式:

SELECT c.C_ID, 0, a.A_ID, :A_INDEX

克服这种情况的唯一方法是预处理器宏,但它们不能作为真正的查询参数工作,因为它们会修改 SQL 命令,因此 DBMS 必须重新准备命令。

例如:

procedure Test;
var
  Query: TFDQuery;
begin
  Query := TFDQuery.Create(nil);
  try
    Query.Connection := DBDataModule.dbMain;

    Query.SQL.Add('INSERT INTO RELATIONS (C_ID, M_ID, A_ID, A_INDEX)');
    Query.SQL.Add('SELECT c.C_ID, 0, a.A_ID, &A_INDEX'); // ← & defines macro
    Query.SQL.Add('FROM CATEGORIES c, ACTIONS a');
    Query.SQL.Add('WHERE c.NAME = :CATEGORY AND a.NAME = :ACTION');

    Query.ParamByName('CATEGORY').Value := 'Foo';
    Query.ParamByName('ACTION').Value := 'Foo';
    Query.MacroByName('A_INDEX').AsIdentifier := '0';

    Query.ExecSQL;
  finally
    Query.Free;
  end;    
end;

但如果 A_INDEX 字段值应该是自动递增值,请像这样创建它。或者,如果它是来自另一个表的值,请在查询中加入该表。宏不能用作真正的命令参数,并且在插入许多需要准备好的命令的记录时可能效率低下。

【讨论】:

    【解决方案2】:

    可能你在DFM中设置了SQL(直接在组件中),可以在组件的Params属性中看到创建的参数。删除所有参数或手动添加 A_INDEX(整数,输入)。

    我强烈建议您在源代码中创建参数并将属性 ParamCreate 设置为 false。如果您知道需要手动创建,您可以轻松控制它。

    要手动创建参数,请使用 CreateParam 方法(在 TParams 类中)。

    【讨论】:

    • 我所有的查询都是局部变量,唯一属于设计组件的是TFDConnection。我接受了您的建议,并手动创建了参数,结果是一样的。我已经用我用于此查询的完整代码更新了我的答案。
    • 您遇到的错误基本上是说数据库无法执行此操作:“其中列 = 字符串”我建议您将代码更改为以下代码并反馈给我们:Query.ParamByName ('CATEGORY').Value := QuotedStr('Foo'); Query.ParamByName('ACTION').Value := QuotedStr('Foo');
    • 感谢您试图找出我的代码有什么问题,但在这里使用 QuotedStr 是错误的。参数不希望您将字符串括在引号中,您可以在文档中验证这一点。
    • 是的,我知道而且它也很丑,但我认为你应该尝试一下。
    • 你第一次建议的时候我试过了,没用但没想到会。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-19
    • 2014-05-25
    • 1970-01-01
    • 1970-01-01
    • 2015-05-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多