15

データベースにデータを挿入するストアドプロシージャ(今後はSP)があります(SaveClient、以下を参照)。SPが完了したら、PHPページをエントリを一覧表示する別のPHPページにリダイレクトします(FetchObjectList、以下を参照)。ページをリロード/更新するまで、リストは新しく作成されたレコードを返しません。

ストアドプロシージャCOMMITの最後にがあります。SPが呼び出された後、PHPコードでデータベース接続を閉じ、エラーのチェックがありますが、何も問題はありません。

ページ自体は200ステータスコードを返します。これは、キャッシュされていないため、ブラウザに関連することもできないことを意味します。

現在の回避策はsleep(1)PHPコードにありますが、コードが公開されたときに、それで十分かどうかはわかりません。もちろん、MySQLに正しい結果セットを提供してもらいたいと思います。

編集:私はPHPのMySQLiオブジェクトインターフェイスを使用しています。知っておくと便利かもしれません。;)

私の開発コンピューターには、PHP 5.2.17、MySQL 5.0.51a(InnoDB)、およびApache2.2.17がインストールされてWindows7x64で実行されています。

アップデート

CALL FetchObjectList('client_tbl', NULL, NULL, 1, 'client_tbl.name ASC', NULL, NULL);SaveClientの最後に次の行を追加しました。結果セットには、表示された結果セットに新しく作成されたクライアントがありません。

更新2

私はここSQL_NO_CACHEに見られるように使用しようとしましたが、役に立ちませんでした。

SPを呼び出す代わりに、同じSQLをPHPで直接試してみます。

更新3-9月20日

私はこれまでに得た合理的な答え/コメントを運がなくて試しました。今日、PHPとMySQLのバージョンを更新しようとしましたが(ライブサーバーはPHP5.3.somethingとMySQL5.1.somethingで実行されることを知ったので)、動作しませんでした。PHPを更新して最新のものを入手する必要がありますphp_mysqli.dll/入手libmysql.dllしたものは5.0.51aまでしかサポートしておらず、実際のDBには何も機能していないため、問題が発生する可能性があります。MySQLのインストールから試してみましたlibmysql.dllが無駄になりました。

user_tblを呼び出していた間違ったコードを実際にコピーし、それを単純化した(複数のクエリを削除した)ので、含めたPHPコードも変更した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コードスニペット(9月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;
}
4

2 に答える 2

1

mysql_query の問題は、複数の結果セットをサポートしていないことです。ストアド プロシージャは、複数の結果セットを返す傾向があります。SP を呼び出すたびに、(空の) 結果セットで終了ステータスが密かに運ばれます。プロシージャからの独自の出力を合計すると、クエリからの結果セットの一部が PHP 検索コードによって無視されます。別のクエリを実行しようとすると、保留中の結果セットがバッファ内に残ります。

于 2012-09-20T11:37:59.997 に答える
1

これはかなり古いバージョンの mysql であるため、これがバグに関連していることは驚くことではありませんが、私が知りたいことの 1 つは、トランザクションをまったく使用しないことでこれが機能するかどうかです。(例: autocommit = オン)。

そのバージョン 5.0 では、クエリ キャッシュもチェックし、クエリごとではなくまとめて無効にします (SHOW VARIABLES LIKE 'have_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を参照してください。

于 2012-09-18T17:10:46.393 に答える