2010-04-26 56 views
7

我一直在使用PDO並且主要是爲了安全原因準備我的所有陳述。然而,我有一部分代碼確實多次用不同的參數執行相同的語句,我認爲這將是準備好的語句真正發光的地方。但他們實際上破壞了代碼...準備好的陳述中的遞歸

代碼的基本邏輯是這樣的。

function someFunction($something) { 
    global $pdo; 

    $array = array(); 

    static $handle = null; 
    if (!$handle) { 
    $handle = $pdo->prepare("A STATEMENT WITH :a_param"); 
    } 

    $handle->bindValue(":a_param", $something); 
    if ($handle->execute()) { 
    while ($row = $handle->fetch()) { 
     $array[] = someFunction($row['blah']); 
    } 
    } 

    return $array; 
} 

它看起來很好,但它錯過了很多行。最終,我意識到語句句柄正在被改變(用不同的參數執行),這意味着在while循環中獲取的調用只能工作一次,然後函數再次調用自己,並且結果集被改變。

所以我想知道什麼是以遞歸方式使用PDO準備語句的最佳方式。

一種方法可能是使用fetchAll(),但它在手冊中說有很大的開銷。這整個意義在於讓它更有效率。

我可以做的另一件事是不重用一個靜態句柄,而是每次都做一個新的。我相信由於查詢字符串是相同的,因此MySQL內部的驅動程序無論如何都會使用準備好的語句,所以在每次遞歸調用時創建一個新句柄的開銷很小。就我個人而言,我認爲這打破了這一點。

或者有什麼方法可以重寫?

+0

我只是做了一些(不是超級科學)測試我非常相似的遞歸函數代碼,並有濃對於我的目的而言,準備好的陳述總體上較慢。如果您在每次使用前必須重新準備,那麼準備一份聲明有什麼意義?我肯定錯過了什麼。 – 2012-11-14 19:32:13

回答

2

不能嵌套語句句柄:在單個會話中打開另一個句柄之前,需要先關閉先前打開的句柄。

實際上,PDO會在您發出新準備時自動執行。

當你調用的函數遞歸:

  • 最初的手柄被分配(1)
  • 第一個記錄是牽強的(1)
  • 函數被遞歸調用。 (1)的值駐留在遞歸堆棧中。
  • 新的手柄被分配(2)無效(1)
  • 第一個記錄是牽強的(2)
  • 該函數返回
  • 你嘗試獲取的(1)下一個記錄和失敗,因爲它是無效的

因此說,MySQL不支持它的遞歸,這意味着你必須在PHP一邊,使用fetchAll

+0

你指的是什麼手柄?你可以在一個會話中有很多準備好的語句句柄(我認爲每個會話有大約500個服務器端準備語句的限制,總共有max_prepared_stmt_count'語句,但這不適用於使用客戶端庫創建的預準備語句)。結果沒有得到他們自己的處理。 – outis 2010-04-28 00:03:57

0

真正的問題是$handle是靜態的。當狀態需要在遞歸調用中保留時,靜態變量對於遞歸是有問題的,而不僅僅是預處理語句。在這種情況下,遞歸調用執行一個新的查詢,放棄以前的狀態。如果你想要一個準備好的查詢,PDO::fetchAll確實是唯一的選擇。

根據聲明的內容,您可能會重寫它以一次返回所有結果,然後再構建樹。

+0

@outis:'$ handle'是靜態的,原因是:會話中只能有一個活動語句。 – Quassnoi 2010-04-27 08:55:32

+0

@Quassnoi:我意識到'$ handle'是靜態的原因。但是,靜態變量根本不會像Rob希望他們在遞歸調用中那樣工作。 – outis 2010-04-27 23:45:58

+0

@Quassnoi:以及「主動」是什麼意思?在單個會話中可以有許多預處理語句,但每個語句只能保存一個結果集。 – outis 2010-04-28 00:12:02

0

如果您使用相同的變量(由於pdo bindValue),每次的值與第一個值相同。因此,這將失敗:

foreach ($bind_params as $key => $value) { 
    $stmt->bindParam(":$key", $value); 
} 

結果:

$key[0] = $value[0]; 
$key[1] = $value[0]; 
$key[2] = $value[0]; 
$key[3] = $value[0]; 

所以,你想要做的醜陋伎倆,那麼:

 $i = 0; 
     foreach ($bind_params as $key => $value) { 
      $i++; 
      $$i = $value; 
      $stmt->bindParam(":$key", $$i); 
     } 

結果:

$key[0] = $value[0]; 
$key[1] = $value[1]; 
$key[2] = $value[2]; 
$key[3] = $value[3];