【问题标题】:Query after query in pdo? Binding problems [duplicate]在pdo中查询后查询?绑定问题[重复]
【发布时间】:2021-04-28 19:26:35
【问题描述】:

我对 PDO 有一个疑问。 我在类中有一个方法,它从数据库返回数据以用于发送的过滤器。 我想获得该查询的行数,但查询中有 LIMIT 和 STAR。 因此,正因为如此,我使用两个查询来获取许多行和数据,但要工作,我需要绑定相同的值两次。有没有更优雅的方式来实现不重复代码? 我使用的方法如下。

        $db = $this->openConnection();
        $sql = " SELECT * FROM contacts";
        
        // Filter data by main search input
        if(!empty($search_query)){
            $sql .= " WHERE ( location LIKE :search_query_location OR address LIKE :search_query_address ) ";
        } 

        $sql .=" ORDER BY ".$order;
        $stmt = $db->prepare($sql);

        if(!empty($search_query)){
            $stmt->bindValue(':search_query_location', (string) $search_query.'%');
            $stmt->bindValue(':search_query_address', (string) $search_query.'%');
        }
       
        // Get number of rows after filter
        $stmt->execute();
        $total = $stmt->rowCount();


        $sql .=" LIMIT :start, :limit_num";
        $stmt = $db->prepare($sql);

        if(!empty($search_query)){
            $stmt->bindValue(':search_query_location', (string) $search_query.'%');
            $stmt->bindValue(':search_query_address', (string) $search_query.'%');
        }
      
        // Bind start and limit value
        $stmt->bindValue(':start', (int) $start, PDO::PARAM_INT);
        $stmt->bindValue(':limit_num', (int) $limit, PDO::PARAM_INT);

        // Get filtered data 
        $stmt->execute();
        $data = $stmt->fetchAll(PDO::FETCH_ASSOC);

        return array($total,$data);

为什么我需要为两个相同的查询重复绑定一个没有限制的工作是否有任何优雅的解决方案

【问题讨论】:

  • 你需要绑定两次,因为你绑定的是两个不同的对象...

标签: php mysql sql pdo


【解决方案1】:

问题

您必须绑定两次的原因是$pdo->prepare($sql) 返回一个PDOStatement,它在设置后不可编辑。所以当你更新它时,你必须覆盖它并重新开始......显然新语句不会保留旧的绑定参数。

如果您将其视为一个向其中添加一些数据然后用新的空白数组覆盖的数组...您将无法从原始数组中读取信息,因为它不存在于新的:

$array = [];

$array[] = 1;
$array[] = 2;
$array[] = 3;

var_dump($array);

/*
Output...

Array
(
    [0] => 1
    [1] => 2
    [2] => 3
)

*/


$array = [];

print_r($array);

/*
Output...

Array
(
)

*/

区别在于PDOStatement 是一个对象而不是一个数组。但它在功能上是一样的!

注意

  • 虽然$pdo->rowCount() 可能返回来自SELECT 查询的结果数量,但这并不能保证,因此通常最好不要使用它。
  • 无论如何我都不会用新查询覆盖变量...最好使用不同的变量名称,例如$countQuery$dataQuery

解决方案

因此,如果唯一的原因是您试图减少代码量,那么您可以使用许多解决方案。但是,这似乎不是代码高尔夫,那有什么关系呢?

解决方案 1

假设查询返回的不需要的结果数量不合理,那么您可以只从第一个查询返回数组并使用array_slice 代替第二个查询...

$pdo = $this->openConnection();
$sql = "SELECT * FROM contacts";

if($search_query){
    $sql .= " WHERE ( location LIKE :search_query_location OR address LIKE :search_query_address ) ";
} 

$sql  .= " ORDER BY :order";
$query = $pdo->prepare($sql);

if($search_query){
    $query->bindValue(':search_query_location', $search_query.'%');
    $query->bindValue(':search_query_address',  $search_query.'%');
}

$query->bindValue(':order', $order);
$query->execute();

$result = $query->fetchAll(PDO::FETCH_ASSOC);
$count  = count($result);

return [$count, array_slice($result, $start, $limit)];

解决方案 2

如果您担心可读性和代码维护,那么您应该记住:方法/函数通常具有相当具体的功能,例如...

  1. 返回匹配查询的行数
  2. 返回匹配查询的数据

实现这意味着您将每个查询都放在单独的函数中:

function countContacts(...)
{
    $sql   = 'SELECT count(*) FROM contacts WHERE ...';
    $query = $pdo->prepare($sql);
    $query->bindValue(...);
    $query->execute();
    return $query->fetchColumn();
}

function getContacts(...)
{
    $sql   = 'SELECT * FROM contacts WHERE ... ORDER BY ...  LIMIT ...';
    $query = $pdo->prepare($sql);
    $query->bindValue(...);
    $query->execute();
    return $result->fetchAll(PDO::FETCH_ASSOC);
}

解决方案 3

我不会使用它,但它在技术上解决了问题

您可以使用联合并同时运行两个查询,然后您可以使用模拟的准备好的语句(根据@Straberry 的回答)绑定一次...

尽管如此,这里的任何人都不会建议您在没有充分理由的情况下使用模拟的预处理语句。当然你可以使用普通的准备并使用不同的绑定参数名称。

无论如何,这都不是一个很好的解决方案。我不会用它。

$sql = "
    SELECT COUNT(*) as col1, null as col2, null as col3, null as col4, null as col5 FROM contacts WHERE ...
    UNTION
    SELECT col1, col2, col3, col4, col5 FROM contacts WHERE ... ORDER BY ... LIMIT ...
";

$query = $pdo->prepare($sql);
$query->bindValue(...);
$query->execute();

$result = $query->fetchAll(PDO::FETCH_ASSOC);

return [$result[0]["col1"], array_slice($result, 1)];

【讨论】:

  • 嘿,史蒂文,我要感谢您抽出宝贵的时间来写这个详细的答案。我喜欢你的第二个解决方案,我会考虑的。感谢NB建议我一定会把它改成我的代码。
猜你喜欢
  • 2012-06-22
  • 2010-09-29
  • 1970-01-01
  • 2018-10-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多