首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何使用dbExpress执行SQL脚本?

如何使用dbExpress执行SQL脚本?
EN

Stack Overflow用户
提问于 2012-03-01 22:09:49
回答 5查看 10.1K关注 0票数 2

我正在将一个旧的Delphi应用程序(使用ZeosDB)迁移到Delphi XE2。我想用dbExpress作为对Firebird 2.5或MS-SQL数据库访问的ZeosDB替代。我需要运行很多用于创建表、视图和存储过程的sql脚本。Firebird脚本命令与^、MS-SQL脚本命令用"GO“分隔。

如何使用dbexpress连接在数据库上运行这些脚本?ZeosDB提供了一个TZSqlProcessor,但我找不到任何与dbExpress等效的组件。

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2012-03-05 21:56:55

我不使用SQL,但据我所知,您一次只能执行一个DBExpress命令(通过execute或ExecuteDirect)。换句话说,您不能将整个脚本放入Execute方法中。

这与FireBird和MS使用的不同命令语法(^与GO)无关。你必须明白'^‘符号或'GO’命令不是"TSQL命令“!两者都是特定的命令分隔符,由各自的应用程序用来针对SQL引擎执行命令。相反,它是"Firebird管理器“(或其调用方式)和"SQL查询探查器”(或"SQL Server Management Studio")之间的区别。

解决方案是使用某种解析器,将脚本拆分成一个单独的命令列表,然后逐个TSQLConnection.Execute这些命令。

类似于下面的伪代码:

代码语言:javascript
复制
var
  DelimiterPos: Integer;
  S: String;
  Command: String;
begin
  S:= ScriptFile; // ScriptFile: String - your whole script
  While True Do
  begin
    DelimiterPos:= Pos('^', ScriptFile);
    if DelimiterPos = 0 then DelimiterPos:= Length(S);
    Command:= Copy(S, 1, DelimiterPos - 1);
    SQLConnection.Execute(Command);
    Delete(S, 1, DelimiterPos);
    if Lengh(S) = 0 Then Exit;
  end;
end;

请注意,只有在脚本中没有使用“^”符号而是命令分隔符的情况下,上面的示例才能正常工作。

顺便说一句,我确信有一些已经构建好的组件可以帮你做到这一点(比如TZSQLProcessor)。我不知道有什么可以给你指点的。

旁注2:我非常确定,您将不得不修改您的脚本,以完全兼容MS SQL。尽管Firebird和MS SQL都是SQL服务器,但它们在DML/DDL语法上总是存在差异。

编辑:

  1. 如果你能把SQL脚本“重写”成代码,你就可以使用Jedi VCL jvStringHolder组件。将每个单独的命令作为一个项目(类型为TStrings)放在jvStringHolder.
  2. Creating中解析器相当复杂,但不可撤消。在SynEdit的启发下,我创建了这些类来满足您的需求:使用TSQLScript.ParseScript加载脚本,然后迭代Commandindex: integer属性。SQLLexer不是完整的SQL Lexer,但实现了关键字分离与注释,括号,代码折叠等我还添加了一个特殊的语法到注释($ sign in注释块),帮助我把标题到脚本中。这是我的一个项目的完整复制-粘贴。我不会给出更多的解释,但我希望你能明白这个想法,并让它在你的项目中运行。

单元SQLParser;

代码语言:javascript
复制
interface

type

  TTokenKind = (tkUknown, tkEOF, tkComment, tkKeyword, tkIdentifier,
                tkCommentParam, tkCommentParamValue, tkCommandEnd, tkCRLF);

  TBlockKind = (bkNone, bkLineComment, bkBlockComment);

  TSQLLexer = class
  private
    FBlockKind: TBlockKind;
    FParseString: String;
    FPosition: PChar;
    FTokenKind: TTokenKind;
    FTokenPosition: PChar;
    function GetToken: String;
    procedure Reset;
    procedure SetParseString(Value: String);
  protected
    procedure ReadComment;
    procedure ReadCommentParam;
    procedure ReadCommentParamValue;
    procedure ReadCRLF;
    procedure ReadIdentifier;
    procedure ReadSpace;
  public
    constructor Create(ParseString: String);
    function NextToken: TTokenKind;

    property Position: PChar read FPosition;
    property SQLText: String read FParseString write SetParseString;
    property Token: String read GetToken;
    property TokenKind: TTokenKind read FTokenKind;
    property TokenPosition: PChar read FTokenPosition;
  end;



implementation

uses SysUtils;

{ TSQLLexer }

constructor TSQLLexer.Create(ParseString: string);
begin
  inherited Create;
  FParseString:= ParseString;
  Reset;
end;

function TSQLLexer.GetToken;
begin
  SetString(Result, FTokenPosition, FPosition - FTokenPosition);
end;

function TSQLLexer.NextToken: TTokenKind;
begin
  case FBlockKind of
    bkLineComment, bkBlockComment: ReadComment;
    else
      case FPosition^ of
      #0: FTokenKind:= tkEOF;
      #1..#9, #11, #12, #14..#32:
        begin
          ReadSpace;
          NextToken;
        end;
      #10, #13: ReadCRLF;
      '-':
        if PChar(FPosition +1)^ = '-' then
          ReadComment
        else
          Inc(FPosition);
      '/':
        if PChar(FPosition +1)^ = '*' then
          ReadComment
        else
          Inc(FPosition);
      'a'..'z', 'A'..'Z': ReadIdentifier;
      ';':
        begin
          FTokenPosition:= FPosition;
          Inc(FPosition);
          FTokenKind:= tkCommandEnd;
        end
      else
        Inc(FPosition);
      end;
  end;
  Result:= FTokenKind;
end;


procedure TSQLLexer.ReadComment;
begin
  FTokenPosition:= FPosition;
  if not (FBlockKind in [bkLineComment, bkBlockComment])  then
  begin
    if FPosition^ = '/' then
      FBlockKind:= bkBlockComment
    else
      FBlockKind:= bkLineComment;
    Inc(FPosition, 2);
  end;
  case FPosition^ of
    '$': ReadCommentParam;
    ':': ReadCommentParamValue;
  else
    while not CharInSet(FPosition^, [#0, '$']) do
    begin
      if FBlockKind = bkBlockComment then
      begin
        if (FPosition^ = '*') And (PChar(FPosition + 1)^ = '/') then
        begin
          Inc(FPosition, 2);
          FBlockKind:= bkNone;
          Break;
        end;
      end
      else
      begin
        if CharInSet(Fposition^, [#10, #13]) then
        begin
          ReadCRLF;
          FBlockKind:= bkNone;
          Break;
        end;
      end;
      Inc(FPosition);
    end;
    FTokenKind:= tkComment;
  end;
end;

procedure TSQLLexer.ReadCommentParam;
begin
  Inc(FPosition);
  ReadIdentifier;
  FTokenKind:= tkCommentParam;
end;

procedure TSQLLexer.ReadCommentParamValue;
begin
  Inc(FPosition);
  ReadSpace;
  FTokenPosition:= FPosition;
  while not CharInSet(FPosition^, [#0, #10, #13]) do
    Inc(FPosition);
  FTokenKind:= tkCommentParamValue;
end;

procedure TSQLLexer.ReadCRLF;
begin
  while CharInSet(FPosition^, [#10, #13]) do
    Inc(FPosition);
  FTokenKind:= tkCRLF;
end;

procedure TSQLLexer.ReadIdentifier;
begin
  FTokenPosition:= FPosition;
  while CharInSet(FPosition^, ['a'..'z', 'A'..'Z', '_']) do
    Inc(FPosition);

  FTokenKind:= tkIdentifier;

  if Token = 'GO' then
    FTokenKind:= tkKeyword;
end;

procedure TSQLLexer.ReadSpace;
begin
  while CharInSet(FPosition^, [#1..#9, #11, #12, #14..#32]) do
  Inc(FPosition);
end;

procedure TSQLLexer.Reset;
begin
  FTokenPosition:= PChar(FParseString);
  FPosition:= FTokenPosition;
  FTokenKind:= tkUknown;
  FBlockKind:= bkNone;
end;

procedure TSQLLexer.SetParseString(Value: String);
begin
  FParseString:= Value;
  Reset;
end;

end.

解析器:

代码语言:javascript
复制
type
  TScriptCommand = class
  private
    FCommandText: String;
  public
    constructor Create(ACommand: String);
    property CommandText: String read FCommandText write FCommandText;
  end;

  TSQLScript = class
  private
    FCommands: TStringList;
    function GetCount: Integer;
    function GetCommandList: TStrings;
    function GetCommand(index: Integer): TScriptCommand;
  protected
    procedure AddCommand(AName: String; ACommand: String);
  public
    Constructor Create;
    Destructor Destroy; override;
    procedure ParseScript(Script: TStrings);

    property Count: Integer read GetCount;
    property CommandList: TStrings read GetCommandList;
    property Command[index: integer]: TScriptCommand read GetCommand;
  end;

{ TSQLScriptCommand }

constructor TScriptCommand.Create(ACommand: string);
begin
  inherited Create;
  FCommandText:= ACommand;
end;

{ TSQLSCript }

constructor TSQLScript.Create;
begin
  inherited;
  FCommands:= TStringList.Create(True);
  FCommands.Duplicates:= dupIgnore;
  FCommands.Sorted:= False;
end;

destructor TSQLScript.Destroy;
begin
  FCommands.Free;
  inherited;
end;

procedure TSQLScript.AddCommand(AName, ACommand: String);
var
  ScriptCommand: TScriptCommand;
  S: String;
begin
  if AName = '' then
    S:= SUnnamedCommand
  else
    S:= AName;
  ScriptCommand:= TScriptCommand.Create(ACommand);
  FCommands.AddObject(S, ScriptCommand);
end;

function TSQLScript.GetCommand(index: Integer): TScriptCommand;
begin
  Result:= TScriptCommand(FCommands.Objects[index]);
end;

function TSQLScript.GetCommandList: TStrings;
begin
  Result:= FCommands;
end;

function TSQLScript.GetCount: Integer;
begin
  Result:= FCommands.Count;
end;

procedure TSQLScript.ParseScript(Script: TStrings);
var
  Title: String;
  Command: String;
  LastParam: String;
  LineParser: TSQLLexer;
  IsNewLine: Boolean;
  LastPos: PChar;

  procedure AppendCommand;
  var
    S: String;
  begin
    SetString(S, LastPos, LineParser.Position - LastPos);
    Command:= Command + S;
    LastPos:= LineParser.Position;
  end;

  procedure FinishCommand;
  begin
    if Command <> '' then
      AddCommand(Title, Command);
    Title:= '';
    Command:= '';
    LastPos:= LineParser.Position;
    if LastPos^ = ';' then Inc(LastPos);
  end;

begin
  LineParser:= TSQLLexer.Create(Script.Text);
  try
    LastPos:= LineParser.Position;
    IsNewLine:= True;
    repeat
      LineParser.NextToken;
      case LineParser.TokenKind of
        tkComment: LastPos:= LineParser.Position;
        tkCommentParam:
          begin
            LastParam:= UpperCase(LineParser.Token);
            LastPos:= LineParser.Position;
          end;
        tkCommentParamValue:
          if LastParam = 'TITLE' then
          begin
            Title:= LineParser.Token;
            LastParam:= '';
            LastPos:= LineParser.Position;
          end;
        tkKeyword:
            if (LineParser.Token = 'GO') and IsNewLine then FinishCommand
            else
              AppendCommand;
        tkEOF:
          FinishCommand;
        else
          AppendCommand;
      end;
      IsNewLine:= LineParser.TokenKind in [tkCRLF, tkCommandEnd];
    until LineParser.TokenKind = tkEOF;
  finally
    LineParser.Free;
  end;
end;
票数 6
EN

Stack Overflow用户

发布于 2012-03-01 23:10:46

您需要使用TSQLConnection。这是一个组件,有两个方法,ExecuteExecuteDirect。第一个方法不接受参数,但第二个方法接受。

使用第一种方法:

代码语言:javascript
复制
procedure TForm1.Button1Click(Sender: TObject);
var
   MeuSQL: String;
begin
   MeuSQL := 'INSERT INTO YOUR_TABLE ('FIELD1', 'FIELD2') VALUES ('VALUE1', 'VALUE2')';
   SQLConnection.ExecuteDirect(MeuSQL);
end;

如果您愿意,您可以使用事务。

使用第二种方法:

代码语言:javascript
复制
procedure TForm1.Button1Click(Sender: TObject);
var
  MySQL: string;
  MyParams: TParams;
begin
  MySQL := 'INSERT INTO TABLE ("FIELD1", "FIELD2") VALUE (:PARAM1, :PARAM2)';
  MyParams.Create;
  MyParams.CreateParam(ftString, 'PARAM1', ptInput).Value := 'Seu valor1';
  MyParams.CreateParam(ftString, 'PARAM2', ptInput).Value := 'Seu valor2';

  SQLConnection1.Execute(MySQL,MyParams, Nil);
end;
票数 3
EN

Stack Overflow用户

发布于 2012-03-06 11:04:28

我有大约90%的把握,你不能,至少不能不解析GO之间的各个命令,然后串行执行每个命令,正如你已经指出的,这是有问题的。

(我很高兴在上面的问题上被证明是错误的,并且对看到解决方案很感兴趣……)

如果你只是简单地使用脚本作为初始化逻辑(例如创建表格等),你可以考虑的另一个解决方案是在一个批处理文件中启动脚本,并通过“Sqlcmd”执行它们,这可以通过你的delphi应用程序执行(使用ShellExecute),然后等待它完成后再继续。

不像使用组件那样优雅,但如果只是为了初始化逻辑,它可能是一个快速的、可接受的折衷方案。我当然不会考虑在初始化后进行任何处理。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9517652

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档