日期不工作的 PDO bindParam

PDO bindParam for date not working

我是 PDO 的初学者,我正在制作一个 php 函数来 return 航班搜索结果,这是我的代码:

$db = DB::getConnection();
$stmt = $db->prepare("SELECT * FROM `flights` WHERE `date` BETWEEN :befDate AND :aftDate 
                     AND `from` = :from 
                     AND `to` = :to 
                     AND `weight` >= :weight");
$stmt->bindParam(':befDate', $befDate, PDO::PARAM_STR);    //$befDate = '2016-07-21';
$stmt->bindParam(':aftDate', $aftDate, PDO::PARAM_STR);   //$aftDate = '2016-07-28';
$stmt->bindParam(':from', $from, PDO::PARAM_INT);
$stmt->bindParam(':to', $to, PDO::PARAM_INT);
$stmt->bindParam(':weight', $weight, PDO::PARAM_INT);
$ok = $stmt->execute();
if ($ok) {
    if ($stmt->fetchColumn()>=1) {
        $result = $stmt->fetchAll();
    }
    else{
        $result = 'nope';
    }
  return $result;
}
else{
  return false;
}

问题是,它总是 returning 0 个搜索结果。我尝试 运行 我的 SQL 通过 phpMyAdmin 中的函数手动生成,发现问题是因为 PDO 生成的 SQL 是:

"SELECT * FROM `FLIGHTS` WHERE `DATE` BETWEEN 2016-07-17 AND 2016-07-25 AND `FROM` = 1237 AND `TO` = 2380 AND `WEIGHT` >= 4"

而我得到结果的正确 SQL 应该是:

"SELECT * FROM `FLIGHTS` WHERE `DATE` BETWEEN '2016-07-17' AND '2016-07-25' AND `FROM` = 1237 AND `TO` = 2380 AND `WEIGHT` >= 4"

即,在单引号之间包含日期值。现在,如果我在 PDO 中为我的 SQL 添加引号,例如:

$stmt = $db->prepare("SELECT * FROM `flights` WHERE `date` BETWEEN ':befDate' AND ':aftDate' 
                         AND `from` = :from 
                         AND `to` = :to 
                         AND `weight` >= :weight");

我收到 "Invalid parameter number: number of bound variables does not match number of tokens" 错误。 提前感谢您的帮助!

更新:

我的"flights"table结构是:

CREATE TABLE `flights` (
  `fid` int(30) NOT NULL,
  `user_id` int(30) NOT NULL,
  `date` date NOT NULL,
  `from` int(30) NOT NULL,
  `to` int(30) NOT NULL,
  `weight` int(30) NOT NULL,
  `size` varchar(30) NOT NULL,
  `details` varchar(200) NOT NULL,
  `price` int(50) NOT NULL,
  `airline` varchar(100) NOT NULL,
  `pnr` varchar(100) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

我尝试从查询中删除所有引号并将其也放在一个完整的行中:

$stmt = $db->prepare("SELECT * FROM flights WHERE date BETWEEN :befDate AND :aftDate AND from = :from AND to = :to AND weight >= :weight");

但还是不行...

更新 2 为了确定 SQL 语句在将参数与 PDO 绑定后的样子(没有单引号的语句),我在函数的开头创建了一个与我的 SQL 相同的会话变量,并回显它以查看结果:

$_SESSION['err'] = "SELECT * FROM flights WHERE date BETWEEN $befDate AND $aftDate 
                         AND from = $from 
                         AND to = $to 
                         AND weight >= $weight";

这是您的主要问题:

if ($stmt->fetchColumn()>=1) {
    $result = $stmt->fetchAll();
}
else{
    $result = 'nope';
}

调用 fetchColumn() 使结果集超过其第一行。然后,当您调用 fetchAll() 时,它只会获取 remaining 行。它不能返回并获取第一行,那已经丢失了。因此,如果您的查询结果只有一行,您将永远看不到它。

相反,我建议使用以下代码:

$result = $stmt->fetchAll();
if (empty($result)) {
  $result = "nope";
}

其他提示:

切勿将参数占位符放在引号内。如果这样做,它们就不再是参数占位符,它们只是像“:befDate”这样的文字字符串。这些不是有效的日期文字。

BETWEEN :befDate AND :aftDate 等表达式中的参数不会生成 BETWEEN 2016-07-17 AND 2016-07-25 作为查询。参数永远不会成为那样的表达式,它们总是成为每个参数的标量值(例如带引号的日期文字)。

我试过你的代码。首先我启用了 MySQL 一般查询日志:

mysql> SET GLOBAL general_log = ON;

现在我可以准确地看到 MySQL 认为是 PDO 提交的查询。我 运行 PHP 脚本,并阅读我的一般查询日志(/var/lib/mysql/localhost.log 在我的虚拟机上):

160716 19:26:16     8 Connect   root@localhost on test
            8 Query SELECT * FROM `flights` WHERE `date` BETWEEN NULL AND NULL 
                     AND `from` = NULL 
                     AND `to` = NULL 
                     AND `weight` >= NULL
            8 Quit  

啊,我忘记给参数绑定的变量设置值了。如果这些变量中的任何一个都没有值,这将解释为什么结果为空,因为与 NULL 的任何比较都是不正确的。所以我编辑了 PHP 以首先将示例值设置为变量。

$befDate = '2016-07-21';
$aftDate = '2016-07-28';
$from = 1;
$to = 2;
$weight = 10;

我再次运行查询,在日志中我看到以下内容:

160716 19:33:17    13 Query SELECT * FROM `flights` WHERE `date` BETWEEN '2016-07-21' AND '2016-07-28' 
                     AND `from` = 1 
                     AND `to` = 2 
                     AND `weight` >= 10

这证明 PDO 确实在参数化值周围加上引号(如果它是字符串或日期)。