使用Doctrine 2 DQL查询海量联接数据的最有效方法

时间:2018-08-06 12:25:46

标签: php sql symfony doctrine-orm doctrine

我有一个实体“硬币”,其价格与oneToMany相关联。价格每分钟更新一次,有数百万的条目。

我要实现的是一个DQL查询,它将获取迄今为止的最新价格。

现在,我看到了两种实现方法,并且我想知道哪种方法在性能方面最好:

我可以在数据库中查找“价格”。“上次更新”等于上次更新。

或者我可以获取最后100个价格ID(我们每分钟更新100个硬币,并在数据库中添加100个新行),并在我的左侧联接上添加LIMIT 100和ORDER BY'id'DESC。 我知道在带有理论的左联接上使用LIMIT是很棘手的,但是我在这里找到了解决方案: How to limit results of a left-join with Doctrine (DQL) 此处:https://www.colinodell.com/blog/201703/limiting-subqueries-doctrine-2-dql

我想知道执行该查询所需的资源最少。

我当然使用的是getArrayResults(),并且使用的是局部函数和学说缓存。

您对此有何看法? 谢谢!

2 个答案:

答案 0 :(得分:1)

我也遇到过类似情况。例如,我运行一个social commerce网络,并希望从企业那里获取所有follower_id,以更新它们是否已执行操作。如果您想要Liker_ids等,就一样。

在这种情况下,您仅对一列中的值(对您的价格)感兴趣,但基于涉及不同字段(coin_id,lastupdated)的查询。为此,我强烈建议您使用主义发送本机SQL查询。它的效率提高了几个数量级,避免了昂贵的理论水合作用等。

我在实体存储库中为您编写了一个示例查询。

<?php

namespace App\EntityRepository;

use Doctrine\ORM\EntityRepository;
use PDO;

class CoinPricesRepository extends EntityRepository
{
    public function queryLatestPricesForCoinId(int $coin_id, int $limit)
    {
        $sql = 'SELECT price FROM coin_prices WHERE coin_id = :coin_id ORDER BY lastupdated DESC LIMIT  = :limit;';
        $params['coin_id'] = $coin_id;
        $params['limit'] = $limit;

        $stmt = $this->getEntityManager()->getConnection()->prepare($sql);
        $stmt->execute($params);

        return $stmt->fetchAll(PDO::FETCH_COLUMN);
    }
}

答案 1 :(得分:0)

我一直在优化我的学说要求,并获得了一些很棒的性能改进,如果有人正在寻找类似的解决方案,我将在这里分享。

首先,尽可能用where子句限制左连接 二,使用部分对象 第三,使用数组结果。这实际上改变了一切。

/**
 * @return Coins[] Returns an array of Crypto objects
 */

public function findOneByTickerRelationnal($ticker)
{
    $em = $this->getEntityManager();
    $updatesrepository = $em->getRepository(Updates::class);
    $updates = $updatesrepository->findOneBy(['id'=> 1 ]);

    // This is where I’ve been doing additional work to limit my left join as much as possible with a ‘with’ on left join
    $recentMarkets = $updates->getMarket();
    $recentPrices = $updates->getPrice();
    $recentSources = $updates->getSources();

    $cryptos = $this->createQueryBuilder('c')
        ->select('partial c.{id, name, ticker}’) //<= use of partial is a plus but you need to know exactly which fields you want
        ->leftJoin('c.prices', 'p','WITH', 'p.last_updated >= :recentPrices')
        ->addSelect('partial p.{id, price_usd, daily_volume_usd, change_1h, change_1d, change_7d, rank}')
        ->leftJoin('c.markets', 'm','WITH', 'm.last_updated >= :recentMarkets')
        ->addSelect('partial m.{id, cur_supply, market_cap, max_supply}')
        ->leftJoin('c.sources', 's','WITH', 's.last_updated >= :recentSources')
        ->addSelect('s')
        ->where('c.ticker = :ticker')
        ->setParameter('recentPrices', $recentPrices)
        ->setParameter('recentMarkets', $recentMarkets)
        ->setParameter('recentSources', $recentSources)
        ->setParameter('ticker', $ticker)
        ->getQuery()
        ->getArrayResult(); //<=Changes everything 

    $results = $cryptos[0];

    return $results;
}