Doctrine2在选择时有超过1000行的问题


Doctrine2 has problems with more than 1000 rows at select

我有这个查询,返回我在给定区域的所有poi:

    $query = $em
            ->createQuery(
                    'SELECT f FROM MyApplication'MyBundle'Entity'POI p
            WHERE (p.latitude BETWEEN :southEastLatitude AND :norhtWestLatitude) AND
                  (p.longitude BETWEEN :southEastLongitude AND :norhtWestLongitude)
            ORDER BY p.name
    ');
    $query->setParameter(":norhtWestLatitude", $northWestLat);
    $query->setParameter(":norhtWestLongitude", $northWestLng);
    $query->setParameter(":southEastLatitude", $southEastLat);
    $query->setParameter(":southEastLongitude", $southEastLng);

如果我尝试访问一个小区域(参数与小的差异),我成功地得到了结果。我想我得到的结果高达1000行…我不太确定。

如果我尝试访问更大的区域,我得到一个空的结果集…

以某种方式使用更大区域的参数触发相同的查询,我得到了正确的结果集(~1060行)。

所以我想知道学说(甚至Symfony)的局限性??我在Symfony2项目中使用教条,有教条吗?我也试过$query->setMaxResults(999999999);,但它没有帮助…

有人遇到同样的问题吗?

编辑:也许php内存使用是高?我在getresult之前和之后添加了这些行:
echo "Memory usage before: " . (memory_get_usage() / 1024) . " KB" . PHP_EOL;
echo "Memory usage after: " . (memory_get_usage() / 1024) . " KB" . PHP_EOL;

输出为:

之后的内存使用情况:5964.0625 KB

修改后的内存使用情况:10019.421875 KB

编辑:

奇怪的测试:

1)如果我用这些参数进行测试:

WHERE
    (f.latitude BETWEEN 45.64273082966722 AND 47.29965978937995) AND
    (f.longitude BETWEEN 4.93262593696295 AND  9.99999999999999)

我得到923行(正常行为)

2)如果我将参数9.99999999999999更改为10.000000000(或大于9.9999999的某个数字),我将在应用程序中获得空结果集。数据库仍然返回923行(对于10.000000000)。

编辑:

我可以解决这个问题,这里讨论:https://groups.google.com/d/msg/doctrine-user/qLSon6m4nM4/y5vLztHcbDgJ

从Doctrine2邮件列表中我了解到您将属性映射为类型"string":

/** @ORM'Column(name="latitude", type="string", length=255, nullable=false) */
private $latitude;
/** @ORM'Column(name="longitude", type="string", length=255, nullable=false) */
private $longitude;

这意味着您的数据库将为这些属性提供VARCHAR列。因此,当数据库运行您的查询时,它将执行字符串比较。

字符串比较不同于(普通的)数字比较。查看这些结果:

$a = 1234567890;
$b = 987654321;
echo $a == $b ? '0' : ($a < $b ? '-1' : '1');   // output:  1 (means $a is bigger than $b)
echo strcmp( $a, $b );                          // output: -8 (means $a is smaller than $b)

因此,最好将属性映射为在数据库中表示数字的东西(DECIMAL将是一个不错的选择):

/** @ORM'Column(name="latitude", type="decimal", nullable=false) */
private $latitude;
/** @ORM'Column(name="longitude", type="decimal", nullable=false) */
private $longitude;

PS:您提到您自己执行查询时会得到有效的结果。这可能是因为您这样做了:

... latitude BETWEEN 45.64273082966722 AND 47.29965978937995 ...

但是Doctrine(因为您将属性映射为字符串)会这样做:

... latitude BETWEEN '45.64273082966722' AND '47.29965978937995' ...

注意这里的引号。这两个语句的处理方式有很大的不同,正如我的测试用例所示;)

这可能与内存使用有关。请检查apache日志是否有内存错误。

在选择大量数据时,如本文所指出的,使用$query->iterate()代替$query->getResult()可能会有所帮助。

为了检查您的查询是否生成了正确的SQL,您可以记录或以其他方式输出调用$query->getSQL()的结果(在设置参数之后)。将生成的SQL提供给您选择的数据库客户机,看看是否返回所需的结果。