Doctrine 2:子查询别名在结果中创建一个额外的级别

时间:2013-08-08 01:41:11

标签: doctrine-orm

考虑以下DQL:

SELECT
  a,
  (SELECT COUNT(c.id) FROM Comment c WHERE c.article_id = a.id) num_comments
FROM Article a

我希望结果包含所有文章列和同一级别的num_comments,但是文章列包含在索引0的数组中:

array(
  0 => array(
    0 => array(
      "id" => 1,
      "title" => "Title",
    ),
    "num_comments" => 15
  ),
  1 => array(
    0 => array(
      "id" => 2,
      "title" => "Title",
    ),
    "num_comments" => 20
  )
);

如何将所有文章列和任何额外字段保持在同一级别?

2 个答案:

答案 0 :(得分:3)

我将在短期和长期解释(和替代答案)中分开我的答案。

简短说明:

SELECT a.field1
     , ...
     , a.fieldN
     , (SELECT COUNT(c.id) FROM Comment c WHERE c.article_id = a.id) num_comments 
  FROM Article a

然后保湿作为ARRAY,它将返回你期待的东西。

长解释:

无论何时引用实体本身(在您的情况下,“SELECT a”),这意味着您要求返回与任何其他元素隔离的整个实体。 默认情况下,Doctrine总是将此实体分配给索引0并继续,具体取决于您选择的FROM实体的数量。要更改此行为,您所要做的就是为实体添加别名,就像使用子查询一样:

SELECT a AS article
     , (SELECT COUNT(c.id) FROM Comment c WHERE c.article_id = a.id) AS num_comments 
  FROM Article a

这将返回到:

array(
  0 => array(
    "article" => array(
      "id" => 1,
      "title" => "Title",
    ),
    "num_comments" => 15
  ),
  1 => array(
    "article" => array(
      "id" => 2,
      "title" => "Title",
    ),
    "num_comments" => 20
  )
);

如果您不想返回整个实体,可以使用PARTIAL支持并仅返回您感兴趣的字段。让我们支持您只需要id(必需,因为它是实体标识符)和标题:< / p>

SELECT PARTIAL a.{id, title} AS article
     , (SELECT COUNT(c.id) FROM Comment c WHERE c.article_id = a.id) AS num_comments 
  FROM Article a

另一种方法是将结果封装到DTO中。当您决定将值水合为对象时,这将提供更多的OO控制。 DQL看起来像这样:

SELECT NEW ArticleDTO(a, COUNT(c.id)) 
  FROM Article a
  LEFT JOIN a.comments c
 GROUP BY a.id

您不能将整个子查询作为参数放置的原因是由于DQL的限制。没有人要求完整的子查询,所以现在我们遵循JPA建议的ScalarExpression。

干杯,

Guilherme Blanco

答案 1 :(得分:0)

@hari提到的非常接近答案。

你只需要进行LEFT JOIN即可获得所有文章,即使他们没有评论。

 SELECT a, COUNT(c.id) AS num_comments FROM Article a LEFT JOIN a.comments c GROUP BY a.id 

并且将COUNT与文章放在同一个数组中,我会单独按字段说SELECT。

SELECT a.id, a.text, a.date, COUNT(c.id) AS num_comments FROM Article a LEFT JOIN a.comments c GROUP BY a.id