【问题标题】:Can dbExpress's TSQLQuery use ? as parameters?dbExpress 的 TSQLQuery 可以使用吗?作为参数?
【发布时间】:2012-02-14 04:54:01
【问题描述】:

我们已经将代码移植到 Delphi XE2,并且需要将我们的数据访问组件从不再使用的第三方 ODBCExpress 更改为 dbExpress 的 TSQLQuery。

我们有参数化的 SQL 查询,例如:

sSQL :=
  'UPDATE ZTestData SET '+
  ' StringField =?, '+
  ' IntField = ?, '+
  ' DecimalField = ?, '+
  ' BooleanField = ?, '+
  ' DateTimeField = ?, '+
  ' TextField = ? '+
  ' WHERE UniqueID = 3';

如果我们使用以下代码:

var
  qry:TSQLQuery;
begin
  qry.Close;
  qry.SQL.Text := sSQL;
  ShowMessage(IntToStr(qry.Params.Count));
end;

它返回 0,所以我们无法让绑定工作,但是如果我们将 sSQL 更改为:

sSQL :=
  'UPDATE ZTestData SET '+
  ' StringField =:Param1, '+
  ' IntField = :Param2, '+
  ' DecimalField = ?, '+
  ' BooleanField = ?, '+
  ' DateTimeField = ?, '+
  ' TextField = ? '+
  ' WHERE UniqueID = 3';

它返回 2。

将所有 SQL 查询更改为新的参数语法会很麻烦。无论如何,TSQLQuery 是否可以识别?语法?

我看到 DBXCommon.TDBXCommand 使用 ?语法:

http://www.andreanolanusse.com/en/parameterized-queries-with-dbexpress-dbx-framework/

但这意味着丢弃我们使用 TSQLQuery 的代码。解决此问题的最快/最简单方法是什么?无论如何,TSQLQuery 和 TDBXCommand 有什么区别,与我相关的是什么?

【问题讨论】:

    标签: delphi dbexpress


    【解决方案1】:

    我认为最快的方法是使用类助手来实现此功能,例如:

    type
      TMyParamsHelper = class Helper for TSQLQuery
      public
        function SetupParams(AParamList: array of Variant): Boolean; overload;
        function SetupParams(ASQL: string; AParamList: array of Variant): Boolean; overload;
      end;
    
    // implementation
    
    function TMyParamsHelper.SetupParams(AParamList: array of Variant): Boolean;
    var
      Index: Integer;
    begin
      // here you can process the SQL as text and replace each ?
      // with :paramINDEX
      // first occurence of ? will be :param0, second will be :param1, etc.
      // implement your replace algorithm before the "for loop"
      for Index := Low(AParamList) to High(AParamList) do
        ParamByName(Format(':param%d', [Index])).AsVaraint := AParamList[ Index ];
      // of course you need to do it in a try...except block and return TRUE|FALSE
    end;
    
    function TMyParamsHelper.SetupParams(ASQL: string; AParamList: array of Variant): Boolean;
    begin
      SQL.Text := ASQL;
      Result := SetupParams( AParamList );
    end;
    

    所以现在你要做的就是打电话:

    ...
    ASQLQueryVariable.SetupParams([2012, 'Hello World', 2.14, 'It WORKS!']);
    // or
    ASQLQueryVariable.SetupParams(
      'UPDATE MyTable SET Year = ?, Title = ?, Cents = ?, Comment = ? WHERE <CLAUSE HERE>',
      [2012, 'Hello World', 0.02, 'It WORKS!']
    );
    ...
    

    注意:我是在脑海中写下这个,可能有错别字,可能不是最好的方法......

    让我知道这对你有什么影响,我一直想要“?”而不是 ParamByName 但懒得实现它......

    【讨论】:

    • 看我的回答。 DB.TParams.ParseSQL 是一种将参数转换为问号的方法,如果这是您要走的路线。我自己更喜欢 ParamByName。
    【解决方案2】:

    不平凡的方法:

    • 子类TMyQuery来自TSQLQuery
    • TMyQuery 的构造函数中将TStringList(SQL).OnChange 设置为您自己的QueryChanged 方法。有关详细信息,请参阅 SqlExpr.pas 单元中的TSQLQuery.QueryChanged,了解它在做什么。
    • 您需要将SetParamsFromSQL 调用替换为您自己的调用,这将解析SQL 文本并为每个“?”创建参数对象。发生。

    更简单的方法:

    • 创建过程,将获得一个 SQL 字符串和 Params 集合;
    • 该过程将解析 SQL 文本并为每个“?”创建参数对象发生;
    • TSQLQuery.ParamCheck 设置为False,并在设置SQL 属性后调用proc。

    最后考虑使用 3d 派对解决方案,例如AnyDAC。它支持 ODBC 和 '?'参数标记。

    【讨论】:

      【解决方案3】:

      我最终编写了一个方法来将查询中的问号转换为 :param1 样式参数。有趣的是,Delphi 有一个DB.TParams.ParseSQL 方法可以将参数转换为问号。这种方法基本上是相反的。

      function THstmt.AddParamsToSQL(const SQL: String): String;
      var
        LiteralChar: Char;
        CurPos, StartPos, BeginPos: PChar;
        ParamCount:Integer;
      begin
        //Locates the question marks in an SQL statement
        //and replaces them with parameters.
        //i.e. the reverse of DB.TParams.ParseSQL
      
        //This method is base on DB.TParams.ParseSQL
      
        //For example, given the SQL string
        //SELECT * FROM EMPLOYEES WHERE (ID = ?) AND (NAME = ?)
      
        //ParseSQL returns the string
        //SELECT * FROM EMPLOYEES WHERE (ID = :1) AND (NAME = :2)
      
        Result := '';
      
        ParamCount := 0;
        StartPos := PChar(SQL);
        BeginPos := StartPos;
        CurPos := StartPos;
        while True do
        begin
          // Fast forward
          while True do
          begin
            case CurPos^ of
              #0, '?', '''', '"', '`':
                Break;
            end;
            Inc(CurPos);
          end;
      
          case CurPos^ of
            #0: // string end
              Break;
            '''', '"', '`': // literal
            begin
              LiteralChar := CurPos^;
              Inc(CurPos);
              // skip literal, escaped literal chars must not be handled because they
              // end the string and start a new string immediately.
              while (CurPos^ <> #0) and (CurPos^ <> LiteralChar) do
                Inc(CurPos);
              if CurPos^ = #0 then
                Break;
              Inc(CurPos);
            end;
            '?': //parameter
            begin
              Inc(CurPos);
              Inc(ParamCount);
              Result := Result + Copy(SQL, StartPos - BeginPos + 1, CurPos - StartPos - 1) + ':' + IntToStr(ParamCount);
              StartPos := CurPos;
            end;
          end;
        end;
        Result := Result + Copy(SQL, StartPos - BeginPos + 1, CurPos - StartPos);
      end;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-22
        • 1970-01-01
        • 1970-01-01
        • 2015-03-10
        • 1970-01-01
        • 2010-12-08
        • 2016-09-14
        • 1970-01-01
        相关资源
        最近更新 更多