【问题标题】:Symfony2 Doctrine - WHERE IN as pairSymfony2 Doctrine - 成对出现在哪里
【发布时间】:2014-07-09 23:31:11
【问题描述】:

基本上我想进行这样的查询:

SELECT * FROM `table` WHERE (`c1`,`c2`) in (('a','b'),('c','d')) 

我尝试了一些方法,但找不到任何建议如何实现。我尝试了一些类似的东西:

foreach ($pairs as $pair) {
    $columns[] = "(" . $pair->getc1() . "," . $pair->getc2() . ")";
}

$query = $this->entityManager->getRepository('AcmeTestBundle:Combinations')->createQueryBuilder('c')
                ->select()
                ->where('(c.c1,c.c2) in (:pairs)')
                ->setParameter('pairs', $columns)
                ->getQuery();

这显然对我不起作用,你知道如何在where in 语句中使用多个值吗?

编辑:

我设法做到了这种糟糕的方式。我怀疑这是最好的方法,我仍然认为应该有更好的解决方案,所以我很乐意看到它。

$query = $this->entityManager
    ->getRepository('AcmeTestBundle:Combinations')
    ->createQueryBuilder('c')
    ->select();

$expr = $query->expr();

$i = 1;
foreach ($pairs as $pair) {
    $query = $query->orWhere($expr->andX($expr->eq('c.c1', ':c1' . $i), $expr->eq('c.c2', ':c2' . $i)))
            ->setParameter('c1' . $i, $pair->getc1())
            ->setParameter('c2' . $i, $pair->getc2()) 
    ;

    $i++;
}

$query = $query->getQuery();

【问题讨论】:

  • 不幸的是,您不能在 Doctrine 中使用 WHERE (col1, col2)... 但是,我可以想象一个扩展,它将成为 Doctrine 中的自定义函数。它可能是类似于PAIRS((col1, col2), (val1_1, val2_1), (val1_2, val2_2), (val1_N, val2_N)) 的东西,最终会变成普通的原始 sql。问题是验证输入会很棘手。
  • 该死的 :( 谢谢你的信息,至少我不用再找这个了
  • 请检查我的回答,也许它对你有用。

标签: php mysql symfony doctrine-orm where-in


【解决方案1】:

我找到了一些你肯定不喜欢的解决方案,但它确实有效。

基本上存在三个问题: 1. DOCTRINE 中不能有 WHERE (col1, col2) 2.您不能将数组数组作为参数传递(构建对) 3. 没有比较就不能有 where 表达式。

但是请检查我的解决方法。

扩展类:

/*
 * This file is part of the Acme package.
 *
 * (c) Denis V.
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Acme\Bundle\AcmeBundle\Query;

use Doctrine\ORM\Query\AST\Functions\FunctionNode,
    Doctrine\ORM\Query\Lexer,
    Doctrine\ORM\Query\Parser,
    Doctrine\ORM\Query\SqlWalker;

/**
 * Class Pairs
 *
 * @author Denis V.
 *
 * @package Acme\Bundle\AcmeBundle\Query
 */
class Pairs extends FunctionNode
{
    public $cols = array();
    public $pathExp = array();

    /**
     * {@inheritdoc}
     */
    public function parse(Parser $parser)
    {
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $lexer = $parser->getLexer();

        // first Path Expression is mandatory
        $this->pathExp = array();
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->cols[] = $parser->SingleValuedPathExpression();
        while ($lexer->isNextToken(Lexer::T_COMMA)) {
            $parser->match(Lexer::T_COMMA);
            $this->cols[] = $parser->SingleValuedPathExpression();
        }
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);

        $j = 1;
        while ($lexer->isNextToken(Lexer::T_COMMA)) {
            $parser->match(Lexer::T_COMMA);
            $parser->match(Lexer::T_OPEN_PARENTHESIS);
            $this->pathExp[$j] = array($parser->StringPrimary());
            while ($lexer->isNextToken(Lexer::T_COMMA)) {
                $parser->match(Lexer::T_COMMA);
                $this->pathExp[$j][] = $parser->StringPrimary();
            }

            // commented out as it should be possible to accept array parameters
            //if (count($this->cols) != count($this->pathExp[$j])) {
            //    $parser->semanticalError('Number of columns must be the same as number of compared values');
            //}

            $parser->match(Lexer::T_CLOSE_PARENTHESIS);
            $j++;
        }

        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    }

    /**
     * {@inheritdoc}
     */
    public function getSql(SqlWalker $sqlWalker)
    {
        $fields = array();
        foreach ($this->cols as $col) {
            $fields[] = $col->dispatch($sqlWalker);
        }

        $result = sprintf('(%s)', implode(', ', $fields));

        $result .= ' IN ';

        $expr = array();
        foreach ($this->pathExp as $pathExp) {
            $fields = array();
            foreach ($pathExp as $item) {
                $fields[] = $item->dispatch($sqlWalker);
            }
            $expr[] = sprintf('(%s)', implode(', ', $fields));
        }

        $result .= sprintf('(%s)', implode(', ', $expr));

        return $result;
    }

}

现在您可以执行以下操作(假设您在实体存储库中):

    $qb = $this
        ->createQueryBuilder('c')
        ->where('PAIRS((c.c1, c.c2), (:pair)) != \'dummy\'')
        ->setParameter('pair', array('val1', 'val2'));
    $q = $qb->getQuery();

    return $q;

您可能会注意到代码中有!= 'dummy'。这是因为你不能在没有比较的情况下传递 WHERE 表达式。我检查了一下,它实际上与 MySQL 一起工作。但您可以使用自定义 SqlWalker 将其删除。

要传递更多对,您需要使用循环,因为您不能将数组数组作为单个参数传递。

类似这样的:

$dqlParts = array();
foreach ($pairs as $i => $pair) {
    $dqlParts[] = sprintf('(?%d)', $i);
    $qb->setParameter($i, $pair);
}
$dqlPartsStr = implode(', ', $dqlParts);

然后在你的 DQL 中使用这个 $dqlPartsStr(注意:最后一个代码块是在 StackOverflow 上编写的,没有经过测试)。

我知道,这看起来更像是一个 hack,但这是您在 Doctrine2 中所能做的所有事情。如果您有任何问题,请告诉我。

【讨论】:

  • 尽管这不是我要找的东西,但我还是按照您所做的最多研究将分数分配给您,谢谢。
  • @KeluThatsall 感谢您的赏金。不完全是你要找的东西,好吧,似乎唯一的区别是这个技巧与“dummy”,否则 DQL 将转换为相当好的配对 SQL:WHERE (col1, col2) IN (('val1.1', 'val2.2'), ('val.2', 'val2.2'), ('val1.N', 'val2.N')) 与“dummy”相比(它有无副作用)。就像我提到的,这个“虚拟”字符串可以通过自定义 SqlWalker 轻松删除。无论如何,再次感谢您的赏金;)
  • 是的,但是我希望能够使用不同数量的参数,不仅是对 :P 我希望已经在教义中内置了一个解决方案,但我只是没有找到它。最后,我甚至没有使用 QueryBuilder 就构建了这个查询 - 不值得 imo ;)
  • @KeluThatsall 我看到了,但实际上我的解决方案不限于对;)Pairs 函数节点首先解析列,直到第一个右括号:PAIRS((col1, col2, ..., colN),然后解析值:,('val1.1','val.2', ..., 'val1.N'), ... ('valM.1', 'valM.2', ..., 'valM.N')),所以,如你所见,不限于这种方式。
  • 啊,说实话,我没有很好地测试它,谢谢。但正如我所说,我将使用标准 SQL 准备语句,因为我认为将那些 QueryBuilder 用于一些更复杂的查询太麻烦了;)
【解决方案2】:

也许可以尝试一下:

$separator = '|||';
$columns = [];
foreach ($pairs as $pair) {
    $columns[] = $pair->getc1() . $separator . $pair->getc2();
}
$query = $this
    ->entityManager
    ->getRepository('AcmeTestBundle:Combinations')
    ->createQueryBuilder('c')
    ->select()
    ->where("CONCAT_WS('" . $separator . "', c.c1, c.c2) in (:pairs)")
    ->setParameter('pairs', $columns)
    ->getQuery();

【讨论】:

  • 你检查过还是只是猜测?
【解决方案3】:

你试过 toArray() 吗?

像这样:

->select()
->where('(c.c1,c.c2) in (:pairs)')
->setParameter('pairs', $columns->toArray())
->getQuery();

【讨论】:

  • 这将如何工作? $columns 已经是一个数组,它没有任何方法,它不是一个对象
猜你喜欢
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 2011-08-15
  • 1970-01-01
  • 2020-05-02
  • 1970-01-01
相关资源
最近更新 更多