最近有一個批量更新數據庫表中某幾個字段的需求,在做這個需求的時候,使用了PDO做參數綁定,其中遇到了一個坑。
筆者已知的做批量更新有以下幾種方案:
1、逐條更新
這種是最簡單的方案,但無疑也是效率最低的方案。
2、CASE WHEN
類似如下的語句
UPDATE tbl_test SET val = CASE id WHEN 1 THEN 2 WHEN 2 THEN 3 END WHERE id IN(1, 2);
為了防止SQL注入,使用了PDO擴展綁定參數。上面的數字在一般情況下是變量,那麼就需要做參數綁定。剛開始是想著在IN的時候將id組成的字符串作為變量綁定過去,第一次實現的代碼如下:
1 <?php 2 $data = array(array('id' => 1, 'val' => 2), array('id' => 2, 'val' => 3)); 3 $ids = implode(',', array_map(function($v) {return $v['id'];}, $data)); //獲取ID數組 4 $update_sql = 'UPDATE tbl_test SET val = CASE id'; 5 $params = array(); 6 $params[":ids"] = $ids; 7 foreach($data as $key => $item) { 8 $update_sql .= "WHEN :id_" . $key . "THEN :val_" . $key . " "; 9 $params[":id_" . $key] = $item['id']; 10 $params[":val_" . $key] = $item['val']; 11 } 12 $update_sql .= "END WHERE id IN (:_ids)"; 13 TEST::execute($update_sql, $params);//此處會調用bindParam綁定參數
後來發現這樣是行不通的,而且比較詭異的是這樣只能更新第一條記錄。查閱資料後,發現這樣的綁定方式是不行的,IN語句的參數應該一個一個地綁定。看看文檔中對bindParam函數的描述:
可以看到,說明裡寫的是會綁定一個PHP變量到占位符裡,所以如果綁定了:ids為1, 2的字符串,那麼MySQL解析語句的時候會將1,2解析為單個的變量,而不會當作一串。這也是PDO防SQL注入的原理,通過占位符的綁定,只將綁定的值當作一個值,而不是語句之類的其它東西,這樣MySQL只會把傳遞過去的值當作一個變量的值。
修改後的寫法:
1 <?php 2 $data = array(array('id' => 1, 'val' => 2), array('id' => 2, 'val' => 3)); 3 $update_sql = 'UPDATE tbl_test SET val = CASE id'; 4 $params = array(); 5 $params[":ids"] = $ids; 6 $in_arr = array(); 7 8 foreach($data as $key => $item) { 9 $update_sql .= "WHEN :id_" . $key . "THEN :val_" . $key . " "; 10 $params[":id_" . $key] = $item['id']; 11 $params[":val_" . $key] = $item['val']; 12 $params[":ids_" . $key] = $item['id']; 13 array_push($in_arr, ":id_" . $key); 14 } 15 $update_sql .= "END WHERE id IN (" . implode(',' $in_arr) . ")"; 16 TEST::execute($update_sql, $params);//此處會調用bindParam綁定參數
這是最近遇到的一個小問題,其實更多的是說明在MySQL的IN語句裡面做參數綁定時應該一個一個的綁定。
參考鏈接:
mysql語句:批量更新多條記錄的不同值
Can I bind an array to an IN() condition?
原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。
如果本文對你有幫助,請點下推薦,寫文章不容易。