views:

151

answers:

2

Ok, so I need to populate a MS Access database table with results from a MySQL query. That's not hard at all. I've got the program written to where it copies a template .mdb file to a temp name and opens it via odbc. No problem so far.

I've noticed that Access does not support batch inserting (VALUES (foo, bar), (second, query), (third query)). So that means I need to execute one query per row (there are potentially hundreds of thousands of rows). Initial performance tests show a rate of around 900 inserts/sec into Access. With our largest data sets, that could mean execution times of minutes (Which isn't the end of the world, but obviously the faster the better).

So, I tried testing a prepared statement. But I keep getting an error (Warning: odbc_execute() [function.odbc-execute]: SQL error: [Microsoft][ODBC Microsoft Access Driver]COUNT field incorrect , SQL state 07001 in SQLExecute in D:\....php on line 30).

Here's the code I'm using (Line 30 is odbc_execute):

$sql = 'INSERT INTO table 
    ([field0], [field1], [field2], [field3], [field4], [field5]) 
    VALUES (?, ?, ?, ?, ?, ?)';
$stmt = odbc_prepare($conn, $sql);
for ($i = 200001; $i < 300001; $i++) {
    $a = array($i, "Field1 $", "Field2 $i", "Field3 $i", "Field4 $i", $i);
    odbc_execute($stmt, $a);
}

So my question is two fold. First, is there any idea on why I'm getting that error (I've checked, and the number in the array matches the field list which matches the number of parameter ? markers)? And second, should I even bother with this or just use the straight INSERT statements? Like I said, time isn't critical, but if it's possible, I'd like to get that time as low as possible (Then again, I may be limited by disk throughput, since 900 operations/sec is high already)...

Thanks

+1  A: 

Does PHP give you a way to view the INSERT statement you're executing after parameter replacement? I think you may not be getting quotes around text values in your VALUES list. Without quotes, the Jet database engine will interpret 'Field1 200001' as two values rather than one.

Also, I don't know PHP, but should the second member of your array be "Field1 $i" instead of "Field1 $"?

Can you execute this statement from PHP? And does it work?

INSERT INTO table 
    ([field0], [field1], [field2], [field3], [field4], [field5]) 
VALUES (
    200001,
    'Field1 200001',
    'Field2 200001',
    'Field3 200001',
    'Field4 200001',
    200001);

How about this?

$sql = 'INSERT INTO table 
    ([field0], [field1], [field2], [field3], [field4], [field5]) 
    VALUES (?, "?", "?", "?", "?", ?)';
HansUp
Not that I've found. Since it's writing to a local file, I can't even wireshark it (that I know of)...
ircmaxell
Yes, that works quite fine...
ircmaxell
In that case, I suspect the problem INSERT statements are submitted to Jet without quoted text values. Unfortunately, I got no clue how to remedy that from PHP.
HansUp
Adding quotes around the `?` in the values statement is showing the same error as before... I'm not sure what's going on. Any advice on the other question (should I bother, or will the insert rates be the same)?
ircmaxell
No, I was just focused on get it working. Afraid I'm stuck. It's a shame you can't do this from within Access; it could be simple to link to your MySql table and use it to run an insert into a Jet table.
HansUp
I looked at the PHP documentation for odbc_prepare. It talks about use with stored procedures. Since Jet doesn't support what we usually think of as stored procedures, I'm doubtful odbc_prepare is all that useful here. Since that one statement I suggested worked, build your statement and execute for each row. I'm guessing the time it takes to do the Jet insert, rather than the time it takes for PHP to build each INSERT statement string, will be your bottleneck.
HansUp
Well, I come from a MySQL background. And in MySQL the TCP and SQL parsing time is typically your bottleneck. So if you can group 10 inserts into 1 SQL statement, it'll typically complete in around 10% of the time. So since prepared queries can take away the parsing step, I figured I'd gain something. But I guess not...
ircmaxell
Another point I considered is that when you INSERT into MySQL, you don't necessarily have to wait on a disk write. But with ODBC to Jet, I don't think there's any way to defer the writes. It might be interesting to see what would happen if you copied your template MDB to a RAM disk and did the INSERTs there. I also like Remou's suggestion to use a single set-based operation. Maybe do both ...
HansUp
Well, I'm going to play around on Monday with some more options (including Remou's) and see where that gets me. Perhaps I'll try a RAM disk as well (I could do that in production too... Just create the file in RAM, then cp to disk once it's finished.... interesting...)
ircmaxell
+1  A: 

Do you need to do this row by row? Why not insert all the data at once?

http://stackoverflow.com/questions/442915/what-is-the-best-way-to-synchronize-data-between-ms-access-and-mysql/443003#443003

Remou
Woah! You can do that? Now, my MySQL query is pretty complicated. Would I need to write that in Access syntax? Or do I write it in MySQL syntax, but execute it via Access? I most definitely don't want to do it row-by-row (if I have to, fine), but the query that generates the results has between 2 and 8 joins, and a varying amount of where clause (and group by clause) trickery... Even still this looks like an interesting route...
ircmaxell
You should be able to execute a pass-through query in Access, or use ADO and a MySQL connection string, in which case you can refer to Access with IN. I am not sure where you want to start from, so it is a little difficult to give more detail.
Remou
I'll play around with this a bit on Monday, and if I run into issues, post a new question... Thanks!
ircmaxell
From what I recall, MySQL can't access ODBC data except in another MySQL database, so you'd need to create the ODBC linked table (to the MySQL target table) in Access and tell Access to run the SQL via ODBC.
David-W-Fenton
It is a complex query, so I do not think Access is necessarily the best place to have it, however, it is not difficult to work with MySQL connection strings in Access.
Remou
I was looking at the PHP tag and assuming the programming language was PHP, and so you'd have your MySQL data source, where you'd do stuff directly with MySQL, and then you'd have a Jet/ACE data source that had an ODBC linked table pointing at the MySQL target table, and you'd use ODBC on the Jet/ACE data source to execute the insert, which would take the data from the linked table (from MySQL) and insert it into the Jet/ACE table. Is that not the correct scenario?
David-W-Fenton