2016-03-12 203 views
3

有3層不同的過濾器:booksauthorsstoresselect列表),我可以使用他們一起一次或者只有一兩個人,所以我使用UNION讓所有查詢一起綁定多個陣列

require('database.php'); 

if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
} 

$query = ""; 

if(!empty($books_ids)) 
{ 
    $books_ids_in = implode(',', array_fill(0, count($books_ids), '?')); 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (". $books_ids_in .") 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 

if(!empty($authors_ids)) 
{ 
    $authors_ids_in = implode(',', array_fill(0, count($authors_ids), '?')); 

    if (!empty($query)) { 
     $query .= " UNION "; 
    } 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'author' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (
      SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (". $authors_ids_in .") 
      ) 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 

if(!empty($stores_ids)) 
{ 
    $stores_ids_in = implode(',', array_fill(0, count($stores_ids), '?')); 

    if (!empty($query)) { 
     $query .= " UNION "; 
    } 

    $query .= "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'store' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id 
    WHERE 
     b.id IN (
      SELECT DISTINCT book_id FROM books_stores WHERE store_id IN (". $stores_ids_in .") 
      ) 
    GROUP BY b.id 
    ORDER BY b.id"; 
} 


if(!empty($query)) { 

    $stmt = $conn->prepare($query); 

    if(!empty($books_ids)) 
    { 
     foreach ($books_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    if(!empty($authors_ids)) 
    { 
     foreach ($authors_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    if(!empty($stores_ids)) 
    { 
     foreach ($stores_ids as $k => $id) { 
      $stmt->bindValue(($k+1), $id); 
     } 
    } 

    $stmt->execute(); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

$conn = null; 

代碼工作得很好,當我只用一個過濾器,但是當我嘗試使用2個或更多,我得到錯誤

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens' in C:\xampp\htdocs\bookstore\filter.php:123 Stack trace: #0 C:\xampp\htdocs\bookstore\filter.php(123): PDOStatement->execute() #1 {main} thrown in C:\xampp\htdocs\bookstore\filter.php on line 123

我想,什麼是錯的與bindValue使用,但我不知道如何解決這個問題?

UPD var_dump($query)(3本書和2名作者選擇)
string(1097) "SELECT b.id, b., b., GROUP_CONCAT(DISTINCT a.) AS author_names, GROUP_CONCAT(DISTINCT s.) AS store_names, 'book' as param FROM books AS b LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id LEFT JOIN authors AS a ON a.id = b_a.author_id LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id LEFT JOIN stores AS s ON s.id = b_s.store_id WHERE b.id IN (?,?,?) GROUP BY b.id ORDER BY b.id UNION SELECT b.id, b., b., GROUP_CONCAT(DISTINCT a.) AS author_names, GROUP_CONCAT(DISTINCT s.) AS store_names, 'author' as param FROM books AS b LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id LEFT JOIN authors AS a ON a.id = b_a.author_id LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id LEFT JOIN stores AS s ON s.id = b_s.store_id WHERE b.id IN (SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (?,?)) GROUP BY b.id ORDER BY b.id" 01201

+0

該錯誤意味着你有更少/更多的綁定值比預期的查詢。我會回顯出每個bindValue循環的計數來找到罪魁禍首。 –

+0

不,參數的數量與查詢中的數量相同,我已經檢查過。我選擇了3本書和2位作者,並在我的查詢WHERE b.id IN(?,?,?)和WHERE author_id IN(?,?))中查詢了var_dump($ query);' – Heidel

+0

var_dump並顯示結果請 –

回答

3

有你的代碼問題,爲構建動態查詢。 構建動態查詢時,需要將查詢中那些靜態的部分與動態的部分分開。

你可以看到下面的代碼是靜態的。

$query = "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id "; 

而且還

" GROUP BY b.id 
    ORDER BY b.id"; 

的代碼的其餘部分是動態的。 過濾記錄時,使用WHERE子句,並使用OR運算符根據多個條件篩選記錄。 如果第一個條件和第二個條件都爲真,AND運算符將顯示記錄。 如果第一個條件或第二個條件爲真,則OR運算符顯示記錄。 所以在使用的第一個條件,但之後AND或OR必須使用(使用或在您的例子)

// Static code 
sql = "SELECT * FROM `table`" 
// Set initial condition to WHERE  
clause = "WHERE";  
if(!empty(filter)){ 
    Add clause to sql 
    Add condition to sql 
    change clause to OR or AND as required 
} 
Repeat for each filter 
Note the filter is not changed until a filter is not empty and remains changed once changed. 
The remaining static code is added after all the filters have been handled 

要允許不同的過濾器被應用,你可以使用一個flag

$flag = 0; 
if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
    $flag += 1; 

} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
    $flag += 10; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
    $flag += 100; 
} 

儘可能地使用「懶惰」綁定 - 將數據傳遞到執行將顯着縮短您的代碼。 請參閱PDO info 您需要合併數組才能執行此操作。使用switch語句和標誌合併所需的數組。

switch ($flag) { 
    case 1: 
     $param_array = $books_ids; 
     break; 
    case 10: 
     $param_array = $authors_ids; 
     break; 
    case 100: 
     $param_array = $stores_ids; 
     break; 
    case 11://books & authors 
     $param_array = array_merge($books_ids, $authors_ids); 
     break; 
    case 101://books & stores 
     $param_array = array_merge($books_ids, $stores_ids); 
     break; 
    case 110://authors & stores 
     $param_array = array_merge($authors_ids, $stores_ids); 
     break; 
    case 111://books & authors & stores 
     $param_array = array_merge(array_merge($books_ids,$authors_ids),$stores_ids); 
     break; 

} 

if(!empty($query)) { 
    $stmt = $conn->prepare($query); 
    $stmt->execute($param_array); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

以下代碼使用了上述幾點。我回應了一些行,表明一旦完成測試就可以刪除結果。還有一些代碼已被註釋掉以供測試。

//Set flag 
$flag = 0; 
if(isset($_POST['books'])){ 
    $books_ids = $_POST["books"]; 
    $flag += 1; 
} 
if(isset($_POST['authors'])){ 
    $authors_ids = $_POST["authors"]; 
    $flag += 10; 
} 
if(isset($_POST['stores'])){ 
    $stores_ids = $_POST["stores"]; 
    $flag += 100; 
} 
echo $flag. " <BR>";//Remove after testing 

//Basic SQL statement 
$query = "SELECT 
     b.id, 
     b.`name`, 
     b.`year`, 
     GROUP_CONCAT(DISTINCT a.`name`) AS author_names, 
     GROUP_CONCAT(DISTINCT s.`name`) AS store_names, 
     'book' as param 
    FROM 
     books AS b 
     LEFT JOIN books_authors AS b_a ON b.id = b_a.book_id 
     LEFT JOIN authors AS a ON a.id = b_a.author_id 
     LEFT JOIN books_stores AS b_s ON b.id = b_s.book_id 
     LEFT JOIN stores AS s ON s.id = b_s.store_id "; 
// Set initial condition to WHERE  
$clause = "WHERE";  

if(!empty($books_ids)) 
{ 
    $books_ids_in = implode(',', array_fill(0, count($books_ids), '?')); 
    $query .= $clause; 
    $query .= " b.id IN (". $books_ids_in .")"; 
    // Set condition to OR for additional condition 
    $clause = " OR "; 
} 
if(!empty($authors_ids)) 
{ 
    $authors_ids_in = implode(',', array_fill(0, count($authors_ids), '?')); 

    /* This part commented out as I don't see relevance 
     if (!empty($query)) { 
     $query .= " UNION "; 
    } 
    */ 
    $query .= $clause; 
    $query .= " b.id IN (
      SELECT DISTINCT book_id FROM books_authors WHERE author_id IN (". $authors_ids_in .") 
      )"; 
    // Set condition to OR for additional condition  
    $clause = " OR ";  

} 


if(!empty($stores_ids)) 
{ 
    $stores_ids_in = implode(',', array_fill(0, count($stores_ids), '?')); 

    /* if (!empty($query)) { 
     $query .= " UNION "; 
    } 
    */ 
    $query .= $clause; 
    $query .= " b.id IN (
      SELECT DISTINCT book_id FROM books_stores WHERE store_id IN (". $stores_ids_in .") 
      )"; 
    $clause = " OR "; 
} 

//Add GROUP & ORDER 
    $query .= " GROUP BY b.id 
    ORDER BY b.id"; 
echo $query; //Remove after testing 
//building $param_array 
switch ($flag) { 
    case 1: 
     $param_array = $books_ids; 
     break; 
    case 10: 
     $param_array = $authors_ids; 
     break; 
    case 100: 
     $param_array = $stores_ids; 
     break; 
    case 11://books & authors 
     $param_array = array_merge($books_ids, $authors_ids); 
     break; 
    case 101://books & stores 
     $param_array = array_merge($books_ids, $stores_ids); 
     break; 
    case 110://authors & stores 
     $param_array = array_merge($authors_ids, $stores_ids); 
     break; 
    case 111://books & authors & stores 
     $param_array = array_merge(array_merge($books_ids,$authors_ids),$stores_ids); 
     break; 

} 
echo "<br>"; 
print_r($param_array);// remove after testing 
/* 
if(!empty($query)) { 
    $stmt = $conn->prepare($query); 
    $stmt->execute($param_array); 
    $results = $stmt->fetchAll(); 
    echo json_encode($results); 
} 

$conn = null; 
+0

問題是,我可以使用所有3個過濾器,或者只使用其中的1個或2個,所以我可以將'$ books_ids','$ authors_ids'和$ stores_ids'數組放在一起,或者只放置1或2個。在這種情況下,我有錯誤,例如:注意:未定義的變量:books_ids,位於第130行的C:\ xampp \ htdocs \ bookstore \ filter.php上012警察:array_merge():參數#1不是C:\中的數組。 130行上的xampp \ htdocs \ bookstore \ filter.php – Heidel

+0

@ Heidel已添加代碼進行回答。 –

+0

@海德爾我已經更新了答案 –

0

不要使用相同的$ k;使用一個變量並在每次綁定時增加它;見下面

$bindingIndex = 0; 
if(!empty($books_ids)) 
{ 
    foreach ($books_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
} 

if(!empty($authors_ids)) 
{ 
    foreach ($authors_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
} 

if(!empty($stores_ids)) 
{ 
    foreach ($stores_ids as $k => $id) { 
     $stmt->bindValue((++$bindingIndex), $id); 
    } 
}