После того, как SP Insert, следующая страница имеет пустой результат до перезагрузки
У меня есть хранимая процедура (SP с этого момента), которая вставляет данные в базу данных (SaveClient, см. ниже). Когда SP завершен, я перенаправляю страницу PHP на другую страницу PHP, в которой перечислены записи (FetchObjectList, см. Ниже). Список не возвращает вновь созданную запись, пока я не перезагружаю/обновляю страницу.
Хранимая процедура имеет COMMIT
в конце, я закрываю соединение с базой данных в PHP-коде после вызова SP и проверяется наличие ошибок, но ничего не происходит.
Сама страница возвращает код состояния 200, который означает, что он не кэшируется, поэтому не может быть связан с браузером.
Текущее обходное решение - это sleep(1)
в коде PHP, но когда код идет в живую, я понятия не имею, хватит ли этого. Я бы предпочел, чтобы MySQL выложил правильный набор результатов.
EDIT: Я использую интерфейс объектов MySQLi для PHP, может быть полезно узнать.;)
Мой декомпьютер получил PHP 5.2.17, MySQL 5.0.51a (InnoDB) и Apache 2.2.17, установленные и запущенные в Windows 7 x64.
UPDATE
Добавлена следующая строка CALL FetchObjectList('client_tbl', NULL, NULL, 1, 'client_tbl.name ASC', NULL, NULL);
в конец SaveClient. В представленном наборе результатов нет нового клиента.
ОБНОВЛЕНИЕ 2
Я попытался использовать SQL_NO_CACHE
, как показано здесь, но безрезультатно.
Теперь я попробую тот же SQL непосредственно в PHP вместо вызова SP.
ОБНОВЛЕНИЕ 3 - 20 сентября
Я пробовал любой разумный ответ/комментарий, которого я до сих пор не повезло. Я попытался обновить мою версию PHP и MySQL сегодня (так как сегодня я узнал, что живой сервер будет работать на PHP 5.3.something и MySQL 5.1.something), но не получил его для работы. Мне нужно обновить PHP, чтобы получить более свежий php_mysqli.dll
/libmysql.dll
, поскольку тот, который у меня есть, поддерживает только до 5.0.51a, и может возникнуть моя проблема, так как ничего в фактической БД не работало. Я попробовал libmysql.dll
из установки MySQL безрезультатно.
Обратите внимание, что я также изменил PHP-код, который я включил, поскольку я на самом деле скопировал неверный, который вызывал user_tbl
, а не client_tbl
, а также упростил его (удалил многобайтные), но все тот же результат.
Я не знаю, что случится с щедростью, если он вернется ко мне, я добавлю его снова.
Сохраненная процедура SaveClient
DELIMITER //
DROP PROCEDURE IF EXISTS work.SaveClient//
CREATE PROCEDURE work.SaveClient(
IN ObjectID INT,
IN UserID INT,
IN ClientName VARCHAR(60),
IN VersionFrom DATETIME,
IN VersionTo DATETIME)
root:BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
/*
Default values ---------------------------------------------------------------------------------------------------------
*/
# Used to block INSERT/UPDATEs
SET @DoChanges = TRUE;
SET @Fields = '*';
SET @Version = NULL;
SET @UserVersion = NULL;
SET @DateNow = NOW();
SET @VersionActive = CONCAT(
'( ( NOW() BETWEEN ',
'version_from AND ',
'version_to ) OR ( ',
'version_from < NOW() AND ',
'version_to IS NULL ) )'
);
IF VersionFrom IS NULL THEN
SET VersionFrom = @DateNow;
END IF;
/*
Search for client ------------------------------------------------------------------------------------------------------
*/
IF ObjectID IS NOT NULL THEN
SET @Client = CONCAT(
'SELECT version INTO @Version FROM client_tbl WHERE object_id = ',
ObjectID,
' AND ',
@VersionActive
);
PREPARE stmt FROM @Client;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
# Check if there are any changes
IF @Version IS NOT NULL THEN
SELECT name INTO @Name FROM client_tbl WHERE name = ClientName AND version = @Version;
IF @Name = ClientName THEN
SET @errorMsg = "Duplicate entry";
SET @errorCode = "S0000002";
SELECT @errorCode, @errorMsg;
LEAVE root;
END IF;
END IF;
END IF;
/*
Search for user ---------------------------------------------------------------------------------------------------------
*/
# Create this as a function
IF UserID IS NOT NULL THEN
SET @User = CONCAT(
'SELECT version INTO @UserVersion FROM user_tbl WHERE object_id = ',
UserID,
' AND ',
@VersionActive
);
PREPARE stmt FROM @User;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END IF;
IF @UserVersion IS NULL THEN
SET @errorMsg = "User is missing";
SET @errorCode = "U0000099";
SELECT @errorCode, @errorMsg;
LEAVE root;
END IF;
/*
Add the client ---------------------------------------------------------------------------------------------------------
*/
# Close the current version
IF @Version IS NOT NULL THEN
IF @DoChanges = TRUE THEN
CALL UpdateVersion(
ObjectID,
UserID,
@Version,
@DateNow,
'client_tbl'
);
SET @Version = @Version + 1;
END IF;
ELSE
SET @Version = 1;
END IF;
IF @DoChanges = TRUE THEN
IF ObjectID IS NULL THEN
INSERT INTO
object_tbl
(
object_class_id,
created,
created_by
)
VALUES(
2,
NOW(),
UserID
)
;
SET ObjectID = LAST_INSERT_ID();
END IF;
INSERT INTO
client_tbl
(
object_id,
version,
version_from,
version_to,
changed,
changed_by,
name
)
VALUES(
ObjectID,
@Version,
VersionFrom,
NULL,
@DateNow,
UserID,
ClientName
)
;
END IF;
COMMIT;
END //
DELIMITER ;
Сохраненная процедура FetchObjectList
DELIMITER //
DROP PROCEDURE IF EXISTS work.FetchObjectList//
CREATE PROCEDURE work.FetchObjectList(
IN ObjectType VARCHAR(60),
IN ObjectSubType VARCHAR(60),
IN ObjectSubID INT,
IN IsActive INT,
IN OrderBy VARCHAR(100),
IN SetStart INT,
IN MaxResults INT)
root:BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION ROLLBACK;
# Allow the "JSON" output be a max of 8kb
SET GLOBAL group_concat_max_len = 8096;
/*
Default values ---------------------------------------------------------------------------------------------------------
*/
SET @Fields = '*';
SET @VersionWhere = '1'; # Get everything
SET @Special = '';
SET @OrderBy = '';
SET @SetStart = '';
SET @MaxResults = '';
SET @JoinIn = '';
IF IsActive = 1 THEN
SET @VersionWhere = CONCAT(
'( NOW() BETWEEN ',
ObjectType,
'.version_from AND ',
ObjectType,
'.version_to OR ( ',
ObjectType,
'.version_from < NOW() AND ',
ObjectType,
'.version_to IS NULL ) )'
);
END IF;
IF OrderBy != '' THEN
SET @OrderBy = CONCAT(
'ORDER BY ',
OrderBy
);
END IF;
/*
Specials for each type -------------------------------------------------------------------------------------------------
*/
/*
- Clients ------------
*/
IF ObjectType = 'client_tbl' THEN
SET @Fields = '
*,
client_tbl.object_id AS object_id,
(
SELECT
COUNT(*) AS Total
FROM
client_user_privilege_tbl cup
WHERE
cup.client_id = client_tbl.object_id
) AS usercount
';
END IF;
/*
- Configuration ------------
*/
IF ObjectType = 'configuration_tbl' THEN
SET @Fields = '
*
';
END IF;
/*
Add upp the query to run -----------------------------------------------------------------------------------------------
*/
SET @Query = CONCAT(
'SELECT ',
@Fields,
' FROM ',
ObjectType,
' ',
@JoinIn,
' WHERE ',
@VersionWhere,
' ',
@Special,
@OrderBy
);
PREPARE stmt FROM @Query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
COMMIT;
END //
DELIMITER ;
PHP CODE SNIPPET (обновлено 20 сентября)
$query = "CALL FetchObjectList('client_tbl', NULL, NULL, 1, NULL, NULL, NULL)";
addTrace($query);
$rs = $db->query($query);
if( $rs ) {
addTrace('Query done -> Results: ' . $rs->num_rows);
while($r = $rs->fetch_assoc()){
$fetchArray[] = $r;
}
$count = $rs->num_rows;
$rs->close();
$db->next_result();
} else {
addTrace('Query failed -> ' . $db->error);
flushTrace();
exit;
}
Ответы
Ответ 1
Поскольку это довольно старая версия mysql, меня не удивит, что это будет связано с ошибкой, но одна вещь, которую я хотел бы на вашем месте - хочу знать, будет ли это работать с не, используя транзакций вообще. (например, autocommit = on).
Для этой версии 5.0 я также проверил бы кеш запросов и отключил бы все вместе вместо запроса (см. SHOW VARIABLES LIKE 'has_query_cache'; SET GLOBAL query_cache_size = 0;). Это, по крайней мере, устранит тех, кто играет определенную роль в этой проблеме, воспроизводит (или пытается) проблему и видит, что что-то изменилось. Если нет, я бы начал искать конкретные ошибки, особенно когда кеш запросов отключен и он все еще делает это без использования транзакций.
Я проверил поддержку 5.0 mysql (innodb).
- innodb_flush_log_at_trx_commit = 1
- innodb_flush_method = O_DIRECT
Установите эти параметры специально в my.cnf, самый главный из них. Они прекрасно объясняют, что это делают.
См. http://dev.mysql.com/doc/refman/5.0/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
Ответ 2
Проблема с mysql_query заключается в том, что она не поддерживает несколько наборов результатов. И это именно то, что делают ваши хранимые процедуры - они, как правило, возвращают более одного набора результатов. Всякий раз, когда вы вызываете SP, статус выхода тайно переносится в (пустой) набор результатов. Если вы добавите его с помощью собственного вывода из процедуры, некоторые из результатов из запроса будут проигнорированы кодом поиска PHP. Когда вы пытаетесь запустить другой запрос, ожидающие результатов все равно будут находиться в буфере.