我是PDO的初学者,我在做一个php函数来返回航班的搜索结果,这是我的代码:
$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;
}
问题是,它总是返回0个搜索结果。我试图在phpMyAdmin中手动运行通过函数生成的SQL,发现问题在于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");
我得到“无效参数号:绑定变量数与令牌数不匹配”错误。
谢谢你的帮助!
更新:
我的“flights”表结构是:
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
为了确定将params与PDO(不带单引号的语句)绑定后SQL语句的外观,我在函数的开头创建了一个与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()时,它只获取剩余的行。它不能回去取第一排,那是丢失的。因此,如果查询结果只有一行,就永远看不到它。
相反,我建议使用以下代码:
$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 on my virtual machine):
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确实在参数化的值(如果是字符串或日期)周围加了引号。