Migreer je Data met Delphi 2007 en DBX4

Migreer je Data met Delphi 2007 en DBX4

Van de BDE (of SQL Links) naar een SQL DBMS met Delphi 2007 en DBX4

Een groot deel van de code in dit artikel werkt met alle Win32 versies van Delphi sinds Delphi 6. Toch zal ik van de meest recente versie Delphi 2007 gebruik maken, en hier en daar melding maken van nieuwe features en mogelijkheden.

Zoals (hopelijk) bekend, is de Borland Database Engine (BDE) een bevroren data access technologie die vanaf Delphi 1 beschikbaar was, maar vanaf Delphi 7 het predicaat “frozen” heeft meegekregen, en waar geen onderhoud meer op gepleegd wordt. Voor de grote broer SQL Links (de BDE uitbreiding om met SQL Server databases te werken) geldt zelfs de toevoeging “deprecated”, en SQL Links wordt ook niet meer geleverd bij versies van Delphi 7 of hoger, waarbij moet worden opgemerkt dat SQL Links nog wel gebruikt kan worden door een Delphi met een versie hoger dan 7 als de BDE/SQL Links op de ontwikkelmachine aanwezig is … maar het gebruik is daarbij volledig “op eigen risico”.

Al vanaf Delphi 6 heeft Borland dbExpress aangemerkt als de data access architectuur die de voorkeur heeft boven de BDE en SQL Links

Al vanaf Delphi 6 (en Kylix) heeft Borland dbExpress aangemerkt als de data access architectuur die de voorkeur heeft boven de BDE en SQL Links. Tot nu toe was er nog niet echt haast geboden om van de BDE over stappen (het zit er immers nog altijd in), maar sinds vorig jaar is de laatste BDE engineer van Borland niet meer werkzaam bij CodeGear. En als straks voor Delphi 2008 de stap naar volledige UNICODE wordt gezet, is het nog maar de vraag of de BDE dit zal meemaken. Om niet voor verrassingen te komen staan zal ik in dit artikel aangeven hoe we BDE aliases kunnen analyseren, hoe we SQL kunnen genereren om vergelijkbare tabellen in SQL DBMSen te creëren, hoe we data uit BDE tabellen kunnen overpompen naar deze SQL DBMSen, en tot slot hoe we met DBX4 deze data uit de SQL DBMSen kunnen bewerken.

BDE Analyse

Laten we beginnen met een kleine GUI om alle mogelijke BDE alias strings op te halen die op een machine aanwezig zijn, en voor een gekozen alias alle tabellen op te sommen die daarbinnen te vinden zijn. Hier hebben we twee TComboBox componenten voor nodig, genaamd cbDatabases en cbTables. Het vullen van de cbDatabases met de aanwezige alias strings gaat met behulp van een TSession component als volgt, bijvoorbeeld in de FormCreate:

procedure TFormSDE.FormCreate(Sender: TObject);
begin
  Session1.GetDatabaseNames(cbDatabases.Items);
end;

Hierdoor zal de dbDatabases TCmboBox gevuld worden met de aanwezige BDE alias strings. Als we eenmaal een alias gekozen hebben, kunnen we aan een TDatabase component vragen welke tables er allemaal bekend zijn voor deze alias. Dat gaat met de GetTableNames functie, en doe ik bij voorkeur in de OnChange event van de cbDatabases combobox, zodat je daarmee meteen de cbTables combobox kunt vullen:

procedure TFormSDE.cbDatabasesChange(Sender: TObject);
begin
  Database1.Close;
  Database1.DatabaseName :=
    cbDatabases.Items[cbDatabases.ItemIndex];
  Database1.GetTableNames(cbTables.Items,
    cbSystemTables.Checked);
end;

Eenzelfde truc kunnen we uithalen in de OnChange event van de cbTables combobox, waarbij we de dan gekozen tablename kunnen gebruiken om deze tabel te analyseren.

procedure TFormSDE.cbTablesChange(Sender: TObject);
begin
  Table1.CLose;
  Table1.DatabaseName := Database1.DatabaseName;
  Table1.TableName :=
    cbTables.Items[cbTables.ItemIndex];
end;

Uitgaande van een BDE TTable waarvan de DatabaseName en TableName zijn opgegeven, kunnen we alle benodigde informatie ophalen uit de FieldDefs en IndexDefs collecties. Deze informatie wil ik gaan gebruiken om een SQL CREATE TABLE command te produceren dat de structuur van de tabel kan reproduceren in een SQL DBMS. Het voordeel is dat we de inhoud van dit SQL CREATE TABLE command kunnen opslaan in een script en (later) op een andere machine kunnen draaien.

Bij het fieldtype zullen we een voor een DBMS specifieke waarde moeten kiezen

Een probleem van deze benadering is dat de SQL CREATE TABLE voor ieder DBMS type een iets andere inhoud zal hebben, omdat de veld types niet altijd hetzelfde zullen zijn. Zo kent SQL Server bijvoorbeeld de veld types IMAGE en TEXT, terwijl Blackfish SQL die niet kent. En een floating point getal wordt door het ene DBMS als DOUBLE aangegeven, terwijl het andere een FLOAT wil zien. In dit artikel zal ik voor SQL Server en Blackfish SQL een aantal field mappings aangeven, maar laat het aan de lezer over om voor zijn specifeke DBMS de mapping compleet te maken (zie dan de documentatie van de DBMS voor de benodigde details).

SQL CREATE TABLE

De basis syntax van het SQL CREATE TABLE command is kort gezegd als volgt:

CREATE TABLE (
   [size] [[NOT] NULL] )

Hierbij kun je een of meerdere velden opgeven. De size is optioneel (en wordt voornamelijk bij CHAR of VARCHAR velden gebruikt), evenals de NULL of NOT NULL (default is NULL). Zoals eerder aangegeven zullen we bij het fieldtype een voor een DBMS specifieke waarde moeten kiezen, maar de rest DBMS is onafhankelijk.

Als we het fieldtype nog even buiten beschouwing laten, dan kan het DBMS onafhankelijke deel van de SQL CREATE TABLE command met een functie Table2SQL als volgt geproduceerd worden:

function Table2SQL(const Table: TTable): String;
var
  i: Integer;
  Str: String;
begin
  Table.Open;
  Table.FieldDefs.Update;
  Table.IndexDefs.Update;
  try
    Str := 'CREATE TABLE ' +
      ChangeFileExt(Table.TableName, '') + '(';
    for i:=0 to Table.Fields.Count-1 do
    begin
      Str := Str +
        '"' + Table.Fields[i].DisplayName +'" ';
      // fieldtype
      if Table.FieldDefs[i].Required then
        Str := Str + ' NOT';
      Str := Str + ' NULL';
      if (i < Table.Fields.Count-1) then
        Str := Str + ', ';
    end;
    Str := Str + ')';

    Result := Str;
  finally
    Table.Close;
  end
end;

Merk op dat de FieldDefs.Update en IndexDefs.Update meestal niet meer nodig zijn als de Table reeds geopend is (en zonder dat de Table geopend is levert FieldDefs.Update toch niet de gewenste informatie op).

Voor iedere DBMS die we willen ondersteunen bij de migratie, moeten we nu een eigen field mapping schrijven van het BDE/SQL Links type naar het specifieke DBMS type.

Field Mapping

Voor SQL Server en Blackfish SQL heb ik de volgende field mappings gevonden, waarbij ik aan de BDE/SQL Links kant uitga van de waarde van de Table.FieldDefs[i].FieldClass en aan de DBMS kant het corresponderende type heb gekozen:

BDE/SQL Links SQL Server Blackfish SQL
TStringField VARCHAR(size) VARCHAR(size)
TSmallIntField SMALLINT SMALLINT
TIntegerField INTEGER INTEGER
TDateTimeField DATETIME TIMESTAMP
TMemoField TEXT OBJECT
TGraphicField IMAGE OBJECT
TBlobField IMAGE OBJECT
TfloatField FLOAT DOUBLE

Deze lijst is niet compleet, maar is voldoende om van de DBDEMOS BDE alias alle tabellen om te zetten naar SQL Server of Blackfish SQL, en daar ging het me in eerste instantie om.

Het stukje ontbrekende code in de voorgaande listing kan dan ook als volgt worden aangevuld voor SQL Server (met de strings voor Blackfish SQL in commentaar daarnaast aangegeven):

if Table.FieldDefs[i].FieldClass = TStringField then
  Str := Str + ' VARCHAR(' +
    IntToStr(Table.FieldDefs[i].Size) + ')'
else
if Table.FieldDefs[i].FieldClass = TSmallIntField then
  Str := Str + ' SMALLINT '
else
if Table.FieldDefs[i].FieldClass = TIntegerField then
  Str := Str + ' INTEGER '
else
if Table.FieldDefs[i].FieldClass = TDAteTimeField then
  Str := Str + ' DATETIME ' // TIMESTAMP '
else
if Table.FieldDefs[i].FieldClass = TMemoField then
  Str := Str + ' TEXT ' // OBJECT '
else
if Table.FieldDefs[i].FieldClass = TGraphicField then
  Str := Str + ' IMAGE ' // OBJECT  '
else
if Table.FieldDefs[i].FieldClass = TBLobField then
  Str := Str + ' IMAGE ' // OBJECT '
else
if Table.FieldDefs[i].FieldClass = TFloatField then
  Str := Str + ' FLOAT ' // DOUBLE '
else
  raise Exception.Create('Unsupported field type ' +
    Table.FieldDefs[i].FieldClass.ClassName);

Let op de raise Exception.Create aan het eind die ons zal vertellen wanneer er een ander field type is gevonden, zodat we dan daarvoor ook op zoek moeten naar een bijbehorende SQL mapping.

Voor de Country tabel uit de DBDEMOS alias, is hiermee de volgende CREATE TABLE geproduceerd:

CREATE TABLE country("Name"  VARCHAR(24) NULL,
  "Capital"  VARCHAR(24) NULL,
  "Continent"  VARCHAR(24) NULL,
  "Area"  FLOAT  NULL,
  "Population"  FLOAT  NULL)

We moeten nu alleen nog de data zelf meenemen, wat de laatste stap is in dit migratieverhaal.

Copy Copy

En dan nu de laatste stap: het overpompen van de data uit de BDE/SQL Links tabel naar de SQL DBMS. Dat kan door gebruik te maken van een aantal DBX4 componenten die de BDE records eerst inlezen in een TClientDataSet, en daarna alles naar het SQL DBMS sturen.

Allereerst is daar de dbExpress SQLConnection component die een verbinding met het SQL DBMS moet maken: hiertoe moet je de juiste connection properties opgeven. Dan is er een SQLDataSet die via zijn SQLConnection property aan de SQLConnection component verbonden is, en een SELECT * FROM uitvoert op de betreffende (nog lege) tabel die we eerder met het CREATE TABLE command hebben aangemaakt. Een TDataSetProvider verbonden met de SQLDataSet en een TClientDataSet verbonden met de DataSetProvider maken het plaatje compleet.

En dan nu de laatste stap: het overpompen van de data uit de BDE/SQL Links tabel naar de SQL DBMS

Bij het kopiëren hoeven we nu alleen nog maar door de BDE/SQL Links TTable heen te lopen en per record een Append te doen in de ClientDataSet. Met Append komt ieder volgend record achter het vorige, zodat we dezelfde volgorde aanhouden als in de BDE table.

Vervolgens kunnen we van ieder veld de waarde kopiëren door de Fields[i].Value te gebruiken. Wat nog iets beter is (ook voor memo velden) is om Assign te gebruiken om de waarde van de BDE velden toe te kennen aan die van de DBX4 velden. Hierbij ga ik wel uit van het feit dat beide tabellen de velden in exact dezelfde volgorde hebben staan, zodat we niet via FieldByName hoeven te zoeken, maar kunnen volstaan met de mapping van veld i uit de BDE tabel naar veld i van de SQL DBMS tabel.

Omdat de SQLDataSet met een SELECT * FROM werd gestart, zal bij de aanroep van ApplyUpdates van de ClientDataSet automatisch een bijbehorende SQL INSERT worden gegenereerd.

procedure TFormSDE.btnCopyClick(Sender: TObject);
var
  i: Integer;
begin
  ClientDataSet1.Close;
  Table1.Open;
  SQlDataSet1.CommandText := 'SELECT * FROM ' +
    ChangeFileEXt(Table1.TableName,'');
  ClientDataSet1.Active := True;
  while not Table1.Eof do
  begin
    ClientDataSet1.Append;
    for i:=0 to Table1.Fields.Count-1 do
      ClientDataSet1.Fields[i].Assign(Table1.Fields[i]);
    ClientDataSet1.Post;
    ClientDataSet1.ApplyUpdates(0);
    Table1.Next;
  end;
  Table1.Close;
end;

In de code van de laatste listing wordt deze ApplyUpdates voor ieder record aangeroepen (direct na de Post), maar dit kun je ook na iedere X records doen om het proces een klein beetje te versnellen.

Op het eind van de code laat ik de ClientDataSet op Active staan. Dat doe ik met opzet, zodat een eventueel DBGrid op het conversie form dan meteen de inhoud kan laten zien, zoals in onderstaand figuur:

Conclusie

In dit artikel heb ik laten zien hoe we vanuit een BDE/SQL Links een SQL CREATE TABLE command kunnen produceren, en dit kunnen gebruiken om een nieuwe vergelijkbare tabel in een SQL DBMS te maken, gevolgd door het kopiëren van de data uit de BDE tabel naar de corresponderende tabel in het SQL DBMS. Hiermee kan op eenvoudige wijze BDE data naar een SQL DBMS gemigreerd worden.

Commentaar van anderen:
ChristianLouboutin op 17-8-2010 om 3:45
Christian Louboutin Shoes, Christian Louboutin, Christian Louboutin Shoes, Wedding Shoes, Wedding Shoes, Louboutin Shoes, Christian Louboutin Discount copies of the mirror, so the angel of the practical activities of customers who activities. Christian Louboutin Evening, Manolo Blahnik Shoes, Christian, Louboutin, Christian Louboutin Sale, Louboutin Sale, Cheap Christian Louboutin Can you from your shoes, you give yourself into chargeless travel and bear your lifetime. Christian Louboutin Boots, Christian Louboutin Pumps, Christian Louboutin Sandals, Christian Louboutin Flats, Christian Louboutin Wedges, Christian Louboutin Sandals Do you agree to accept and hear the cases angled heel shoes forward or abstract, tear, buttons, and destruction, and the ability to reduce greenhouse gas emissions, it is exactly like daydreaming and shoes. Yves Saint Laurent Shoes, Christian Louboutin Boots, Manolo Blahnik Shoes, Yves Saint Laurent Boots, Miu Miu Shoes, Christian Dior Shoes this is an extra brand and shoes. However, you will never accept the amorous Louboutin christians through brand. Christian Louboutin Flats, Christian, Herve Leger V Neck Dress, Herve Leger Bandage Dress, Herve Leger Dress, Herve Leger V Neck Dress You can visit in your configuration of the life of their shoes were forgiven a brace, air brief you accept defeat them later anniversary, they are careful bag dust, shoe box that you have to go.
asdasd op 19-8-2010 om 16:01
I had put the cooler pad and Liz’s Silicon Flash Driver on the floor to use many times and the fans stopped turning one day because of my hair. A few strands of hair which were on the floor got caught in the fans. I tried to use a toothpick at first but it was difficult and I finally had to open up the cooler pad to remove the hair.
wholesale nike shoes op 25-8-2010 om 9:41
ehotsale replica nfl jerseys nike blazer shoes womens shoes
ZHUZHU op 30-8-2010 om 3:57
over and over again wholesale cartier brOoch jewelry over and over again cartier jewelry over and over again wholesale fashion jewelRy over and over again pandora Rings jewelry over and over again christIan dior jewelry
GHDStraighteners op 30-8-2010 om 9:59
Do you know what is GHD IV Styler and birkenstock sandals ? It’s a kind of GHD Straighteners that can make the females’hair become more beautiful and create a lot of styles by theirselves.Gucci outlet is famous to run the business. At the same time GHD Iv Styler teaches you how to straighten ,curl and style your hair the way your hairdresser does.So it can help females to solve the hair problem and make you become more nobleness and confident.We can provide you with gucci bags and birkenstock sandals at the same time.Come on ,take the advance of GHD IV Styler to be a beautiful person .
Geef feedback:

CAPTCHA image
Vul de bovenstaande code hieronder in
Verzend Commentaar