My query looks like
SELECT
COUNT(*) as cnt, ROUND(SUM(cost),2) as cost
FROM
(SELECT ROUND(SUM(cost),2) as cost FROM accounts LEFT JOIN earnings ON earnings.id = accounts.id WHERE earnings.date >= ? GROUP BY earnings.id) src;
How to write it using $querybuilder from Doctrine?
I have tried:
$qb = $this->em->getConnection()->createQueryBuilder();
$qb->select('COUNT(*) as cnt, ROUND(SUM(cost),2) as cost');
$qb->from(
$qb->select('SELECT ROUND(SUM(cost),2) as cost')
->from('accounts')
->leftJoin('accounts', 'earnings', 'earnings', 'earnings.id = accounts.id')
->where('earnings.date >= :date')->setParameter(':date', $dto->getFromSqlFormat())
->groupBy('earnings.id')
);
But it is not working and I am getting error:
Warning: Illegal offset type
It looks like you need a total count of accounts and their sum of total cost from earnings table If so then i guess you can simplify your original query as
SELECT COUNT(DISTINCT a.id) cnt,
ROUND(SUM(e.cost),2) as cost
FROM accounts a
LEFT JOIN e ON e.id = a.id
WHERE e.date >= ?
No need for group by if you want a scalar result
In DQL it would be as
SELECT COUNT(DISTINCT a.id) cnt,
SUM(e.cost) as cost
FROM AppBundle\Entity\Account a
LEFT JOIN a.earnings e
WHERE e.date >= ?
In query builder it would look like
$this->createQueryBuilder('a')
->select('count(distinct a.id) as cnt, sum(e.cost) as cost')
->from('AppBundle\Entity\Account','a')
->leftJoin('a.earnings', 'e')
->where('e.date = :date')
->setParameter('date', $date)
->getQuery();
You can always format your end result from your codebase so I have removed round from query builder and DQL
Prior to above make sure you have defined proper relation in your Account entity that points to Earning entity (one to many /many to many) otherwise this won't help
Related
Im tried this to work it using Laravel Eloquent but i cant get exact query. So i make a raw query to get the data i want. Any one help me how to convert this into laravel eloquent or Query builder?
SELECT users.*,
chat.*
FROM users
LEFT JOIN
(SELECT a.customer_id,
a.time,
b.content
FROM
(SELECT customer_id,
MAX(datetimestamp) TIME
FROM chat_messages
GROUP BY customer_id) a
JOIN chat_messages b ON a.customer_id = b.customer_id
AND a.time = b.datetimestamp) chat ON users.id = chat.customer_id
WHERE users.customer_role != 0
ORDER BY TIME DESC
I think you are trying to get latest chat message for each user , your query can be rewritten using left join to pick the latest record per group and it would be easier to transform such query in laravel's query builder format
SQL
select u.*,c.*
from users u
join chat_messages c on u.id = c.customer_id
left join chat_messages c1 on c.customer_id = c1.customer_id and c.datetimestamp < c1.datetimestamp
where c1.customer_id is null
and u.customer_role != 0
order by c.datetimestamp desc
Query Builder
DB::table('users as u')
->select('u.*, c.*')
->join('chat_messages as c', 'u.id', '=', 'c.customer_id' )
->leftJoin('chat_messages as c1', function ($join) {
$join->on('c.customer_id', '=', 'c1.customer_id')
->whereRaw(DB::raw('c.datetimestamp < c1.datetimestamp'));
})
->whereNull('c1.customer_id')
->where('u.customer_role','!=',0)
->orderBy('c.datetimestamp', 'desc')
->get();
Reference:Laravel Eloquent select all rows with max created_at
I am trying to pass following sql query to Doctrine:
SELECT id, name, count(*) FROM (SELECT r.id, r.name, f.timestamp FROM `food_log` as f inner join recipe as r on f.recipe_id = r.id WHERE f.user_id = 6 and timestamp > "2016-09-01" and recipe_id is not null group by f.timestamp, r.id) a GROUP BY a.id ORDER BY count(*) DESC
It should return me recipes with amount of how many times particular user was using single recipe from selected timestamp.
Now I am trying to do this with Doctrine 1.2 and Symfony 1.4, however I do not know how to make query from subquery, I was trying to do something like this
$subQuery = Doctrine_Query::create()
->select('r.id, r.name, f.timestamp')
->from('FoodLog f')
->innerJoin('f.Recipe r')
->where('f.user_id = ?', $userId)
->andWhere('timestamp > ?', "2016-09-01")
->andWhere('f.recipe_id is not null')
->andWhere('r.is_premium = ?', $premium)
->groupBy('f.timestamp, r.id')
->getSqlQuery();
$query = Doctrine_Query::create()
->select('id, name, count(*)')
->from($subQuery)
->groupBy('id')
->orderBy('count(*) DESC');
return $query->fetchArray();
Anybody know where I am wrong with it ?
Thanks a lot for any response !
Basically, you can't do nested queries in this version of Doctrine. I'd recommend using a raw SQL query via the doctrine connector:
$sql = 'SELECT id, name, count(*) FROM (SELECT r.id, r.name, f.timestamp FROM `food_log` as f inner join recipe as r on f.recipe_id = r.id WHERE f.user_id = 6 and timestamp > "2016-09-01" and recipe_id is not null group by f.timestamp, r.id) a GROUP BY a.id ORDER BY count(*)
DESC';
$conn = Doctrine_Manager::getInstance()->getCurrentConnection();
$result = $conn->execute($sql);
foreach($result as $data){
// do something
}
As you're not hydrating objects, you should find this works well.
I am trying and failing to translate my relatively simple SQL statement into one that will work within Doctrine.
This is the SQL statement, which works as required when run against my database:
SELECT a.*
FROM score a
INNER JOIN (
SELECT name, MAX(score) AS highest
FROM score
GROUP BY name
) b
ON a.score = b.highest AND a.name = b.name
GROUP BY name
ORDER BY b.highest DESC, a.dateCreated DESC
Here's the DQL attempt thus far:
$kb = $em->createQuery(
"SELECT a
FROM ShmupBundle:Score a
INNER JOIN a.name ShmupBundle:Score b WITH a.score = b.score AND a.name = b.name GROUP BY b.name
WHERE a.platform='keyboard'
GROUP BY a.name
ORDER BY b.score DESC, a.dateCreated DESC"
);
Which is currently giving this error:
[Semantical Error] line 0, col 73 near 'ShmupBundle:Score': Error: Class ShmupBundle\Entity\Score has no association named name
The table itself is pretty simple:
id, name, score, platform, dateCreated
There are multiple entries with the same name, but different scores. I want to show only the "high score" per name. I've been trying on and off for a day or two now, with no luck. Can anyone point me in the right direction?
The query you are trying to do with doctrine is related to greatest-n-per-group. To use a sub query and then join with main query get things complicated to handle with doctrine. So below is the rewritten SQL version to get the same results without use of any aggregate functions:
SELECT
a.*
FROM
score a
LEFT JOIN score b
ON a.name = b.name
AND a.score < b.score
WHERE b.score IS NULL
ORDER BY a.score DESC
DEMO
To convert above query equivalent to doctrine or DQL is easy, below is the DQL version of above SQL:
SELECT a
FROM AppBundle\Entity\Score a
LEFT JOIN AppBundle\Entity\Score b
WITH a.name = b.name
AND a.score < b.score
WHERE b.score IS NULL
ORDER BY a.score DESC
Or with query builder you can write something like i have tested below with symfony 2.8 using the DEMO Schema
$DM = $this->get( 'Doctrine' )->getManager();
$repo = $DM->getRepository( 'AppBundle\Entity\Score' );
$results = $repo->createQueryBuilder( 'a' )
->select( 'a' )
->leftJoin(
'AppBundle\Entity\Score',
'b',
'WITH',
'a.name = b.name AND a.score < b.score'
)
->where( 'b.score IS NULL' )
->orderBy( 'a.score','DESC' )
->getQuery()
->getResult();
Another idea would be create a view using your query in database and in symfony create an entity put the view name in table annotation and just start calling your entity it will give the results returned by your query but this approach is not recommended just a temporary fix.
Inner Join Statement needs first argument as a table, that is a semantic error in your query.
$kb = $em->createQuery(
"SELECT a
FROM ShmupBundle:Score a
INNER JOIN ShmupBundle:Score b ON a.score = b.score AND a.name = b.name GROUP BY b.name
WHERE a.platform='keyboard'
GROUP BY a.name
ORDER BY b.score DESC, a.dateCreated DESC");
MySQL does not understand the : syntax. If ShmupBundle:Score is supposed to be a database and table, then use .. If Doctrine is supposed to replace it with something, then what does it do with it?
There can be only one GROUP BY clause, and it must be after the WHERE clause. Try removing the GROUP BY b.name.
There is no need to GROUP BY both b.name and a.name since they are equal.
use this
in class
$name = $em->getRepository('AppBundle:BlogPost')->getMaxId();
in repository you can use something like
public function getMaxId()
{
$qb = $this->createQueryBuilder('u');
$qb->select('u, MAX(id) as idMax');
return $qb->getQuery()->getSingleResult();
}
each entity come with some default repository functions either we defined or not
if we wish to add some extra functionality we do write down custom functions in repository class. like i want to add one more function getMaxId i will write down in repository to access this function.
for getting max or min from each group we can do with given query
select * from (select * from mytable order by `Group`, age desc, Person) x group by `Group
this is not good way to fetch max from each group as we need to write down sub query for that.
other than that we have Row_number() function
SELECT sd.* FROM ( SELECT sale_person_id,sale_person_name,no_products_sold,commission_percentage,sales_department,ROW_NUMBER() OVER(PARTITION BY sale_person_id ORDER BY no_products_sold DESC) rowNumber FROM sales_department_details )sd WHERE sd.rowNumber =1;
here you find out all work arounds
I want to get the lowest bid of the user for each product id.
return $this->getEntityManager()
->createQuery('
SELECT PARTIAL b.{Id, MIN(amount) as amount, currency}, PARTIAL p.{id, firstName, lastName}
FROM AppBundle:Bid b
LEFT JOIN b.provider p
WHERE b.product in (:product)
AND b.status = :status
GROUP BY b.product
ORDER BY b.amount'
)
->setParameter('product', $ids)
->setParameter('status', 'active')
->getResult()
;
I get following error
[Syntax Error] line 0, col 42: Error: Expected
Doctrine\ORM\Query\Lexer::T_CLOSE_CURLY_BRACE, got '('
I have run exactly similar query in SQL, it works fine, but I'm not sure how to achieve same in doctrine query builder.
If you want user mysql function with doctrine query you must be add extension of doctrine mysql function OR you can user native query. I have added native query in follow:
use Doctrine\ORM\Query\ResultSetMapping;
$rsm = new ResultSetMapping();
$rsm->addEntityResult('AppBundle:Bid', 'b');
$rsm->addFieldResult('b', 'Id', 'Id');
$rsm->addFieldResult('b', 'amount', 'amount');
$rsm->addFieldResult('b', 'currency', 'currency');
...
$sqlQuery = "SELECT b.Id, MIN(b.amount), b.currency, p.firstName, p.lastName
FROM AppBundle:Bid b
LEFT JOIN b.AppBundle:Provider p
WHERE b.AppBundle:Product in (:product)
AND b.status = :status
GROUP BY b.product
ORDER BY b.amount";
$query = $em->createNativeQuery($sqlQuery, $rsm)
->setParameter('product', $ids)
->setParameter('status', 'active');
$query->getResult();
Please let me know if any problem with this one
When using the PARTIAL keyword in your DQL query, you can only list properties of the entities you select from (the MIN() function is no property of your entity). You can simply leave out the PARTIAL part and use a query like this (please note that Doctrine will not return objects in your result set but only a flat array, see also http://doctrine-orm.readthedocs.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#partial-object-syntax):
SELECT b.Id, MIN(b.amount) as amount, b.currency as bid, p.id, p.firstName, p.lastName as provider
FROM AppBundle:Bid b
LEFT JOIN b.provider p
WHERE b.product in (:product) AND b.status = :status
GROUP BY b.product
ORDER BY b.amount
i have a table user(id,name) and a table payments(date,amount,user_id)
I need a query to display for each user the total sum of payments and the payments in last month.
User - totalpayments - monthlypayments
something like this
SELECT id, name from user;
SELECT COUNT(amount) FROM payments WHERE user_id=user.id;
SELECT COUNT(amount) FROM payments WHERE user_id=user.id AND (data<=$timestamptoday AND data>=$timestamp1stday)
is it possible to do that without doing many queries?
Sure you can do it within one query.
SELECT
u.ID,
u.Name,
SUM(p.amount),
SUM(CASE
WHEN p.date <= '2013-02-01' AND p.date >= '2013-01-01' THEN p.amount
ELSE 0
END)
FROM
Users u
JOIN
Payments p ON u.ID = p.UserID
GROUP BY
u.ID,
u.Name
Working DEMO
Absolutely you can do this as a single query:
SELECT user.name, SUM(amount) AS totalpayments, SUM(IF(date <= '$enddate' AND date >= '$startdate'),amount,0) AS monthlypayments
FROM user LEFT JOIN payments ON user.id = payments.user_id
GROUP BY user.id
ORDER BY user.name ASC
Note that I'm using SUM rather than COUNT on the assumption that you're looking for the total amounts of the payments, rather than the number of payments there were.