Задача, может быть, и тривиальная: существует система из приложения-сервера данных на MS SQL, к которому подключаются клиентские приложения для обновления информации, все остальное время работающие в оффлайне. Фактически, схема МИДАСа, только своего. Ранее система работала так: клиентские программы используют таблицы Парадокс, при подключении к серверу данных он, при необходимости отдать всю таблицу, а не пакет обновлений, соответственно, создавал для клиента целиковую таблицу Парадокс, экспортируя туда записи из MS SQL обычными insert.
Теперь была поставлена задача от Парадокса избавиться и на клиентах и на сервере. В качестве оффлайновой базы клиента остановились на FireBird 1.5. Встал, соответственно, вопрос, в какой формат сервер данных должен экспортировать таблицы, чтобы они могли быстрее всего создаваться на сервере и быстрее всего перебрасываться клиентом в базу FB. По тестам (из "готовенького") на сервере быстрее всего создаются XML при помощи TClientDataSet (через LocalConnection открывается источник MS SQL и вызывается родной метод SaveToFile). На клиенте, я, не долго думая, реализовал такой код:
Код: Выделить всё
function TATIDB.DoCreateTable(Q: TClientDataSet; aTblName: shortstring; ATime: Integer): boolean;
var
i,j : integer;
s : string;
x : byte;
t : string;
Ot, Os : string;
IDT : smallint;
sShowName : shortstring;
function ReseparateField(ss : string) : string;
var
st,sv : string;
begin
Result:='';
st:=ss;
while st<>'' do begin
if Pos(';',st)<>0 then begin
sv:=Copy(st,1,Pos(';',st)-1);
System.Delete(st,1,Pos(';',st))
end
else begin
sv:=St;
st:=''
end;
if Result<>'' then
Result:=Result+', ';
Result:=Result+ANSILowerCase(QuotedStr(sv))
end
end;
begin
Result:=true;
try
Q.First;
s:='';
//... инициализируются внутренние переменные, места много занимает // и вопроса не касается, поэтому опустим
{$Region 'creating table'}
{$Region 'Fill fields'}
for i:=0 to Q.FieldCount-1 do begin
if s<>'' then
s:=s+', ';
s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"';
case Q.Fields[i].DataType of
ftFixedChar,
ftWideString,
ftMemo,
ftFmtMemo,
ftString : s:=s+' VARCHAR('+IntToStr(Q.Fields[i].Size)+') CHARACTER SET WIN1251';
ftBlob,
ftGraphic : s:=s+' BLOB';
ftSmallint,
ftLargeint,
ftAutoInc,
ftInteger,
ftWord : s:=s+' INTEGER';
ftBoolean : s:=s+' SMALLINT';
ftCurrency,
ftBCD,
ftFloat : s:=s+' FLOAT';
ftDate,
ftTime,
ftDateTime : s:=s+' TIMESTAMP';
else
Continue;
end;
if Q.Fields[i].Required then
s:=s+' NOT NULL';
if i=0 then begin
if NOT Q.Fields[i].Required then
s:=s+' NOT NULL'
end
else if i<X then begin
if NOT Q.Fields[i].Required then
s:=s+' NOT NULL';
end;
case Q.Fields[i].DataType of
ftFixedChar,
ftWideString,
ftMemo,
ftFmtMemo,
ftString : s:=s+' COLLATE PXW_CYRL'
end;
end;
{$EndRegion}
s:='CREATE TABLE '+ANSILowerCase(aTblName)+' ('+s+')';
with TIbQuery.Create(nil) do try
DataBase:=FDB;
SQL.Clear;
SQL.Add(s);
ExecSQL;
FIT.CommitRetaining;
{$Region 'Indexing'}
SQL.Clear;
s:='';
for i:=0 to x-1 do begin
if s<>'' then
s:=s+', ';
s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
end;
s:='ALTER TABLE '+ANSILowerCase(aTblName)+' ADD CONSTRAINT PK_'+ANSILowerCase(aTblName)
+' PRIMARY KEY ('+s+')';
SQL.Add(s);
ExecSQL;
FIT.CommitRetaining;
s:='';
if Q.IndexFieldNames<>'' then
s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexFieldNames)+')'
else for i:=0 to Q.IndexDefs.Count-1 do begin
if Q.IndexDefs[i].DescFields<>'' then
s:='CREATE DESC INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexDefs[i].DescFields)+')'
else
s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+ReSeparateField(Q.IndexDefs[i].Fields)+')'
end;
if s='' then begin
for i:=x-1 downto 0 do begin
if s<>'' then
s:=s+', ';
s:=s+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
end;
s:='CREATE INDEX '+aTblName+'_IDX1 ON '+aTblName+' ('+s+')'
end;
if s<>'' then begin
SQL.Clear;
SQL.Add(s);
ExecSQL;
FIT.CommitRetaining;
SQL.Clear
end;
{$EndRegion}
{$EndRegion}
{$Region 'writing data'}
SQL.Clear;
t:='';
j:=0;
Ot:='';
Os:='';
//вот, собственно, начинается главный код импорта:
while NOT Q.Eof do begin
s:='';
t:='';
inc(j);
if ot='' then begin
for i:=0 to Q.FieldCount-1 do begin
if t<>'' then
t:=t+', ';
t:=t+'"'+ANSILowerCase(Q.Fields[i].FieldName)+'"'
end;
Ot:=t
end
else
t:=Ot;
t:='INSERT INTO '+ANSILowerCase(aTblName)+' ('+t+')';
s:='';
if Os='' then begin
for i:=0 to Q.FieldCount-1 do begin
if s<>'' then
s:=s+', ';
s:=s+':'+Q.Fields[i].FieldName
end;
Os:=s
end
else
s:=Os;
t:=t+' VALUES ('+s+')';
SQL.Clear;
SQL.Add(t);
for i:=0 to Q.FieldCount-1 do begin
if ParamByName(Q.Fields[i].FieldName)<>nil then begin
if NOT Q.Fields[i].IsNull then begin
if Q.Fields[i].DataType=ftBoolean then begin
if Q.Fields[i].AsBoolean then
ParamByName(Q.Fields[i].FieldName).AsString:='1'
else
ParamByName(Q.Fields[i].FieldName).AsString:='0'
end
else
ParamByName(Q.Fields[i].FieldName).AsString:=Q.Fields[i].AsString
end
else
ParamByName(Q.Fields[i].FieldName).Value:=Null
end
end;
ExecSQL;
Q.Next
end;
SQL.Clear;
FIT.CommitRetaining
finally
Free
end
{$EndRegion}
except
on E:Exception do begin
raise Exception.Create(E.Message)
end
end
end;