A common problem I have had is to migrate data contained in an access database to other databases or formats. I have used the tclodbc extension [http://www.solagem.fi/~rnurmi/tclodbc.html] to generate an XML file descibing the Access data and this file can then processed with XSLT to recreate the database on another platform. The following tcl script demonstrates how the XML file is generated: #################### # # Packages # #################### package require tclodbc ## # # Procedures # ## # # Certain characters must be encoded in XML # proc Encode {str} { # # Standard encodings # set enc(&) {&} set enc(<) {<} set enc(>) {>} set enc(\") {"} # # HACK ALERT # # Single quotes cause problems in SQL INSERT statements # set enc(') {''} return [string map [array get enc] $str] } # # Take an Access file and dump its contents to an XML file # proc XMLfromAccess {dbFile xmlFile} { # # Assume that we are converting Access files # set driver "Microsoft Access Driver (*.mdb)" # # Connect to db # database db "DRIVER=$driver;DBQ=$dbFile" # # Start XML file # lappend xml "" lappend xml "" # # Obtain the Schema details # lappend xml "\t" foreach tableDef [db tables] { set table(TABLE_QUALIFIER) [lindex $tableDef 0] set table(TABLE_OWNER) [lindex $tableDef 1] set table(TABLE_NAME) [lindex $tableDef 2] set table(TABLE_TYPE) [lindex $tableDef 3] set table(REMARKS) [lindex $tableDef 4] # # Only process normal tables # if {[string compare $table(TABLE_TYPE) TABLE]} { continue } # # TABLE tags start here # lappend xml "\t\t" foreach key [array names table] { lappend xml "\t\t\t<$key>$table($key)" } foreach columnDef [db columns $table(TABLE_NAME)] { set column(TABLE_QUALIFIER) [lindex $columnDef 0] set column(TABLE_OWNER) [lindex $columnDef 1] set column(TABLE_NAME) [lindex $columnDef 2] set column(COLUMN_NAME) [lindex $columnDef 3] set column(DATA_TYPE) [lindex $columnDef 4] set column(TYPE_NAME) [lindex $columnDef 5] set column(PRECISION) [lindex $columnDef 6] set column(LENGTH) [lindex $columnDef 7] set column(SCALE) [lindex $columnDef 8] set column(RADIX) [lindex $columnDef 9] set column(NULLABLE) [lindex $columnDef 10] set column(REMARKS) [lindex $columnDef 11] # # COLUMN tags start here # lappend xml "\t\t\t" foreach key [array names column] { lappend xml "\t\t\t\t<$key>$column($key)" } lappend xml "\t\t\t" } unset column foreach indexDef [db indexes $table(TABLE_NAME)] { set index(TABLE_QUALIFIER) [lindex $indexDef 0] set index(TABLE_OWNER) [lindex $indexDef 1] set index(TABLE_NAME) [lindex $indexDef 2] set index(NON_UNIQUE) [lindex $indexDef 3] set index(INDEX_QUALIFIER) [lindex $indexDef 4] set index(INDEX_NAME) [lindex $indexDef 5] set index(TYPE) [lindex $indexDef 6] set index(SEQ_IN_INDEX) [lindex $indexDef 7] set index(COLUMN_NAME) [lindex $indexDef 8] set index(COLLATION) [lindex $indexDef 9] set index(CARDINALITY) [lindex $indexDef 10] set index(PAGES) [lindex $indexDef 11] set index(FILTER_CONDITION) [lindex $indexDef 12] # # INDEX tags # lappend xml "\t\t\t" foreach key [array names index] { lappend xml "\t\t\t\t<$key>$index($key)" } lappend xml "\t\t\t" } unset index lappend xml "\t\t
" } unset table lappend xml "\t
" # # Obtain the Table data # lappend xml "\t" foreach tableDef [db tables] { set table(TABLE_QUALIFIER) [lindex $tableDef 0] set table(TABLE_OWNER) [lindex $tableDef 1] set table(TABLE_NAME) [lindex $tableDef 2] set table(TABLE_TYPE) [lindex $tableDef 3] set table(REMARKS) [lindex $tableDef 4] # # Only process normal tables # if {[string compare $table(TABLE_TYPE) TABLE]} { continue } # # Execute SELECT # db statement data "SELECT * FROM $table(TABLE_NAME)" data execute # # Save column specification # foreach colSpec [db columns $table(TABLE_NAME)] { lappend typeList [lindex $colSpec 3] lappend typeList [lindex $colSpec 5] } array set type $typeList # # Fetch each row # lappend xml "\t\t" while {[data fetch row]} { lappend xml "\t\t\t" foreach key [array names row] { lappend xml "\t\t\t\t[Encode $row($key)]" } lappend xml "\t\t\t" } unset row lappend xml "\t\t
" } lappend xml "\t
" lappend xml "
" # # Write the XML file # set fp [open $xmlFile w] puts $fp [join $xml "\n"] close $fp # # Cleanup # db disconnect } ## # # Main Program # ## foreach file [glob *.mdb] { XMLfromAccess $file [file rootname $file].xml } The following example XSLT stylesheet shows how the tables and indexes can be created and the data loaded. CREATE TABLE (); INTEGER , CREATE UNIQUE INDEX ON (); INSERT INTO () VALUES (); , '' '' ,