DAO.Recordset.Обновление приводит к блокировке record

#mysql #ms-access #vba

#mysql #ms-access #vba

Вопрос:

Я пытаюсь запустить следующий код, чтобы обойти набор записей и выполнять обновления там, где это необходимо.

У меня есть база данных Microsoft Access, подключенная к серверной части MySQL. Всякий раз, когда я запускаю этот код, я получаю следующую ошибку:

Ошибка 3197: ядро базы данных Microsoft Office Access остановило процесс, потому что вы и другой пользователь пытаетесь изменить одни и те же данные одновременно.

Код приведен ниже:

 Private Sub test()
    Dim rs As DAO.Recordset, rsCnt As Long, i As Long

    Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
    rs.MoveLast
    rsCnt = rs.RecordCount
    rs.MoveFirst
    For i = 1 To rsCnt
        rs.Edit
        rs!MyFieldInTable = "test"
        rs.Update
    Next i
End Sub
 

Я подумал, что база данных Access может быть повреждена, поэтому я извлек более раннюю резервную копию, но она делает то же самое, что заставляет меня думать, что это проблема MySQL.

Мы используем идентичный фрагмент кода в другой версии этой базы данных, связанной с другой таблицей MySQL, и она работает нормально.

Кроме того, когда я открываю запрос, на основе которого основан набор записей, я могу редактировать данные в запросе без каких-либо проблем.

Просто чтобы добавить в первом цикле rs!MyFieldInTable обновляется, затем я получаю сообщение об ошибке.

Ответ №1:

Не похоже, что вы переходите к другой записи в наборе записей. Простое увеличение i не приводит к переходу к следующей записи. Более традиционным подходом было бы перебирать набор записей без необходимости в других ваших переменных ( i и rsCnt ).

 Dim rs as DAO.Recordset
Set rs = CurrentDb.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop
 

Редактировать
После небольшого поиска я наткнулся на эту тему, которая, похоже, похожа на вашу проблему. В нижней части потока предлагается изменить настройки ODBC для вашего MySQL DSN, выбрав вкладку «Дополнительно» и выбрав опцию «Возвращать совпадающие строки». В сообщении также говорится, что нужно удалить связанную таблицу, а затем повторно связать ее с вашей базой данных Access.
В прошлом я не использовал Access с MySQL, поэтому понятия не имею, сработает это или нет, поэтому действуйте осторожно!

Вы также можете попробовать изменить свой набор записей, чтобы использовать флаг dbOptimistic для параметра блокировки набора записей, чтобы посмотреть, помогает ли это вообще:

set rs = CurrentDB.OpenRecordSet("qryMyQuery", DB_OPEN_DYNASET, dbOptimistic)

Комментарии:

1. 1 Как и @XIVSolutions, я писал аналогичный ответ, когда получил сообщение о том, что загружен новый ответ.

2.Кроме того, поправьте меня, если я ошибаюсь, но я верю rs.Редактирование должно проходить внутри цикла. Из файла справки DAO: Метод редактирования: копирует текущую запись из обновляемого объекта набора записей в буфер копирования для последующего редактирования.

3. Лучше использовать . Выполнить метод DAO. Переменная объекта базы данных и включает dbFailOnError, как описано в @mwolfe02. Также добавьте обработчик ошибок для обработки любых поверхностей dbFailOnError. Одним из последствий DoCmd.RunSQL является то, что пользователи часто отключают SetWarnings, чтобы избежать сообщений с подтверждением. Отключив SetWarnings, вы рискуете скрыть важную информацию, которая затем затруднит устранение неполадок.

4. Я хочу повторить то, что только что сказал @HansUp. Когда-нибудь вы избавите себя от многих душевных страданий, если привыкнете использовать. Выполнить с dbFailOnError вместо подхода Warnings False / RunSQL / Warnings True . Я не знаю веских причин для использования DoCmd.RunSQL.

5. @Hk1: Я согласен, хотя целью этого примера не было продемонстрировать полный текст, который обычно пишут в коде производственного качества, он должен был просто проиллюстрировать, как перебирать набор записей без использования целых чисел, как первоначально было показано на плакате с примером кода.

Ответ №2:

Вы можете попробовать две вещи. Сначала попробуйте добавить опцию dbSeeChanges при открытии набора записей:

 Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", dbOpenDynaset, dbSeeChanges)
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop
 

Другой вариант, как предложил @HansUp, заключается в использовании инструкции SQL update вместо динамического набора записей. Ключ заключается в том, чтобы открыть набор записей как моментальный снимок, чтобы изменения, которые вы вносите в записи, не влияли на сам набор записей.

 Dim rs as DAO.Recordset, db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryBatchPayments", dbOpenSnapshot)
Do Until rs.EOF
    db.Execute "UPDATE Payments " amp; _
               "SET DCReference='test' " amp; _
               "WHERE PaymentID=" amp; !PaymentID, dbFailOnError
    rs.MoveNext
Loop
 

Ответ №3:

У меня была такая же проблема, и мое решение оказалось значением по умолчанию для БИТОВЫХ (1) полей. Access не любит, когда они равны нулю. Убедитесь, что вы используете 0 или 1 в mysql для этих полей.

Ответ №4:

У меня здесь нет MySQL, чтобы попробовать это, но мне кажется, что ваш код не продвигает набор записей после выполнения метода rs.Update, так что вы пытаетесь удалить то же поле в первой записи.

Добавьте эту строку после rs.Update:

 rs.MoveNext
 

Надеюсь, это поможет.

Комментарии:

1. Кроме того, код, опубликованный Тимом Лентином, является более элегантным способом сделать это. Он опубликовал, пока я все еще писал свой ответ.

2. Если люди собираются проголосовать против, было бы просто замечательно, если бы они сопровождали голосование против некоторыми аргументами. Просто говорю

Ответ №5:

Попробуйте вызвать OpenRecordset из объектной переменной, установленной в CurrentDb(), а не напрямую из CurrentDb() .

 Dim rs as DAO.Recordset
Dim db As DAO.Database
Set db = Currentdb
Set rs = db.OpenRecordset("qryMyQuery", DB_OPEN_DYNASET)
rs.moveFirst
Do Until rs.EOF
    rs.Edit
    rs!FieldNameHere = "test"
    rs.Update
    rs.MoveNext
Loop
 

Причина этого предложения в том, что я обнаружил, что операции с CurrentDb напрямую могут выдавать ошибку о том, что «блок не установлен». Но я не получаю ошибку при использовании вместо этого объектной переменной. И ISTR OpenRecordset был одной из таких операций, где это было проблемой.

Кроме того, у меня сложилось впечатление, что ваш подход является громоздким способом достижения эквивалента:

 UPDATE qryMyQuery SET FieldNameHere = "test";
 

Однако я подозреваю, что этот пример является прокси для реальной ситуации, когда подход с набором записей полезен. Тем не менее, это заставляет меня задуматься, увидите ли вы ту же или другую ошибку при выполнении инструкции UPDATE.

Если у вас по-прежнему будут проблемы с этим, это может помочь показать нам представление SQL для qryMyQuery.

Ответ №6:

Я обнаружил, что если кто-то попытается сохранить данные, которые совпадают с данными, которые уже есть в записи MySQL, Access отобразит ошибку такого рода. Я попробовал несколько предложений из этой темы, но это не помогло.

Простым решением для этого является сохранение слегка отличающихся данных с помощью ручной временной метки. Вот пример добавления поля порядка сортировки и установки для него значений 10, 20, 30…

     i = 10
    timeStamp = Now()
    Do Until Employee.EOF
        Employee.Edit
        Employee!SortOrderDefault = i
        Employee!LastUpdated = timeStamp
        Employee.Update
        i = i   10
        Employee.MoveNext
    Loop
 

Я пробовал автоматическую временную метку в таблице MySQL, но это не помогло, когда новые входные данные совпадают со старыми.

Комментарии:

1. Я испытал то же самое. Упоминается в этом отчете об ошибке MySQL: bugs.mysql.com/bug.php?id=46406 По-видимому, это вызвано тем, как Access формирует свои запросы на обновление. Это можно решить, как вы предлагаете на стороне клиента или на стороне сервера, используя timestamp столбец.

Ответ №7:

Мой маленький полезный совет заключается в том, что bits — это очень, очень, очень плохие типы данных для использования при связывании таблиц SQL с Microsoft Access, поскольку только SQL Server понимает, что такое bit, Microsoft Access с трудом интерпретирует, что такое bit. Измените любые битовые типы данных на int (целые числа) и повторно свяжите свои таблицы, которые должны прояснить ситуацию. Кроме того, убедитесь, что ваши логические значения всегда содержат 1 или 0 (не yes / no или true / flase) в вашем коде VBA, иначе ваши обновления не будут доступны для связанных таблиц SQL, потому что Microsoft Access попытается обновить их с помощью True / False или Yes / No, и SQL будетне так.

Ответ №8:

У меня также была такая же проблема; я решил их, добавив их в код, используя dao.recordset:

 **rst.lockedits = true**
rst.edit
rst.fields(...).value = 1 / rst!... = 1
rst.update
**rst.lockedits = false**
 

похоже, это устраняет конфликт между только что открытыми данными (например, в форме) и обновлением их с помощью кода.

Извините за мой плохой английский … я много читал, но никогда не изучал его! Я просто итальянец.

Комментарии:

1. Внимание, если вы изменили свой код на инструкцию SQL. Событие Form_Unload пытается обновить те же данные, которые вы изменили с помощью SQL, и выдает неуправляемую ошибку; это может привести к исчезновению всего вашего приложения.