Doctrine query builder - nested queries - doctrine-orm

How can I convert this query into symfony 2 doctrine query builder?
SELECT
artist_id,
DATE,
balance,
TYPE
FROM TRANSACTION AS
t1
WHERE
DATE =(
SELECT
MAX(DATE)
FROM TRANSACTION
WHERE
artist_id = t1.artist_id AND
STATUS
IN(
'partial',
'pending',
'deducted',
'accepted'
) AND TYPE NOT LIKE 'payment'
)
GROUP BY
artist_id
ORDER BY
artist_id
I tried the following:
$qb = $this->getEntityManager()->createQueryBuilder()
->select('t.balance','a.id','t.date')
->from('TestMainBundle:Transaction','t')
->Join('t.artist','a')
->where("t.status in ('partial','pending','deducted','accepted')")
->andWhere("t.type NOT LIKE 'payment'")
->groupBy('a.id')
->orderBy('a.id');
return $qb->getQuery()->getResult();
But I am stuck with including the condition of max (date) as well. Any help on this is very much appreciated.

Your Doctrine query will look something like this,
$qb1 = $this->getDoctrine()->getManager()->createQueryBuilder();
$select = $qb1->select('MAX(date) AS max_data')
->from('YourBundle:Transaction', 's')
->where('s.artist_id = :ti_artist_id')
->andWhere('s.status IN (:statuses)')
->andWhere('s.type NOT LIKE :type')
->getQuery();
$qb2 = $this->getDoctrine()->getManager()->createQueryBuilder();
$result = $qb2->select('t.artist_id', 't.date', 't.balance', 't.type')
->from('YourBundle:Transaction', 't');
$result->where($qb2->expr()->eq('t.date', $select->getDQL()))
->setParameter('ti_artist_id', 't.id')
->setParameter('statuses', array('partial','pending','deducted','accepted'))
->setParameter('type', 'payment') //possibly '%payment%'
->orderBy('t.artist_id')
->getQuery()
->getResult();
Cheers!!!

Related

Doctrine2 DQL Syntax error when ordering by count

This is Doctrine Repository function
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar)')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('COUNT(ar)', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
which should be equivalent to this query
SELECT article_id, count(id)
FROM profile_article_reads
WHERE author_id = 2
GROUP BY article_id
Order by count(id) DESC
LIMIT 1;
When I execute this code I get error
Error: Expected end of string, got '('
QueryException: SELECT ar.articleId, COUNT(ar) FROM
SciProfileBundle\Entity\ProfileArticleReads ar WHERE ar.authorId =
:userId GROUP BY ar.articleId ORDER BY COUNT(ar) DESC
THe count funtion accept a field, so try with
COUNT(ar.id)
instead of:
COUNT(ar)
Probably for sorting is better using an alias, as example:
public function mostReadArticleByUser($userId){
$total = $this->createQueryBuilder('ar')
->select('ar.articleId', 'COUNT(ar.id) as total')
->where('ar.authorId = :userId')
->groupBy('ar.articleId')
->orderBy('total', 'DESC')
->setMaxResults(1)
->setParameter('userId', $userId)
->getQuery()
->getResult();
return $total;
}
Hope this help

Doctrine use count() in NativeQuery with ResultSetMapping

i try to use NativeQuery of Doctrine. If i use my SQL ind Phpmyadmin i have the good result. If i use this, my var_dump return an empty array. I don't understand why.
$sql = 'SELECT COUNT(id) as nb FROM app_facture f WHERE SUBSTR(f.datecreate_facture, 1, 4) = 2014 AND SUBSTR(f.numero_facture,1,1) != "E"';
$rsm = new ResultSetMapping;
$rsm->addEntityResult('Acme\MyBundle\Entity\Facture', 'f');
$rsm->addFieldResult('f', 'COUNT(id)', 'nb');
$query = $this->getEntityManager()->createNativeQuery($sql,$rsm);
$results = $query->getResult();
var_dump($results);//return empty array
Thanks
Try
$rsm->addFieldResult('f', 'nb', 'id');
this worked for me.

spring-data Specification and joined subquery

I have the following Named query on my spring-data repository:
#Query("FROM Pedido p JOIN FETCH p.status ps WHERE ps.status IN (?1) AND ps.id IN (SELECT MAX(ps2.id) FROM PedidoStatus ps2 GROUP BY ps2.pedido)")
I'm trying to achieve the same result using the Criteria API and spring-data Specifications, this is what I have so far:
public static Specification<Pedido> byUltimoStatus(final List<PedidoStatus.StatusPedido> ultimoStatus) {
return new Specification<Pedido>() {
public Predicate toPredicate(Root<Pedido> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
Expression<PedidoStatus.StatusPedido> status = root.join("status").get("status");
Predicate predicateByStatus = status.in(ultimoStatus);
final Subquery<Long> subQuery = query.subquery(Long.class);
final Root<PedidoStatus> ps = subQuery.from(PedidoStatus.class);
Expression<Long> psId= ps.get("id");
Expression<Long> maxId = builder.max(psId);
subQuery.select(maxId);
subQuery.groupBy(ps.get("pedido").get("id"));
Predicate predicateByUltimoStatus = builder.in(root.join("status").get("id")).value(subQuery);
return builder.and(predicateByStatus, predicateByUltimoStatus);
}
};}
It's still not working, looks like there is an extra
INNERJOIN PedidoStatus
in the result query.
This is the result of the #Query:
select ... from Pedido pedido0_ inner join PedidoStatus status1_ on pedido0_.id=status1_.pedido where (status1_.status in (? , ?)) and (status1_.id in (select max(pedidostat2_.id) from PedidoStatus pedidostat2_ group by pedidostat2_.pedido))
And this is the result of the Criteria API:
select ... from Pedido pedido0_ inner join PedidoStatus status1_ on pedido0_.id=status1_.pedido inner join PedidoStatus status2_ on pedido0_.id=status2_.pedido where (pedido0_.id is not null) and status1_.status IN (?, ?) and (status2_.id in (select max(pedidostat3_.id) from PedidoStatus pedidostat3_ group by pedidostat3_.pedido))
Knowing that this is a very old question, it looks to me like the reason for the duplicate INNERJOIN in the query generated by a CriteriaQuery is that the code building the query, does actually invoke root.join("status") twice. The result of the first invocation should be saved into a local variable, so you can reuse it, instead of joining twice.
First you do:
Expression<PedidoStatus.StatusPedido> status = root.join("status").get("status");
And later you do:
Predicate predicateByUltimoStatus = builder.in(root.join("status").get("id")).value(subQuery);

Doctrine 2: how do you use a subquery column (in the SELECT clause)

I'm trying to do a simple select query with a subquery in the SELECT clause and have simply not found a way to do it. I've tried with both DQL and with the QueryBuilder, neither work. The code follows, please don't say I could just use a join, this is a simplified example just to illustrate the problem, I have legitimate use cases for subqueries.
// With QueryBuilder
$query = $qb->select(array('a',
'(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId
) AS addresstypeName'))
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
// With DQL
$dql = "SELECT a,
(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId
) AS addresstypeName
FROM e:Address a
WHERE a.addressId = :addressId";
$query = $em->createQuery($dql)->setParameter(':addressId', 1);
The following relationship is defined on the Address table:
/**
* #ORM\ManyToOne(targetEntity="Addresstype")
* #ORM\JoinColumns({
* #ORM\JoinColumn(name="addresstype_id", referencedColumnName="addresstype_id")
* })
*/
protected $addresstype;
In native SQL, the query would look like this:
SELECT
a.*,
(
SELECT at.addresstype_name
FROM addresstype at
WHERE at.addresstype_id = a.addresstype_id
) AS addresstype_name
FROM address a
WHERE a.address_id = 1
Any ideas?
$query = $qb->select('a')
->addSelect('(SELECT at.addresstypeName
FROM e:Addresstype at
WHERE at.addresstypeId = a.addresstypeId) AS addresstypeName'
)
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
For me subquery with doctrine works with this query :
$qb->select('e.field')
->addSelect('(SELECT count(mv.nm)
FROM Clt\Bundle\MyBundle\Entity\MV mv
LEFT JOIN Clt\Bundle\MyBundle\Entity\M ma WITH mv.nm=ma.nm
WHERE mv.ne=e.ne and ma.nm is null
) AS nm'
)
->from($this->_entityName, 'e')
->leftJoin('e.m', 'm')
->where($qb->expr()->eq('t.id'.$typeModule, $idElementModule));
Note that in the left join you must use WITH instead of ON...
I know this is an old question, but if you want, you could have used another query builder as your subquery:
$qb->select("a")
->addSelect("(" . $qb2->select("at.addresstypeName")
->from("e:Addresstype", "at")
->where("at.addresstypeId = a.addresstypeId")
->getDQL() . ") AS addresstypeName"
)
->from('e:Address', 'a')
->where('a.addressId = :addressId')
->setParameter('addressId', 1);
In my scenario what I needed was to look into a join and find an Id and use it as boolean, found 1 otherwise 0, then applying this to orderBy. DQL expressions worked only when combined with Where clause, which wasn't my case. So, a DQL subselect saved me.
Adapted more or less to your scenario, it would look like this:
// With QueryBuilder
// In AddressRepository
// Where one address may belong to several addressTypes
public function getWithType($addressType){
$qb = $this->createQueryBuilder('a1');
$qb->addSelect('a1.someField', 'a1.otherField')
$qb->addSelect(
'(SELECT at.addressTypeName
FROM App\Entity\Address a2
JOIN a2.addressType at
WHERE at.id = '.$addressType.' AND a2.id = a1.id
) AS addressTypeName')
//The rest part of the query
}

conversion sql query to jpa

I have a query
SELECT d.name, count(e.id) FROM department d LEFT OUTER JOIN employee e on e.department_id = d.id and e.salary > 5000
and how i can convert this to jpa
right now i have:
CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class);
Root<Department> root = criteria.from(Department.class);
Path<String> name = root.get("name");
Expression<Long> empCount = builder.count(root.get("employees").get("id"));
criteria.multiselect(name,empCount);
TypedQuery<Object[]> query = em.createQuery(criteria);
I simplified both examples by removing ordering and grouping
can anyone tell me how i can modifie my jpa code to get same reslults like from my sql query
thanks in advance
You're not far from the result. The problem is that, AFAIK, you can't add any restriction on the on clause, using JPA. So the query wil have to be rewritten as
SELECT d.name, count(e.id) FROM department d
LEFT OUTER JOIN employee e on e.department_id = d.id
where (e.id is null or e.salary > 5000)
Here is the equivalent of this query not tested):
CriteriaQuery<Object[]> criteria = builder.createQuery(Object[].class);
Root<Department> root = criteria.from(Department.class);
Path<String> name = root.get("name");
Join<Department, Employee> employee = root.join("employees", JoinType.LEFT);
Expression<Long> empCount = builder.count(employee.get("id"));
criteria.multiselect(name,empCount);
criteria.where(builder.or(builder.isNull(employee.get("id")),
builder.gt(employee.get("salary"), 5000)));
TypedQuery<Object[]> query = em.createQuery(criteria);