40

以下是我如何在数据库中查询一些单词

$query = $qb->select('w')
    ->from('DbEntities\Entity\Word', 'w')
    ->where('w.indictionary = 0 AND w.frequency > 3')
    ->orderBy('w.frequency', 'DESC')
    ->getQuery()
    ->setMaxResults(100);

我正在使用 mysql,我想获得符合条件的随机行,我会在查询中使用 order by rand()。

我发现这个类似的问题基本上表明由于 ORDER BY RAND 在教义中不受支持,您可以改为随机化主键。但是,在我的情况下无法做到这一点,因为我有一个搜索条件和一个 where 子句,因此并非每个主键都满足该条件。

我还发现了一个代码片段,它建议您使用 OFFSET 来随机化这样的行:

$userCount = Doctrine::getTable('User')
     ->createQuery()
     ->select('count(*)')
     ->fetchOne(array(), Doctrine::HYDRATE_NONE); 
$user = Doctrine::getTable('User')
     ->createQuery()
     ->limit(1)
     ->offset(rand(0, $userCount[0] - 1))
     ->fetchOne();

我有点困惑,这是否会帮助我解决在我的情况下缺乏对随机排序的支持的问题。我无法在 setMaxResult 之后添加偏移量。

知道如何实现吗?

4

13 回答 13

48

Doctrine 团队不愿意实现这个功能

您的问题有几种解决方案,每种都有自己的缺点:

  • 添加自定义数字函数:查看此DQL RAND() 函数
    (如果您有很多匹配的行可能会很慢)
  • 使用本机查询
    (我个人尽量避免这种解决方案,我发现它很难维护)
  • 首先发出原始 SQL 查询以随机获取一些 ID,然后使用 DQLWHERE x.id IN(?)通过将 ID 数组作为参数传递来加载关联的对象。
    此解决方案涉及两个单独的查询,但可能比第一个解决方案提供更好的性能(其他原始 SQL 技术不ORDER BY RAND()存在,我不会在这里详细介绍,您可以在此网站上找到一些很好的资源)。
于 2012-05-26T00:33:28.363 回答
44

跟着这些步骤:

在您的项目中定义一个新类:

namespace My\Custom\Doctrine2\Function;

use Doctrine\ORM\Query\Lexer;

class Rand extends \Doctrine\ORM\Query\AST\Functions\FunctionNode
{

    public function parse(\Doctrine\ORM\Query\Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
    {
        return 'RAND()';
    }
}

注册课程 config.yml

doctrine:
     orm:
         dql:
             numeric_functions:
                 Rand: My\Custom\Doctrine2\Function\Rand

直接使用它作为:

$qb->addSelect('RAND() as HIDDEN rand')->orderBy('rand()'); //Missing curly brackets
于 2014-12-22T14:38:51.687 回答
40

根据 Hassan Magdy Saad的建议,您可以使用流行的DoctrineExtensions库:

在此处查看 mysql 实现:https ://github.com/beberlei/DoctrineExtensions/blob/master/src/Query/Mysql/Rand.php

# config.yml

doctrine:
     orm:
         dql:
             numeric_functions:
                 rand: DoctrineExtensions\Query\Mysql\Rand

在 Doctrine ORM 2.6.x-dev 中经过测试,您实际上可以执行以下操作:

->orderBy('RAND()')
于 2016-12-04T14:15:12.417 回答
9

或者你可以这样做 -->

$words = $em->getRepository('Entity\Word')->findAll();
shuffle($words);

当然,如果您有很多记录,那么这将非常低效,因此请谨慎使用。

于 2013-02-16T13:01:32.087 回答
7

为什么不使用存储库?

<?php

namespace Project\ProductsBundle\Entity;

use Doctrine\ORM;

class ProductRepository extends ORM\EntityRepository
{
    /**
     * @param int $amount
     * @return Product[]
     */
    public function getRandomProducts($amount = 7)
    {
        return $this->getRandomProductsNativeQuery($amount)->getResult();
    }

    /**
     * @param int $amount
     * @return ORM\NativeQuery
     */
    public function getRandomProductsNativeQuery($amount = 7)
    {
        # set entity name
        $table = $this->getClassMetadata()
            ->getTableName();

        # create rsm object
        $rsm = new ORM\Query\ResultSetMapping();
        $rsm->addEntityResult($this->getEntityName(), 'p');
        $rsm->addFieldResult('p', 'id', 'id');

        # make query
        return $this->getEntityManager()->createNativeQuery("
            SELECT p.id FROM {$table} p ORDER BY RAND() LIMIT 0, {$amount}
        ", $rsm);
    }
}
于 2016-08-30T09:40:37.583 回答
2

对我来说,最有用的方法是创建两个数组,我说订单类型和实体的不同属性。例如:

    $order = array_rand(array(
        'DESC' => 'DESC',
        'ASC' => 'ASC'
    ));

    $column = array_rand(array(
        'w.id' => 'w.id',
        'w.date' => 'w.date',
        'w.name' => 'w.name'
    ));

您可以向数组 $column 中添加更多条目,例如标准。

之后,您可以使用 Doctrine 在 ->orderBy 中添加 $column 和 $order 来构建您的查询。例如:

$query = $qb->select('w')
->from('DbEntities\Entity\Word', 'w')
->where('w.indictionary = 0 AND w.frequency > 3')
->orderBy($column, $order)
->getQuery()
->setMaxResults(100);

这种方式提高了我的应用程序的性能。我希望这可以帮助别人。

于 2018-11-19T11:29:56.447 回答
0

可以对查询(数组)结果进行改组,但改组不会随机选择。

为了从一个实体中随机选择,我更喜欢在 PHP 中执行此操作,这可能会减慢随机选择的速度,但它允许我控制我正在做的测试并使最终的调试更容易。

下面的示例将实体中的所有 ID 放入一个数组中,然后我可以使用该数组在 php.ini 中“随机处理”。

public function getRandomArt($nbSlotsOnPage)
{
    $qbList=$this->createQueryBuilder('a');

    // get all the relevant id's from the entity
    $qbList ->select('a.id')
            ->where('a.publicate=true')
            ;       
    // $list is not a simple list of values, but an nested associative array
    $list=$qbList->getQuery()->getScalarResult();       

    // get rid of the nested array from ScalarResult
    $rawlist=array();
    foreach ($list as $keyword=>$value)
        {
            // entity id's have to figure as keyword as array_rand() will pick only keywords - not values
            $id=$value['id'];
            $rawlist[$id]=null;
        }

    $total=min($nbSlotsOnPage,count($rawlist));
    // pick only a few (i.e.$total)
    $keylist=array_rand($rawlist,$total);

    $qb=$this->createQueryBuilder('aw');
    foreach ($keylist as $keyword=>$value)
        {
            $qb ->setParameter('keyword'.$keyword,$value)
                ->orWhere('aw.id = :keyword'.$keyword)
            ;
        }

    $result=$qb->getQuery()->getResult();

    // if mixing the results is also required (could also be done by orderby rand();
    shuffle($result);

    return $result;
}
于 2015-09-05T08:35:10.387 回答
0

我希望这对其他人有帮助:

        $limit = $editForm->get('numberOfQuestions')->getData();
        $sql = "Select * from question order by RAND() limit $limit";

        $statement = $em->getConnection()->prepare($sql);
        $statement->execute();
        $questions = $statement->fetchAll();

注意这里的表问题是一个 AppBundle:Question Entity。相应地更改详细信息。问题的数量取自编辑表单,请确保检查表单构建器的变量并相应地使用。

于 2016-08-11T20:10:03.570 回答
0

@Krzysztof 的解决方案在这里是最好的恕我直言,但是 RAND() 在大型查询上非常慢,所以我更新了@Krysztof 的解决方案以提供更少的“随机”结果,但它们仍然足够随机。受此答案启发https://stackoverflow.com/a/4329492/839434

namespace Project\ProductsBundle\Entity;

use Doctrine\ORM;

class ProductRepository extends ORM\EntityRepository
{
    /**
     * @param int $amount
     * @return Product[]
     */
    public function getRandomProducts($amount = 7)
    {
        return $this->getRandomProductsNativeQuery($amount)->getResult();
    }

    /**
     * @param int $amount
     * @return ORM\NativeQuery
     */
    public function getRandomProductsNativeQuery($amount = 7)
    {
        # set entity name
        $table = $this->getClassMetadata()
            ->getTableName();

        # create rsm object
        $rsm = new ORM\Query\ResultSetMapping();
        $rsm->addEntityResult($this->getEntityName(), 'p');
        $rsm->addFieldResult('p', 'id', 'id');

        # sql query
        $sql = "
            SELECT * FROM {$table}
            WHERE id >= FLOOR(1 + RAND()*(
                SELECT MAX(id) FROM {$table})
            ) 
            LIMIT ?
        ";

        # make query
        return $this->getEntityManager()
            ->createNativeQuery($sql, $rsm)
            ->setParameter(1, $amount);
    }
}
于 2018-02-08T12:23:25.233 回答
-1

尽快获得单个对象结果的最简单(但不一定是最聪明)的方法可能是在您的 Repository 类中实现它:

public function findOneRandom()
{
    $className = $this->getClassMetadata()->getName();

    $counter = (int) $this->getEntityManager()->createQuery("SELECT COUNT(c) FROM {$className} c")->getSingleScalarResult();

    return $this->getEntityManager()

        ->createQuery("SELECT ent FROM {$className} ent ORDER BY ent.id ASC")
        ->setMaxResults(1)
        ->setFirstResult(mt_rand(0, $counter - 1))
        ->getSingleResult()
    ;
}
于 2018-02-14T17:11:26.887 回答
-1

首先从 DB 表中获取 MAX 值,然后将其用作 PHP 中的偏移量,即 $offset = mt_rand(1, $maxId)

于 2019-04-07T13:30:12.157 回答
-2

我知道这是一个老问题。但我使用以下解决方案来获取随机行。

使用EntityRepository方法:

public function findOneRandom()
{
    $id_limits = $this->createQueryBuilder('entity')
        ->select('MIN(entity.id)', 'MAX(entity.id)')
        ->getQuery()
        ->getOneOrNullResult();
    $random_possible_id = rand($id_limits[1], $id_limits[2]);

    return $this->createQueryBuilder('entity')
        ->where('entity.id >= :random_id')
        ->setParameter('random_id', $random_possible_id)
        ->setMaxResults(1)
        ->getQuery()
        ->getOneOrNullResult();
}
于 2017-11-24T01:09:02.483 回答
-7

只需添加以下内容:

->orderBy('RAND()')
于 2016-01-20T15:07:05.277 回答