![]() |
Database »
Database »
General
Intermediate
Reading Excel files using ODBCBy Alexander MikulaA discussion and demonstration of reading Excel files using ODBC |
SQL, VC6, SQL Server, DBA, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||
After contributing that article about writing into an Excel file I got tons of requests about how to read from one. Well, you asked for it...
The main problem is that you can�t read an Excel file without previously having some formatting done. Microsoft refers to this in one of their KB papers. If somewhere out there finds there�s a way to do the reading whithout the formatting, please let me know...
Another problem is the DSN you need to have installed in your ODBC Admin. This is not very useful because you don�t always know the name of the Excel file from the start.
The last problem I�m dealing with here is generally doing ODBC reading using CRecordset
without deriving from it. That is because if I always have to create a class for every single table
I want to use, I�ll end up with lots of rather unnecessary code enlarging my app�s exe.
SQLGetInstalledDrivers() will show all the installed drivers. For an example see
CReadExcelDlg::GetExcelDriver() below.
CRecordset the plain way you have to use a readonly, foreward only
recordset. The data to get is defined by the SQL statement you put into CRecordset::Open().
Reading out the result is done by CRecordset::GetFieldValue(). For an example see the
code below.
In order to get the code below going you have to
Using a pseudo DSN only works with ODBC Admin V3.51 and higher. Earlier versions will not be able to use a DSN that actually isn�t installed. The result of an attempt to do so is some mumbling about missing registry keys.
If using an underived CRecordset it needs to be readonly, foreward only. So any attempts to change
the data or to move back will fail horribly. If you need to do something like that you�re bound to
use CRecordset the "usual" way. Another drawback is that the tremendous overhead of CRecordset does
in fact make it rather slow. A solution to this would be using the class CSQLDirect contributed by
Dave Merner at http://www.codeguru.com/mfc_database/direct_sql_with_odbc.shtml.
One unsolved mystery in reading those files is how to get the data WITHOUT having a name defined for it. That means how can the structure of the data be retrieved, how many "tables" are in there, and so on. If you have any idea about that I�d be glad to read it under almikula@EUnet.at (please make a CC to alexander.mikula@siemens.at).
// Query an Excel file void CReadExcelDlg::OnButton1() { CDatabase database; CString sSql; CString sItem1, sItem2; CString sDriver; CString sDsn; CString sFile = "ReadExcel.xls"; // the file name. Could also be something // like C:\\Sheets\\WhatDoIKnow.xls // Clear the contents of the listbox m_ctrlList.ResetContent(); // Retrieve the name of the Excel driver. This is // necessary because Microsoft tends to use language // specific names like "Microsoft Excel Driver (*.xls)" versus // "Microsoft Excel Treiber (*.xls)" sDriver = GetExcelDriver(); if (sDriver.IsEmpty()) { // Blast! We didn�t find that driver! AfxMessageBox("No Excel ODBC driver found"); return; } // Create a pseudo DSN including the name of the Driver and the Excel file // so we don�t have to have an explicit DSN installed in our ODBC admin sDsn.Format("ODBC;DRIVER={%s};DSN='';DBQ=%s", sDriver, sFile); TRY { // Open the database using the former created pseudo DSN database.Open(NULL, false, false, sDsn); // Allocate the recordset CRecordset recset(&database); // Build the SQL string // Remember to name a section of data in the Excel sheet using // "Insert->Names" to be able to work with the data like you would // with a table in a "real" database. There may be more than one table // contained in a worksheet. sSql = "SELECT field_1, field_2 " "FROM demo_table " "ORDER BY field_1"; // Execute that query (implicitly by opening the recordset) recset.Open(CRecordset::forwardOnly, sSql, CRecordset::readOnly); // Browse the result while (!recset.IsEOF()) { // Read the result line recset.GetFieldValue("field_1", sItem1); recset.GetFieldValue("field_2", sItem2); // Insert result into the list m_ctrlList.AddString(sItem1 + " --> "+sItem2); // Skip to the next resultline recset.MoveNext(); } // Close the database database.Close(); } CATCH(CDBException, e) { // A database exception occured. Pop out the details... AfxMessageBox("Database error: " + e->m_strError); } END_CATCH; } // Get the name of the Excel-ODBC driver // Contibuted by Christopher W. Backen - Thanx Christoper CString CReadExcelDlg::GetExcelDriver() { char szBuf[2001]; WORD cbBufMax = 2000; WORD cbBufOut; char *pszBuf = szBuf; CString sDriver; // Get the names of the installed drivers // ("odbcinst.h" has to be included ) if (!SQLGetInstalledDrivers(szBuf, cbBufMax, &cbBufOut)) return ""; // Search for the driver... do { if (strstr(pszBuf, "Excel") != 0) { // Found ! sDriver = CString(pszBuf); break; } pszBuf = strchr(pszBuf, '\0') + 1; } while (pszBuf[1] != '\0'); return sDriver; }
Please refer the demo project (ReadExcelDlg.cpp) for more details.
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 12 Jan 2000 Editor: Chris Maunder |
Copyright 2000 by Alexander Mikula Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |