2016-03-12 192 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); 
    } 
}