Возник такой вопрос. В БД (Access 2000) заносятся записи со скоростью приблизительно 8 записей в секунду (+- 3 записи). Записи успешно добавляются, вот код для их добавления
char *szQueryY="INSERT INTO `Y_Table` (`Num`,`WorkTime`,`Y`) VALUES (?,?,?);";
char *szQueryI="INSERT INTO `I_Table` (`Num`,`WorkTime`,`I`) VALUES (?,?,?);";
char *szQuery;
switch(table)
{
case 'Y':szQuery=szQueryY; break;
case 'I':szQuery=szQueryI; break;
default: return false;
}
SQLPrepare(hstmt, (UCHAR*)szQuery, SQL_NTS);
SQLBindParameter(hstmt, 1, SQL_PARAM_INPUT, SQL_INTEGER,
SQL_INTEGER, 1, 0, &Num, 0, 0);
SQLBindParameter(hstmt, 2, SQL_PARAM_INPUT, SQL_C_DOUBLE,
SQL_C_DOUBLE, 1, 0, &Time, 0, 0);
SQLBindParameter(hstmt, 3, SQL_PARAM_INPUT, SQL_C_DOUBLE,
SQL_C_DOUBLE, 1, 0, &mean, 0, 0);
if(SQLExecute(hstmt)==SQL_ERROR)
{
return false;
}
return true ;
}
Как я сказал, записи добавляются успешно. Но я немного лукавлю. Есть проблема, время от времени SQLExecute() возвращает SQL_ERROR. При этом входные данные подаются корректные (т.е. практически ничем не отличаются от тех, на которых SQL_Execute нормально отрабатывала). Никакой закономерности по времени и по входным данным не обнаружено. Т.е. я не могу сказать, что каждые 2 минуты происходит ошибка или при каких-то определенных значениях. Но я могу сказать, что чем дольше работает прога, тем ошибки появляются чаще. У меня складывается ощущеие, что это зависит от размера БД. Чем больше в ней записей, тем чаще ошибки. Если БД почистить, то некоторое время ошибки не выскакивают, но потом постепенно появляются, при этом как я говорил частота их появления возрастает.
Если БД не обнулять, но закрыть приложение, а потом опять запустить, то ошибки начинают выскакивать практически сразу (что косвенно подтверждает зависимость их появления от размеров БД)
У меня 2 вопроса:
1) Почему это происходит? Кто(что) в этом виноват?
2) Как с этим бороться? (Что делать? -Чернышевский).
Я пробовал подставить такой запрос
INSERT INTO `Y_Table` (`Num`,`WorkTime`,`Y`) VALUES (1,1,1);";
INSERT INTO `I_Table` (`Num`,`WorkTime`,`I`) VALUES (2,2,2);
Но ошибки все равно, время от времени выскакивали, что еще раз подтверждает, что дело не во входных даны.
Пробовал транзакции – добавлял в конце функции
SQLTransact(henv,hdbc,SQL_COMMIT);
Не помогло. Кстати эта функция нужна там или нет?
У меня есть на примете одно решение:
В случае, если запрос не удался делать Sleep() на 10-50мс и повторить запрос. Так до тех пор, пока запрос не проскочит, но не более 5 раз (к примеру, а то может и 2 или 10).
Но хотелось бы более элегантно решить эту проблему. К тому же попытка повторить запрос не гарантирует успех, т.к. уже пробовал повторять запрос второй раз в случае неудачи и бывало, что и второй раз подряд выскакивала ошибка.
Итак, работа происходит с БД – Microsoft Access 2000, используется ODBC (ну, я думаю, все догадались).
На всякий случай опишу структуру БД (а именно 2 таблицы, т.к. только в них трабблы):
№ Имя Тип
I_Table
1) Num счетчик (автоинкремент) Длинное целое
2) WorkTime Длинное целое
3) I Действительное
Y_Table
1) Num счетчик (автоинкремент) Длинное целое
2) WorkTime Действительное
3)Y Двойное с плавающей точкой
Структура БД не обсуждается

.
Заранее спасибо за развернутые ответы.
P.s. Забыл написать как подключаюсь, мало ли пригодится
Codbc::Codbc()
{
//////////////
szUser[256] = 0;
szPass[256] = 0;
strcpy(szDsn,"mysource");
hstmt=NULL;
henv = NULL;
hdbc = NULL;
/////////////
if (::SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv) == SQL_ERROR)
{
MessageBox(0,"error connect","error connect",0);
}
::SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(void*) SQL_OV_ODBC3, SQL_IS_INTEGER);
if ( ::SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc) == SQL_ERROR)
{
MessageBox(0,"error connect2","error connect2",0);
}
if ( ::SQLConnect(hdbc, (SQLTCHAR*)szDsn, SQL_NTS,
(SQLTCHAR*)szUser, _tcslen(szUser), (SQLTCHAR*)szPass,
_tcslen(szPass) ) == SQL_ERROR )
{
MessageBox(0,"error connect source","error connect source",0);
}
if (::SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
{
MessageBox(0,"Error zapros","Error zapros",0);
}
}
Здравствуйте, NdF, Вы писали:
NdF>szErrorMsg=Обновление невозможно; установлена блокировка
NdF>szSqlState=HY000
NdF>pcbErrorMsg=0.00000
Ну это мало о чем говорит. К тому же SQLERROR() уже давно deprecated, лучше используй SQLGetDiagRec(). Нужно получить ошибку в стандартном формате
[vendor-identifier][ODBC-component-identifier][data-source-identifier]data-source-supplied-text
NdF>У меня есть мыслишки, но мне стыдно их даже выражать, по сколько я с этим делом не сталкивался. Хотелось бы услышать (прочитать) мнение знающих людей.
Отчего же стыдно, наоборот давай выражай. Вместе же решаем проблему.
Я вот спрошу пока:
1) С базой работает только одно твое приложение, или данный код выполняет несколько пользователей 8 раз в секунду?
2) Счетчик Num в таблицах генерирует последовательные значеения или случайные?
W>Ну это мало о чем говорит. К тому же SQLERROR() уже давно deprecated, лучше используй SQLGetDiagRec(). Нужно получить ошибку в стандартном формате [vendor-identifier][ODBC-component-identifier][data-source-identifier]data-source-supplied-text
W>
Попробую.
W>Я вот спрошу пока:
W>1) С базой работает только одно твое приложение, или данный код выполняет несколько пользователей 8 раз в секунду?
W>2) Счетчик Num в таблицах генерирует последовательные значеения или случайные?
1)С базой работает 8 потоков одного приложения.
И у меня такая мысль, а не может такого быть, что один поток пытается занести свой запрос, в то время как заносится запрос другого потока. Может надо как-то разделить критический ресурс? А с другой стороны, что-то мне кажется, что БД сама как-то решает такие задачи (я по БД мало что знаю, точнее про механизмы работы СУБД).
2)Я перепутал, Num — Длинное целое. Это не primary key и не foreign key. Пока в таблице (в БД) нет никаких ограничений целостности.
Значение Num берется из другой таблицы. Попытаюсь сейчас поподробнее. Для каждого потока одно значение Num и оно для этого потока постоянно, т.е. если в приложении запущено 8 потоков, то в БД на данный момент в поле Num заносится 8 разных значение, которое постоянны.
Пример:
1)255 256 258 257 259
260 254 261
2)261 256 257 258 259 255 260 254
Т.е. значения могут оказаться и перетасованы (порядок их в БД, хотя это не имеет смысла для БД ) но они постоянны.
Если один поток отрубить, а потом запустить, то по данному примеру этот поток будет в поле Num заносить значение 262.
Ну, как смог пояснил.
У меня такой вопрос возник, а не может дело быть в следующем:
Я в первом посте показал конструктор класса, так там послдение строки
if ( ::SQLConnect(hdbc, (SQLTCHAR*)szDsn, SQL_NTS,
(SQLTCHAR*)szUser, _tcslen(szUser), (SQLTCHAR*)szPass,
_tcslen(szPass) ) == SQL_ERROR )
{
MessageBox(0,"error connect source","error connect source",0);
}
// Get a statement handle
if (::SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt) == SQL_ERROR)
{
MessageBox(0,"Error zapros","Error zapros",0);
}
Когда же я делаю запрос, я не делаю этих подключений и ничего не освобождаю. Все освобождаентся только в деструкторе.
Может надо функции SQLConnect и SQLAllocHandle вызывать перед
каждым запросом и соответственно освободжать их после каждого запроса?
зы. Извеняюсь, что долго не отвечал, я думал у вас на форуме уведомления на почту приходят об ответе, оказалось что нужно галочку после каждогопоста ставить.
Провел сейчас очередное "исследование". Был на длительное время запущен только один поток. он сделал в БД около 7 тысяч записей (больше я ждать не стал) и за это время не появилось ни одной ошибки. При 8 потоках ошибки уже начинают выскакивать примерно на 2 тысячах записей (может чуть позже).
Вот так я вызывал функцию SQLGetDiagRec
SQLSMALLINT i = 1;
while ((SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, i, SqlState, &NativeError,
ErrorMsg, sizeof(ErrorMsg), &iErrorMsg)) != SQL_NO_DATA) {
// DisplayError(SqlState,NativeError,Msg,&iErrorMsg);
sprintf(s,"ErrorMsg=%s --- SqlState=%s --- iErrorMsg=%lf",ErrorMsg,SqlState,iErrorMsg);
MessageBox(0,s,&table,0);
i++;
}
Вот что получил:
[quote]
szErrorMsg=Обновление невозможно; установлена блокировка пользователем “admin”
szSqlState=HY000
pcbErrorMsg=0.00000
[/quote]
Короче тоже самое.
ИЗ МСДН
[quote]
HY000 General error An error occurred for which there was no specific SQLSTATE and for which no implementation-specific SQLSTATE was defined. The error message returned by SQLGetDiagRec in the MessageText buffer describes the error and its cause.
[/quote]
Стоит отметить, что с ОДБЦ я работаю впервые, так что может стоит искать глупую ошибку или все таки надо разграничивать пользование критическим ресурсом? Если второе, то как лучше, есть какая-нибудь функция, которая возвращает готовность БД или там чего еще, для того, чтобы была возможность вставлять запрос.
Мужики, может део в транзакции? Тогда что и как исправить? Что никто с этим не сталкивался?