我正在将一个旧的Delphi应用程序(使用ZeosDB)迁移到Delphi XE2。我想用dbExpress作为对Firebird 2.5或MS-SQL数据库访问的ZeosDB替代。我需要运行很多用于创建表、视图和存储过程的sql脚本。Firebird脚本命令与^、MS-SQL脚本命令用"GO“分隔。
如何使用dbexpress连接在数据库上运行这些脚本?ZeosDB提供了一个TZSqlProcessor,但我找不到任何与dbExpress等效的组件。
发布于 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这些命令。
类似于下面的伪代码:
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语法上总是存在差异。
编辑:
单元SQLParser;
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.解析器:
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;发布于 2012-03-01 23:10:46
您需要使用TSQLConnection。这是一个组件,有两个方法,Execute和ExecuteDirect。第一个方法不接受参数,但第二个方法接受。
使用第一种方法:
procedure TForm1.Button1Click(Sender: TObject);
var
MeuSQL: String;
begin
MeuSQL := 'INSERT INTO YOUR_TABLE ('FIELD1', 'FIELD2') VALUES ('VALUE1', 'VALUE2')';
SQLConnection.ExecuteDirect(MeuSQL);
end;如果您愿意,您可以使用事务。
使用第二种方法:
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;发布于 2012-03-06 11:04:28
我有大约90%的把握,你不能,至少不能不解析GO之间的各个命令,然后串行执行每个命令,正如你已经指出的,这是有问题的。
(我很高兴在上面的问题上被证明是错误的,并且对看到解决方案很感兴趣……)
如果你只是简单地使用脚本作为初始化逻辑(例如创建表格等),你可以考虑的另一个解决方案是在一个批处理文件中启动脚本,并通过“Sqlcmd”执行它们,这可以通过你的delphi应用程序执行(使用ShellExecute),然后等待它完成后再继续。
不像使用组件那样优雅,但如果只是为了初始化逻辑,它可能是一个快速的、可接受的折衷方案。我当然不会考虑在初始化后进行任何处理。
https://stackoverflow.com/questions/9517652
复制相似问题