Я не использую DBExpress, но, насколько мне известно, вы можете выполнять (либо через Execute, либо через ExecuteDirect) только одну команду SQL за раз.Другими словами, вы не можете поместить весь скрипт в метод Execute.
Это не связано с разным синтаксисом команд, используемым FireBird и MS SQL (^ против GO).Вы должны понимать, что знак «^» или команда «GO» не является «командой TSQL»!Оба являются определенными разделителями команд, используемыми соответствующим приложением, используемым для выполнения команд для механизмов SQL.Это разница между «Firebird Manager» (или как он называется) и «SQL Query Profiler» (или «SQL Server Management Studio»).
Решение состоит в том, чтобы использовать какой-то синтаксический анализатор, разбить скриптв список отдельных команд и TSQLConnection. Выполните эти команды одну за другой.
Примерно такой псевдокод:
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).Я не знаю никого, на кого можно было бы указать.
Sidenote 2: Я почти уверен, что вам придется изменить свои сценарии, чтобы они были полностью совместимы с MS SQL.Несмотря на то, что Firebird и MS SQL оба являются серверами SQL, всегда существует разница в синтаксисе DML / DDL.
Редактировать:
Если вы можете "переписать"SQL-скрипт в коде, вы можете использовать компонент Jvidi VCL jvStringHolder.Поместите каждую отдельную команду как один элемент (типа TStrings) в jvStringHolder .
Создание синтаксического анализатора довольно сложно, но его нельзя отменить.По вдохновению SynEdit я сделал эти предложения именно для того, что вам нужно: загрузить скрипт с TSQLScript.ParseScript, а затем выполнить итерацию через свойство Command [index: integer].SQLLexer не является полноценным SQL Lexer, но реализует разделение ключевых слов с учетом комментариев, скобок, свертывания кода и т. Д. Я также добавил специальный синтаксис в комментарии ($ знак в блоке комментариев), который помогает мне добавлять заголовки в сценарий.Это полная копия-вставка из одного из моих проектов.Я не даю больше объяснений, но я надеюсь, что вы поймете эту идею и запустите ее в своем проекте.
unit 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;