|
Description:
A function to create and load a table in SQLite from a Microsoft Jet table, using DAO. Also recreates indexes. Fetches and loads records in blocks with a default size of 1000 rows.
Source: Text Source
"""
jetout.py - Reconstructs a given table from a Jet (MS Access) file in SQLite,
storing created table on disk; uses sqlite3, and uses win32com to get to
DAO 3.6 (Office 2000). To ensure index names are unique to the entire database,
names of recreated indexes are prefixed with the table name.
Date/Time typed columns are reformatted to ISO's yyyy-mm-dd, and may be recalled
as datetime.date types using the built-in DATE converter in pysqlite's
converters dictionary, if the database is opened with the argument
'detect_types=sqlite3.PARSE_DECLTYPES' in the 'connect' statement.
To modify this module to import Date/Time type as datetime.datetime instead
of datetime.date: change 'DATE' to 'TIMESTAMP' for CREATE TABLE, and modify
last line of separatedate function to include 'timepart'.--K.Killebrew 5/1/2008
Usage:
sqlitefromjet("input.mdb", "input table name", "output.db"
[, # of rows to fetch/commit at a time])
"""
def sqlitefromjet(mdb, tbl, sqlite_db, fetchsize=1000):
import win32com
from win32com import client
import sqlite3
engine = win32com.client.Dispatch("DAO.DBEngine.36")
db = engine.OpenDatabase(mdb)
table = db.OpenRecordset(tbl)
connection = sqlite3.connect(sqlite_db)
cursor = connection.cursor()
if tbl.find(' ') != -1 and tbl[:1] != '[':
tbl = '[' + tbl + ']'
createStr = 'CREATE TABLE ' + tbl + ' ('
fieldrange = range(table.Fields.Count)
isType = []
pkf = False
havePK = False
for tdef in db.TableDefs:
if tdef.Name == table.Name:
tblDef = tdef
break
for idx in tblDef.Indexes:
if idx.Primary:
for idxf in idx.Fields:
if pkf:
pkf = False
break
pkf = idxf.Name
break
for field in fieldrange:
createStr = createStr + '[' + table.Fields(field).Name + ']'
ftype = table.Fields(field).Type
if ftype == 4:
if pkf:
if table.Fields(field).Name == pkf:
createStr += ' INTEGER PRIMARY KEY, '
havePK = True
else:
createStr += ' INTEGER, '
isType.append(1)
elif ftype in (2,3):
isType.append(1)
createStr += ' INTEGER, '
elif ftype in (5,6,7,20):
isType.append(1)
createStr += ' NUMERIC, '
elif ftype == 8:
isType.append(2)
createStr += ' DATE, '
else:
isType.append(0)
createStr += ' TEXT, '
if table.Fields(field).Required:
createStr = createStr[:-2] + ' NOT NULL, '
createStr = createStr[:-2] + ');'
cursor.execute(createStr)
stmt = "INSERT INTO " + tbl + " VALUES("
for fieldnum in fieldrange:
stmt += "?, "
stmt = stmt[:-2] + ")"
df = [x for x in fieldrange if isType[x] == 2]
lastSet = False
while not lastSet:
fetched = table.GetRows(fetchsize)
fetchnum = len(fetched[0])
print "\tFetched " + "%s" % fetchnum + " rows"
if fetchnum < fetchsize:
lastSet = True
fetched = map(list, fetched)
for x in df:
fetched[x] = map(sepdate, fetched[x])
rows = zip(*fetched)
cursor.executemany(stmt, rows)
connection.commit()
print "INSERTs complete - creating indexes..."
for idx in tblDef.Indexes:
newName = table.Name + idx.Name
if idx.Unique:
if havePK and idx.Primary:
continue
else:
createStr = "CREATE UNIQUE INDEX [" + \
newName + "] ON " + tbl + "("
else:
createStr = "CREATE INDEX [" + newName + "] ON " + \
tbl + "("
for idxf in idx.Fields:
createStr += "[" + idxf.Name + "], "
createStr = createStr[:-2] + ");"
cursor.execute(createStr)
print "ok - all done"
cursor.close()
connection.close()
table.Close()
db.Close()
def sepdate(dt):
"""sepdate('%m/%d/%y %H:%M:%S') -> '%Y-%m-%d'
Return ISO-format yyyy-mm-dd date from Access-formatted Date/Time."""
dt = "%s" % dt
if dt.find(" ") != -1:
datepart, timepart = dt.split(" ")
else:
return None
month, day, year = datepart.split("/")
if int(year) <= 29:
year = '20' + year
elif int(year) <= 99:
year = '19' + year
return year + "-" + month + "-" + day
Discussion:
To directly load a table from MS Access into SQLite, this might be handy. This solution was chosen as an alternative to exporting and loading CSV files or creating and running SQL scripts.
To get to the TableDef object and its indexing information, this code loops through the TableDefs collection to find the right TableDef. How else might a TableDef be accessed?
To modify this module to import Date/Time type as datetime.datetime instead
of datetime.date: change 'DATE' to 'TIMESTAMP' for CREATE TABLE, and modify
last line of separatedate function to include 'timepart'.
For information regarding pysqlite type conversion (useful for reading out date or timestamp columns created):
http://initd.org/pub/software/pysqlite/doc/usage-guide.html#extensions-and-caveats
http://www-eleves-isia.cma.fr/Doc/python-sqlite-1.0.1/doc/rest/manual.txt
|