2016-08-30 68 views
0

我有這個疑問:爲什麼更新查詢不起作用?

START TRANSACTION; 
UPDATE users SET events = 0 WHERE id = 10; 
UPDATE events SET seen = 1 WHERE author_id = 10 AND seen is NULL; 
COMMIT; 

當我執行,在phpMyAdmin的,它的工作原理也是如此。但是當我想要執行由PHP:

$stm = $dbh->prepare("START TRANSACTION; 
         UPDATE users SET events = 0 WHERE id = ?; 
         UPDATE events SET seen = 1 WHERE author_id = ? AND seen is NULL; 
         COMMIT;"); 
$stm->execute(array($user_id, $user_id)); 

它拋出一個錯誤:

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[42000]: Syntax error or access violation: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'UPDATE users SET events = 0 WHERE id = ?; UPDATE events SET seen = 1 ' at line 2' in C:\xampp\htdocs\myweb\really_test.php:306 Stack trace: #0 C:\xampp\htdocs\myweb\really_test.php(306): PDO->prepare('START TRANSACTI...') #1 {main} thrown in C:\xampp\htdocs\myweb\really_test.php on line 306

我怎樣才能解決這個問題?

+0

您需要一個圍繞您的查詢的過程。 –

+0

@juergend爲什麼?我從來沒有使用過程迄今,一切都很好, –

+0

http://php.net/manual/en/pdo.transactions.php你想'beginTransaction()'和'commit()'。 –

回答

5

PDO::ATTR_EMULATE_PREPARES關閉時,PDO不支持多個查詢。在PHP 5.5.21+中,有一個驅動程序特定的常量,用於在PDO::queryPDO::prepare之間打開/關閉多重查詢,其名稱爲PDO::MYSQL_ATTR_MULTI_STATEMENTS

但是......

因爲要交易它的安全依賴於接口的方法(PDO::beingTrasaction()PDO::commit()PDO::rollBack()),而不是通過你的SQL代碼這樣做。首先,讓我演示如何使用多個語句對象來防止模擬準備關閉。

使用多個PDO Statement對象

// prepare the statements for the transaction 
$stm1 = $dbh->prepare("UPDATE users SET events = 0 WHERE id = ?"); 
$stm2 = $dbh->prepare("UPDATE events SET seen = 1 WHERE author_id = ? AND seen is NULL"); 
// Make sure PDO is in exception mode 
$dbh->setAttribute (PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
try { 
    // begin the transaction here you can rollback from the catch block 
    $dbh->beginTransaction(); 
    $stmt1->execute([$user_id]); 
    $stmt2->execute([$user_id]); 
    $dbh->commit(); // all went OK commit 
} catch(PDOException $e) { 
    // something went wrong: roll back and handle errors here 
    $dbh->rollBack(); 
} 

有關詳細信息,請參閱PHP manual on PDO Transactions and auto-commit

使用單個PDO語句對象

要使用我們需要使用模擬多個查詢準備一個準備好的聲明中做到這一點...

// These driver options need to be set in the constructor 
$opts = [ 
    PDO::ATTR_EMULATE_PREPARES => true, 
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, 
    PDO::MYSQL_ATTR_MULTI_STATEMENTS => true, 
]; 
$dbh = new PDO("mysql:host=localhost;dbname=$dbname", $username, $pass, $opts); 

// prepare the statements for the transaction 
$stmt = $dbh->prepare(
    "UPDATE users SET events = 0 WHERE id = ?;" . 
    "UPDATE events SET seen = 1 WHERE author_id = ? AND seen is NULL;" 
); 


try { 
    // begin the transaction here you can rollback from the catch block 
    $dbh->beginTransaction(); 
    $stmt->execute([$user_id, $user_id]); 
    $stmt->nextRowSet(); // move to the next rowset 
    $dbh->commit(); // all went OK commit 
} catch(PDOException $e) { 
    // something went wrong: roll back and handle errors here 
    $dbh->rollBack(); 
} 
+0

謝謝.. upvote。你爲什麼通過'$ id'那樣?爲什麼不使用''''或':id'等準備語句? –

+0

我剛剛從手冊中複製了示例。我幾乎從不使用事務,甚至不確定它們是否與準備好的語句以相同的方式工作。您可能需要仔細閱讀該手冊。我知道它有各種各樣的變體,取決於司機。 – Sherif

+0

第二個想法,我只是試了一下,似乎它工作正常(至少在我根據我非常原始的測試的MySQL)。 – Sherif

1

有解決你的問題兩種方式。

其中一個就像在另一個答案,但沒有那麼大驚小怪。如果仿真關閉,那麼您不能一次運行多個查詢,因此必須逐個運行它們。這只是多個查詢集的任何的一般規則。

儘管建議使用PDO的內置命令進行事務處理,但仍然可以使用原始SQL執行此操作。剛剛拆分批量語句轉換成單獨的查詢:

$dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, TRUE); 

這樣你的多個查詢的語句會被執行沒事:

try { 
    $dbh->query("START TRANSACTION"); 
    $dbh->prepare("UPDATE users SET events = 0 WHERE id = ?")->execute([$user_id]); 
    $dbh->prepare("UPDATE events SET seen = 1 WHERE author_id = ? AND seen is NULL")->execute([$user_id]); 
    $dbh->query("COMMIT"); 
} catch(PDOException $e) { 
    // something went wrong: roll back and handle errors here 
    $dbh->rollBack(); 
    // ALWAYS re-throw an exception or you will never know what went wrong 
    throw $e; 
} 

另一種方式是恰好在開啓仿真。

+1

第三種方式呢? ;-) – Strawberry

+0

謝謝.. Upvote –

+0

多語句是一個當然不應該在任何情況下使用的功能。說真的,永遠不要使用這個選項。 ['羅伯特'; DROP TABLE學生; - '](http://xkcd.com/327/)應該足以說明。 –