Решено (и в блоге), как показано ниже
Я внедряю обновление для набора баз данных Access, чтобы заменить файловую базу данных Access на MySQL, связанную ODBC. Кажется, все идет замечательно гладко, за исключением общей концепции вставки записи в таблицу со столбцом идентификатора с автоприращением и извлечения значения только что созданного идентификатора. Конечно, на PHP или тому подобном можно было бы использовать
SELECT LAST_INSERT_ID ()
Функция для получения идентификатора. Однако сама документация MySQL говорит, что это не работает для некоторых приложений ODBC, таких как Delphi или Access, и предлагает использовать
ВЫБРАТЬ * ОТ ТАБЛИЦЫ, ГДЕ АВТОМАТ НУЛ;
К сожалению, это просто не сработало для меня, когда меня вызвали из приложения Access, с которым я работал, и, кажется, в Интернете есть несколько комментариев, которые действительно ненадежны, так как Access может разорвать соединение для передачи данных и восстановить соединение за сцены - таким образом, сделать вызов недействительным.
В качестве альтернативы я решил использовать функцию MySQL для добавления пустой записи в таблицу, возвращая идентификатор, который Access затем использовал бы для обновления записи (что хорошо согласуется со стилем кода существующего приложения). К сожалению, этот, по-видимому, простой обходной путь не может быть простым, так как давние ошибки в MySQL делают поиск допустимого кода, который может как отправлять, так и возвращать переменную, чем-то сложным. Несколько примеров в Интернете будут работать в ограниченной области использования переменных IN или OUT, но не будут работать с обоими.
Мое окончательное решение, которое работает на развернутой комбинации MySQL 5.1 и Access 2003, выглядит следующим образом
Процедура MySQL
DELIMITER $$
CREATE
PROCEDURE `alicedata`.`sp_ins_identity`(IN tablename VARCHAR(50))
BEGIN
SET @result = 0;
SET @sqlproc = CONCAT("INSERT INTO ",tablename," () VALUES ();");
PREPARE s1 FROM @sqlproc;
EXECUTE s1;
DEALLOCATE PREPARE s1;
SELECT LAST_INSERT_ID();
END$$
Эта процедура полезна тем, что вставляет строку и возвращает идентификатор для любой таблицы, где строка содержит все нулевые или ненулевые поля с определенными значениями по умолчанию. Для вызова я использую следующую функцию:
Public Function InsertMySQLIdentityRow(DSN As String, Tablename As String) As Integer
On Error GoTo Err_InsertMySQLIdentity
Dim cnnSQL As New ADODB.Connection
Dim cmdSQL As ADODB.Command
Dim pid As Integer
Dim rs
Dim strSQL As String
' initialize
pid = 0
' set up ADO connection
Set cnnSQL = New ADODB.Connection
cnnSQL.Open DSN
' execute the procedure - note that assembling by parameter fails to handle IN or OUT correctly
Set cmdSQL = New ADODB.Command
cmdSQL.ActiveConnection = cnnSQL
strSQL = "call sp_ins_identity('" & Tablename & "');"
Set rs = CreateObject("ADODB.Recordset")
Set rs = cnnSQL.Execute(strSQL)
If Not rs.EOF Then
pid = rs(0)
End If
' clean up
Set rs = Nothing
Set cmdSQL = Nothing
cnnSQL.Close
Set cnnSQL = Nothing
Exit_InsertMySQLIdentity:
InsertMySQLIdentityRow = pid
Exit Function
Err_InsertMySQLIdentity:
MsgBox Err.Number & Err.Description
Resume Exit_InsertMySQLIdentity
End Function
Этот код несколько необычен тем, что обычно на MSSQL вы используете параметризованный вызов процедуры, но из-за ошибок в MySQL ODBC (или, по крайней мере, несовместимости с Access) вышеприведенное, кажется, единственный способ, который позволяет данные для передачи и возврата.