Может ли dbExpress TSQLQuery использовать? как параметры? - PullRequest
5 голосов
/ 14 февраля 2012

Мы портируем код на Delphi XE2, и нам нужно изменить наши компоненты доступа к данным со стороннего ODBCExpress, который больше не занимается бизнесом, на TSQLQuery dbExpress.

У нас есть параметризованный 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 с точки зрения того, что для меня важно?

Ответы [ 3 ]

8 голосов
/ 14 февраля 2012

Я думаю, что самый быстрый подход состоит в том, чтобы использовать помощники классов, которые будут реализовывать эту функциональность примерно так:

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, но было ленивым, чтобы реализовать это ...

5 голосов
/ 14 февраля 2012

Нетривиальный подход:

  • подкласс TMyQuery из TSQLQuery;
  • в конструкторе TMyQuery установите TStringList(SQL).OnChange в свой собственный QueryChanged метод.См. TSQLQuery.QueryChanged в модуле SqlExpr.pas, чтобы узнать, что он делает.
  • там вам нужно будет заменить вызов SetParamsFromSQL на ваш собственный, который будет анализировать текст SQL и создавать объект параметра для каждого '?'occurence.

Более простой подход:

  • процедура создания, которая получит строку SQL и коллекцию Params;
  • процедура будет анализировать текст SQL исоздать параметр объекта для каждого '?'occurence;
  • установите для TSQLQuery.ParamCheck значение False и вызовите процедуру после установки свойства SQL.

В заключение рассмотрите возможность использования решений сторонних разработчиков, например AnyDAC .Он поддерживает ODBC и '?'маркеры параметров.

0 голосов
/ 17 февраля 2012

В итоге я написал метод для преобразования вопросительных знаков в запросе в параметры стиля: 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;
...