Jak jednoduše a rychle převést DBF tabulku do MDB databáze.

Převod dbf do MDB databáze

Nyní si předvedeme, jak jednoduše převést DBF tabulku do MDB databáze. Využity jsou i dvě API funkce ODBC pro zjištění několika informací pro založení MDB databáze. Na MDB databázi se vždy připojuje přes název ODBC driveru.

Vstupními parametry jsou: alias DBF souboru - lcAlias, umístění + název cílového MDB souboru - lcMDB a umístění + název exstujícího MDB souboru - lcVMDB. Třetí parametr není potřeba, pokud MDB soubor existuje a nebude se znovu vytvářet.
Celý přenos řídí čtyři proměnné:

LPARAM lcAlias,lcMDB,lcVMDB
* lcAlias - Alias otevřené DBF tabulky
* lcMDB   - Cesta a název MDB souboru kam se vytvoří tabulka
* lcVMDB  - Cesta a název MDB vzorového souboru (může být jakýkoliv, ale musí existovat).
*         - Slouží pro otestování verze ODBC driveru MS Access

* Návratové konstanty
#DEFINE __DBF2MDB_ErrOK                0 && 
#DEFINE __DBF2MDB_ErrConnectionFailed -1 && Nepodařilo se otevřír spojení na MDB soubor
#DEFINE __DBF2MDB_ErrMDBNotCreated    -2 && Nepodařilo se  vytvořit MDB soubor
#DEFINE __DBF2MDB_ErrFewParameters    -3 && Málo parametrů
#DEFINE __DBF2MDB_ErrNoTables         -4 && Nepodařilo se získat seznam tabulek
#DEFINE __DBF2MDB_ErrCannotCTable     -5 && Nepodařilo se vytvořit tabulku
#DEFINE __DBF2MDB_ErrCannotDTable     -6 && Nepodařilo se smazat tabulku
#DEFINE __DBF2MDB_ErrNotaTable        -7 && Tabulka je pohled nebo systémová tabulka
#DEFINE __DBF2MDB_ErrNotInsert        -8 && Nepodařilo se založit větu
#DEFINE __DBF2MDB_ErrNotDelete        -9 && Nepodařilo se smazat věty
#DEFINE __DBF2MDB_ErrAliasNotOpen    -10 && Alias není otevřený

* Konstanty pro ODBC
#DEFINE SQL_DRIVER_VER                       7


IF FILE(lcMDB) AND PCOUNT()<2 OR !FILE(lcMDB) AND PCOUNT()<3 AND !FILE(lcVMDB)
   RETURN __DBF2MDB_ErrFewParameters
ENDIF

IF !USED(lcAlias)
   RETURN __DBF2MDB_ErrAliasNotOpen
ENDIF

* Potřebné ODBC API fukce
DECLARE Integer SQLConfigDataSource in odbccp32.dll Integer, Integer,String, String
DECLARE INTEGER SQLGetInfo IN odbc32.dll INTEGER,INTEGER, STRING @,INTEGER,INTEGER @

LOCAL lcSQL,lihdbc,liErr,liODBChdbc,lcPom,lii,lcMDBVersion,lcCRKey,lcTable,;
      lcATables,llExists,lcSQLI,;
      llReCreate,llReFill,liFormat,llMDBReCreate

LOCAL ARRAY laFields(1)

llMDBReCreate=.F.  && Má se znovu vytvořit MDB soubor?
llReCreate=.T.     && Má se tabulka znovu vytvořit, pokud existuje?
llReFill=.F.       && Má se tabulka znova naplnit?
liFormat=1         && Formát/verze MDB souboru (1 - MS Access 2.0, 2 - MS Access 95/97, 3 - MS Access 2000)

Pokud není databázový soubor založen, či se má znovu vytvořit, musí se nejdříve zjistit, jaká verze ODBC driveru pro MS Acces databázi je nainstalován. Novější verze používá jiná klíčová slova pro vytvoření MDB souboru (viz. MSDN - Q126606). Aby se dala zjistit verze ODBC ovladače, je nutno otevřít spojení na existující MS Access databázi (jedno jakou, třeba i prázdnou). Pak se pomocí ODBC funkce SQLGetInfo() zjistí verze ODBC driveru a dle ní a proměnné liFormat se vygeneruje klíčové slovo pro určení verze nově vytvořené MS Access databáze. Ta se vytvoří pomocí ODBC funkce SQLConfigDataSource().

IF !FILE(lcMDB) OR llMDBReCreate
   * Zjištění verze ODBC driveru pro MS Access
   * protože klíčová slova pro vytvoření MDB souboru jsou pro různé verze jiná
   lihdbc=SQLSTRINGCONNECT("driver={Microsoft Access driver (*.mdb)};dbq="+lcVMDB)
   IF lihdbc=0
      RETURN __DBF2MDB_ErrConnectionFailed
   ENDIF
   liODBChdbc=SQLGETPROP(lihdbc,"ODBChdbc") && Zjisti ODBC hdbc
   lcVersion=SPACE(254) && Nastav buffer
   lii=0
   =SQLGetInfo(liODBChdbc,SQL_DRIVER_VER,@lcVersion,LEN(lcVersion),@lii) && Načti verzi driveru
   =SQLDISCONNECT(lihdbc)

   lcVersion=LTRIM(STR(VAL(LEFT(lcVersion,AT(".",lcVersion)-1)),3))
   lcCRKey = IIF(lcVersion="3",IIF(liFormat=1,"CREATE_V2DB","CREATE_DB"),;
             IIF(liFormat=1,"CREATE_DBV2",IIF(liFormat=2,"CREATE_DBV3","CREATE_DBV4")))

   IF FILE(lcMDB) && Pokud existuje
      DELE FILE (lcMDB) && Smaž jej
   ENDIF

   * Vytvoření databáze
   IF SQLConfigDataSource(0,1,"Microsoft Access Driver (*.mdb)",lcCRKey+"="+lcMDB)#1
      * Selhalo vytvoření MDB souboru
      RETURN __DBF2MDB_ErrMDBNotCreated
   ENDIF
ENDIF

Když je MS Access databáze vytvořena, pak se na ni připojíme. Nastavíme synchronní přenos, automatické transakce a aby se nezobrazoval přihlašovací dialog. Pokud by byl vlastní přenos pomalý, pak lze nastavit ruční transakce, ale po skončení smyčky je pak nutno zavolat funkce SQLCOMMIT() nebo SQLROLLBACK() dle toho, zda se podařilo všechny věty přenést. Takovýto způsob (ruční transakce) je sice rychlý, ale zase zajišťuje buď všechna data nebo nic, bohužel, při potvrzení nebo vrácení změn může nastat delší časová odmlka.
Název tabulky je odvozen přímo z názvu DBF souboru. Kontroluje se, zda vytvářená tabulka již neexistuje jako pohled či systémová tabulka. Pokud ano, pak se přenos přeruší. Při definici SQL příkazu pro vytvoření tabulky se krom názvu a typu položky též přebírá i příznak .NULL. hodnoty.
SQL příkaz pro založení věty se generuje tak, aby byl vlastní přenos nejrychlejší a umožňoval přenášet velké memo položky a General pole - INSERT INTO TABLE myTable (pol1,pol2) VALUES (?Alias.pol1,?Alias.pol2) - používají se parametry.
Pokud se má tabulka znovu vytvořit, tak se nejdříve pomocí SQL příkazu DROP TABLE ... smaže a pak se založí.

* Připojení na MDB soubor
* Vytvoření a naplnění tabulky
lihdbc=SQLSTRINGCONNECT("Driver={Microsoft Access Driver (*.mdb)};DBQ="+lcMDB)
IF lihdbc<=0
   RETURN __DBF2MDB_ErrConnectionFailed
ENDIF
=SQLSETPROP(lihdbc,"Asynchronous",.F.) && Nastav Asynchronní provoz
=SQLSETPROP(lihdbc,"Transactions",1)   && Nastav Automatické transakce
=SQLSETPROP(lihdbc,"DispLogin",3)      && Nikdy nezobrazuj dialog při přihlášení

lcPom=DBF(lcAlias)
lii=RAT("\",lcPom)
lcTable=IIF(lii>0,SUBS(lcPom,lii+1),lcPom)
lii=RAT(".",lcTable)
lcTable=IIF(lii>0,LEFT(lcTable,lii-1),lcTable)

llDal=.T. && Nastav příznak, že je vše OK

* Zjisti, zda tabulka již existuje
lcATables=SYS(2015) && Pracovní kurzor
IF SQLTABLES(lihdbc,"'TABLE','VIEW','SYSTEM TABLE'",lcATables)<=0
   liErr=__DBF2MDB_ErrNoTables
   llDal=.F.
ENDIF

IF llDal
   STORE "" TO lcSQL,lcPom,lcSQLI
   * Načti seznam položek
   FOR lii=1 TO AFIELDS(laFields,lcAlias)
       lcSQL=lcSQL+" "+laFields(lii,1)+" "+;
             IIF(laFields(lii,2)='C',"TEXT ("+LTRIM(STR(laFields(lii,3),11))+")",;
             IIF(laFields(lii,2)='M',"LONGTEXT",;
             IIF(laFields(lii,2)='Y',"CURRENCY",;
             IIF(laFields(lii,2)='F',"FLOAT",;
             IIF(laFields(lii,2)='B',"DOUBLE",;
             IIF(laFields(lii,2)='I',"INTEGER",;
             IIF(laFields(lii,2)='N',"SINGLE" ,;
             IIF(laFields(lii,2)$'D,T',"DATETIME",;
             IIF(laFields(lii,2)='G',"LONGBINARY","BIT")))))))))+" "+IIF(laFields(lii,5),"NULL,","NOT NULL,")

       lcSQLI=lcSQLI+laFields(lii,1)+", "
       lcPom=lcPom+"?"+lcAlias+"."+laFields(lii,1)+", "
   NEXT
   lcSQL="CREATE TABLE "+lcTable+" ("+LEFT(lcSQL,LEN(lcSQL)-1)+")"
   lcSQLI="INSERT INTO "+lcTable+" ("+LEFT(lcSQLI,LEN(lcSQLI)-2)+") VALUES ("+LEFT(lcPom,LEN(lcPom)-2)+")"

   SELE (lcATables)
   LOCATE FOR UPPER(ALLT(TABLE_NAME))==UPPER(lcTable)
   llExists=FOUND()

   IF llExists AND llReCreate AND UPPER(ALLT(TABLE_TYPE))=='VIEW' OR UPPER(ALLT(TABLE_TYPE))=='SYSTEM TABLE'
      llDal=.F.
      liErr=__DBF2MDB_ErrNotaTable
   ENDIF
ENDIF

IF llDal AND (!llExists OR llReCreate)
   IF llExists AND SQLEXEC(lihdbc,"DROP TABLE "+lcTable)<=0
      llDal=.F.
      liErr=__DBF2MDB_ErrCannotDTable
   ENDIF

   IF llDal AND SQLEXEC(lihdbc,lcSQL)<=0
      llDal=.F.
      liErr=__DBF2MDB_ErrCannotCTable
   ENDIF
ENDIF

IF USED(lcATables) && Pokud je pracovní kurzor otevřen
   USE IN (lcATables) && Pak jej uzavři
ENDIF

Pokud se mají původní data smazat, pak se na to použije SQL příkaz DELETE FROM myTable. Vlastní přenos je také urychlen tím, že se SQL příkaz nejdříve předpřipraví pomocí funkce SQLPREPARE() (ODBC driver si SQL příkaz předkompiluje a při každém zavolání fukce SQLEXEC() převezme parametry).

* Smaž věty, pokud tabulka existuje a nebyla znovu vytvořena a má se naplnit
IF llDal AND llExists AND !llReCreate AND llReFill AND SQLEXEC(lihdbc,"DELETE FROM "+lcTable)<=0
   liErr=__DBF2MDB_ErrNotDelete
   llDal=.F.
ENDIF

IF llDal
   liErr=__DBF2MDB_ErrOK
   SELE (lcAlias)
   =SQLPREPARE(lihdbc,lcSQLI)
   SCAN ALL
       IF SQLEXEC(lihdbc,lcSQLI)<=0
          liErr=__DBF2MDB_ErrNotInsert
          EXIT
       ENDIF
   ENDSCAN
   * Pokud jsou nastaveny ruční transakce, pak buď potvrď změny, nebo je zruš
   =IIF(SQLGETPROP(lihdbc,"Transactions")=2,IIF(liErr=__DBF2MDB_ErrOK,SQLCOMMIT(lihdbc),SQLROLLBACK(lihdbc)),.T.)
ENDIF
=SQLDISCONNECT(lihdbc) && Uzavři spojení
RETURN liErr