在 Doctrine2 DQL 查询上进行排序的 CASTING 属性

Posted

技术标签:

【中文标题】在 Doctrine2 DQL 查询上进行排序的 CASTING 属性【英文标题】:CASTING attributes for Ordering on a Doctrine2 DQL Query 【发布时间】:2011-11-16 08:20:52 【问题描述】:

我正在尝试获取 Doctrine2 实体,按他们的 ID 排序,这显然是一个字符串,即使它只包含数字。 所以我想做的是这样的:

SELECT entity1, cast (entity1.id AS integer) AS orderId
FROM Namespace\Bla\MyEntity 
ORDER BY orderId

有没有办法在 Doctrine2 中做这样的事情? 或者,如果我无法更改 id 的类型(当然是由于客户要求),那么获得我的结果的最佳做法是什么?


注意:我不是在问 SQL 代码,我是在问一个 Doctrine2 解决方案,最好是在 DQL 中

【问题讨论】:

我认为您在第 13 行缺少( 【参考方案1】:

你应该可以add your own function来实现这个功能。

这个类看起来像这样:

namespace MyProject\Query;

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

class CastAsInteger extends FunctionNode

    public $stringPrimary;

    public function getSql(SqlWalker $sqlWalker)
    
        return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS integer)';
    

    public function parse(Parser $parser)
    
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);

        $this->stringPrimary = $parser->StringPrimary();

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

你需要注册你的函数:

$config = $em->getConfiguration();
$config->addCustomNumericFunction('INT', CastAsInteger::class);

然后就可以使用了:

SELECT e, INT(e.id) AS HIDDEN orderId
FROM Namespace\Bla\MyEntity e
ORDER BY orderId

PS:通过添加HIDDEN 关键字,别名orderId 将不会出现在结果中(并且仅用于排序)。

【讨论】:

在我自己的函数中,我有一个 SingleValuedPathExpressionu.group 并希望使用实际的 Expression 对象将其“包装”在 "CAST(" SingleValuedPathExpression " AS DECIMAL(10,2))" 中......我该怎么做? CAST ...在手工构建时会是什么样的表达? 好吧,没关系;我使用了一个自定义表达式并修改了它的->dispatch() 方法以满足我的需要。清洁如预期。教义真棒。 :) 如果目标数据库是mysql,这行不行:return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS integer)';试试这个:return 'CAST(' . $this->stringPrimary->dispatch($sqlWalker) . ' AS SIGNED)'; 只是为了在 Symfony 中记录,您将寄存器部分放在 config.yml【参考方案2】:

基于 Jasper N. Brouwer 的回答,这是一个稍微增强的解决方案:

<?php
namespace MyProject\Query;

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

class Cast extends FunctionNode

    /** @var \Doctrine\ORM\Query\AST\PathExpression */
    protected $first;
    /** @var string */
    protected $second;
    /**
     * @param SqlWalker $sqlWalker
     *
     * @return string
     */
    public function getSql(SqlWalker $sqlWalker)
    
        return sprintf("CAST(%s AS %s)",
            $this->first->dispatch($sqlWalker),
            $this->second
            );
    


    /**
     * @param Parser $parser
     *
     * @return void
     */
    public function parse(Parser $parser)
    
        $parser->match(Lexer::T_IDENTIFIER);
        $parser->match(Lexer::T_OPEN_PARENTHESIS);
        $this->first = $parser->ArithmeticPrimary();
        $parser->match(Lexer::T_AS);
        $parser->match(Lexer::T_IDENTIFIER);
        $this->second = $parser->getLexer()->token['value'];
        $parser->match(Lexer::T_CLOSE_PARENTHESIS);
    

现在应该可以像这样编写 DQL:

SELECT e, CAST(e.id AS integer) AS HIDDEN orderId FROM Namespace\Bla\MyEntity e ORDER BY orderId

【讨论】:

【参考方案3】:

认为在这种情况下最好使用一些额外的功能(而不是试图“规避”他们的功能)。例如。为Doctrine 2 添加几乎所有必要(不支持)的东西的优秀解决方案是DoctrineExtensions by beberlei (github)。有了它,就可以直接使用CAST-statement,就像在 OP 的情况下一样:

("Symfony-example") 例如在你的config.xml 添加行:

orm:
    ..
    entity_managers:
            ....
            dql:
                ....
                string_functions:
                    CAST: DoctrineExtensions\Query\Mysql\Cast

然后你可以像这样使用它:

 SELECT entity1, CAST(entity1.id AS integer) AS orderId
 FROM Namespace\Bla\MyEntity 
 ORDER BY orderId

【讨论】:

【参考方案4】:

不确定这是否有效,但要访问实体 ID,您需要 IDENTITY() DQL 函数。试试这个:

SELECT entity1  FROM Namespace\Bla\MyEntity  ORDER BY IDENTITY(entity1)

【讨论】:

【参考方案5】:

我昨天刚刚在自己的代码中做了类似的事情。我能够做到:

select cast(entity1 as int) as OrderID
from yourtablename
where yourconditions

我必须将我的实际转换为 money 然后 int,但如果你没有小数,你不应该有这个问题。您也可以尝试转换为数字或使用转换而不是转换,但在这种情况下转换更好。

如果 OrderID 中已经有相同的值,为什么还需要 entity1 作为列?

【讨论】:

你知道我使用的是doctrine2 DQL而不是MySQL吗? 我没有意识到这一点,我使用的是用于 sqlserver 的 sql,而不是 mysql,但它们非常相似。【参考方案6】:

在不改变数据类型的情况下试试这个

  select (entity1 * 1) as display_value, entity1 as return_value 
      from Table_Name
     order by 1 asc;

【讨论】:

你知道我使用的是doctrine2 DQL而不是MySQL吗?【参考方案7】:

我想你想通过entity1 订购。如果您的 entity1 数据类型是整数,则无需将其更改为整数,否则您应该这样做。下面是你的查询。试试这个。

select entity1,cast(entity1 as integer) as order_id from Table_Name order by 1 asc;

【讨论】:

如前所述,由于客户要求,我无法更改

以上是关于在 Doctrine2 DQL 查询上进行排序的 CASTING 属性的主要内容,如果未能解决你的问题,请参考以下文章

Doctrine2 - 继承映射,查询子类

doctrine2 dql,在进行类似比较时使用带有%wildcard的setParameter

如何在 Doctrine 2 DQL 中使用 now()?

DQL_排序查询和DQL聚合函数

如何在 SQL Server 的 Doctrine 2 中更改 DQL 查询中的 LockMode

Doctrine2:任意连接和单表继承