使用whereHas方法的Laravel Eloquent查询的奇怪行为

时间:2015-01-28 02:48:19

标签: php mysql laravel laravel-4 eloquent

我无法弄清楚我的Laravel脚本发生了什么。我只是选择一个对象,并在其上调用一个运行其他查询的方法,但我看到的行为非常不一致。这是代码:

$product = Product::find(1);

echo $product->numberOfOrders('2014-05-22', '2015-01-27');

以下是numberOfOrders方法的代码:

public function numberOfOrders($startDate = NULL, $endDate = NULL)
{
        if(is_null($startDate)) $startDate = '0000-00-00 00:00:00';
        if(is_null($endDate)) $endDate = date("Y-m-d");

        return Order::whereHas('orderProducts', function($query)
        {
                $query->where('product_id', $this->id);

        })->whereHas('status', function($query)
        {
                $query->where('sales_data', 1);

        })->where('ext_created_at', '>=', $startDate)->where('ext_created_at', '<=', $endDate.' 23:59:59')->distinct()->count();
}

当此代码运行时,它会在返回整数结果之前挂起大约10秒钟。然后,如果我再次运行相同的代码,它会立即返回结果,并在每次运行时继续这样做。如果我更改$startDate并再次运行代码,它会挂起10秒钟,然后返回结果。然后,重新运行代码将立即再次返回结果。

其他时候,当代码挂起时,我收到错误消息而不是返回结果:

PHP Fatal error:  Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 2027 Malformed packet' in /var/www/html/app/vendor/laravel/framework/src/Illuminate/Database/Connection.php:299

在出现错误消息后,如果我再次运行代码,结果会立即返回。

我很困惑我的代码会导致这种不一致的原因。我没有做任何缓存。

我注意到whereHas方法实际上将子查询添加到查询而不是使用连接,我觉得很奇怪。使用Datamapper ORM在CodeIgniter中编写类似的代码会将连接添加到查询而不是子查询,从而产生更好的性能。子查询会导致问题吗?如果是这样,我仍然不确定为什么事情表现得如此不一致。

我已经捕获了一些显示问题的数据。这是第一次来自DB::getQueryLog()的输出:

[3] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 22391.73
    )

正如您所看到的,运行需要很长时间。在那之后,我再次运行相同的东西,输出结果如下:

[3] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 22391.73
    )

[4] => Array
    (
        [query] => select count(*) as aggregate from `orders` where `orders`.`deleted_at` is null and (select count(*) from `order_products` where `order_products`.`order_id` = `orders`.`id` and `product_id` = ? and `order_products`.`deleted_at` is null) >= 1 and (select count(*) from `order_statuses` where `orders`.`order_status_id` = `order_statuses`.`id` and `sales_data` = ? and `order_statuses`.`deleted_at` is null) >= 1 and `ext_created_at` >= ? and `ext_created_at` <= ?
        [bindings] => Array
            (
                [0] => 1
                [1] => 1
                [2] => 2014-07-29
                [3] => 2015-01-27 23:59:59
            )

        [time] => 1.81
    )

正如您所看到的,同样的确切查询,但它的速度要快得多。为什么会有这样的差异?结果是否在某处缓存?

如果有帮助,我会使用php artisan tinker运行所有代码。

修改 经过一些研究,我了解到这可能是mysql query cache的行为。

1 个答案:

答案 0 :(得分:0)

你的查询非常慢。确保您的表格已正确编入索引。

此外,如果查询经常运行,请缓存您的查询。

Eloquent有一个漂亮的remember()方法。

它将使用您当前的缓存驱动程序来存储查询的结果。到目前为止,Redis正在为我做一份非常棒的工作。