摘要
我正在使用PHP和PDO运行MySQL查询。有时,它会返回我期望的结果。其他时候,它什么也没有返回。
解决方案
由于这显然是一个完全不同的问题的重复,我会在这里提出正确答案。
当前的问题出在json_encode
函数中。从this SO answer开始,在编码函数之后立即使用echo json_last_error_msg()
提供错误"格式错误的UTF-8字符,可能是错误编码的",这暗示了潜在的问题。
请注意,似乎没有办法自动输出这些错误。这不是PHP服务器错误报告的问题,而是简单地忽略了如何检查JSON错误日志,这必须手动完成。
另一个有用的技巧是JSON_PARTIAL_OUTPUT_ON_ERROR选项,例如echo json_encode($array, JSON_PARTIAL_OUTPUT_ON_ERROR);
。这为大多数事物提供数据,同时静默地将错误值转换为NULL。它并不理想,但至少有助于缩小有问题的数据。
链接的SO页面上的其他答案也表明错误的字符集可能是罪魁祸首,但不幸的是,所有答案都是针对较旧的连接方法,而不是PDO。
幸运的是,PHP PDO::__construct page上提供了正确的答案,作为用户 Kiipa在live dot com上的评论:
要获取UTF-8字符集,您可以在DSN中指定。
$ link = new PDO(" mysql:host = localhost; dbname = DB; charset = UTF8 ");
将加粗的charset=UTF8
放入DSN字符串可以解决所有问题。
设置
我将XAMPP 3.2.2与Apache 2.4.23(Win32)/ OpenSSL 1.0.2h / PHP 5.6.24用于Web服务器,将MariaDB 10.1.16用于数据库服务器。我在Firefox 50.0上使用localhost进行测试。
问题
我有一个网页,它使用AJAX根据用户输入查询数据库。用户可以键入名字,姓氏和电影标题,MySQL查询返回与搜索结果匹配的字符角色列表。它工作得相当好,但我发现了一些奇怪的限制。
如果我输入所有空字段,它将找到前50个结果(或我用于限制的任何数字)。如果我输入标题的第一个字母或两个标题,则不返回任何内容。如果我输入第三个或第四个字母,它会再次返回有意义的结果。如果我将行限制设置为5左右,它突然用于两个字母的搜索。删除查询中有关与actor表的连接的所有内容,使其有效。
通常,任何会增加执行时间的事情都会导致查询失败,但服务器在超时之前有30秒的执行窗口,这些查询需要半秒钟。我还成功运行了需要5到10秒才能运行的查询。
PHP代码
我已经创建了一个虚拟文件index.php,用于在没有HTML,Javascript等的情况下进行测试。应该回显一个JSON编码的数组。有时会这样,有时候只是一个空页。
<?php
// These would normally be set using POST variables and AJAX.
// Hard-code them for this example.
$title = "I";
$first = "Ki";
$last = "D";
$rowLimit = 50;
searchMovies($title, $first, $last, $rowLimit);
function searchMovies($title, $first, $last, $rowLimit)
{
try
{
// Connect to the database.
$db = "mysql:dbname=imdb;host=127.0.0.1";
$user = "root";
$password = "";
$DB = new PDO($db, $user, $password);
$DB->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Add wildcards for searching.
$title = $title . "%";
$first = $first . "%";
$last = $last . "%";
// Setup the query.
$query = "SELECT mov.name, CONCAT(act.first_name, ' ', act.last_name) as actor, mov.year
FROM roles
INNER JOIN
(
SELECT id, name, year
FROM movies
WHERE name LIKE :title
) mov
ON roles.movie_id = mov.id
INNER JOIN
(
SELECT id, first_name, last_name
FROM actors
WHERE first_name LIKE :first
AND last_name LIKE :last
) act
ON roles.actor_id = act.id
ORDER BY mov.year DESC, mov.name ASC
LIMIT :limit";
// Prepare and execute.
$stmt = $DB->prepare($query);
$stmt->bindParam(":title", $title, PDO::PARAM_STR);
$stmt->bindParam(":first", $first, PDO::PARAM_STR);
$stmt->bindParam(":last", $last, PDO::PARAM_STR);
$stmt->bindParam(":limit", $rowLimit, PDO::PARAM_INT);
$stmt->execute();
$array = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Just echo the array to the page.
echo json_encode($array);
}
catch (PDOException $ex)
{
// Oh noes!
echo $ex->getMessage();
}
}
?>
MySQL设置
我可以找到我正在使用的数据库here。它是IMDB完整版,大小约为50 MB。还有一个奇怪的问题,你必须创建一个数据库来导入它,而不是.sql文件为你创建一个.sql文件。我命名了我的数据库&#39; imdb&#39;使用我的PHP代码正常工作。
有问题的表格是:
电影:id(int11,PK,NN),名称(varchar100),年份(int11),等级(浮动)
actor:id(int11,PK,NN),first_name(varchar100),last_name(varchar100),性别(char1),film_count(int11)
角色:actor_id(int11),movie_id(int11),角色(varchar100)
我查询角色表,然后将其与电影和演员表连接,这样我就可以显示角色的演员姓名,电影名称和发行年份。此外,我过滤电影和演员表仅在其电影的名称与用户输入匹配且其演员的名字和姓氏与用户输入匹配时才显示角色。
具体示例
在角色表中有一条记录,其中actor_id = 608478,movie_id = 161108,role =&#39; Claudia&#39;。它匹配actor记录与actor_id = 608478,first_name =&#39; Kirsten&#39;,last_name =&#39; Dunst&#39;,gender =&#39; F&#39;,film_count = 57。并且它与movie_id = 161108匹配电影记录,名称=&#39;采访吸血鬼:吸血鬼编年史&#39;,年= 1994,等级= 7.1。
我将名字和姓氏合并到一个演员字段中并忽略了大部分字段,让我看起来像名字=&#39;采访吸血鬼:吸血鬼编年史&#39;,演员=&# 39; Kirsten Dunst&#39;,year = 1994。
搜索$ title =&#34; Inter&#34;,$ first =&#34; Kir&#34;,$ last =&#34; D&#34;应该返回此记录(确实如此)。
搜索$ title =&#34;我&#34;,$ first =&#34; Ki&#34;,$ last =&#34; D&#34;应该返回这个记录加上另外23个记录(例如,King Donovan在1956年版入侵身体抢夺者中出现的Jack Belicec角色);相反,它什么都不返回。
查看我的MySQL常规日志,我看到服务器执行了语句:
SELECT mov.name, CONCAT(act.first_name, ' ', act.last_name) as actor, mov.year
FROM roles
INNER JOIN
(
SELECT id, name, year
FROM movies
WHERE name LIKE 'I%'
) mov
ON roles.movie_id = mov.id
INNER JOIN
(
SELECT id, first_name, last_name
FROM actors
WHERE first_name LIKE 'Ki%'
AND last_name LIKE 'D%'
) act
ON roles.actor_id = act.id
ORDER BY mov.year DESC, mov.name ASC
LIMIT 50
当我从MySQL Workbench查询窗口运行语句时,返回我期望的24行而没有问题。
其他尝试修复
1.将数据库连接字符串设置为&#34; localhost&#34; vs.&#34; 127.0.0.1&#34;没有明显区别。
2.将PHP变量直接连接到$ query字符串没有区别
对电脑大喊大叫让狗看着我好笑,但对手头的问题没有帮助。
4.改变&#34; INNER JOIN&#34;到&#34;加入&#34;没有区别。
5.改为&#34; LEFT JOIN&#34;打破它。 &#34; RIGHT JOIN&#34;显然不对。
6.删除&#34; LIMIT&#34;条款没有区别,除了显而易见的。