Symfony lowest user bid using MIN function? - php

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

Related

Laravel Eloquent DB::Raw query does not allow WHERE statement and subquery together

The following query runs just fine when I paste it into my SQL tool, but through Laravel, it returns zero rows.
$sql = "
SELECT main_query.* FROM (
SELECT
c.id,
c.name,
c.order,
cd.case,
(SELECT count(*) from logs cl
where
c.id = cl.id
and cl.status = 'OPEN'
) as cl_count,
sdsc.task
FROM `table` c
INNER JOIN `table2` cd ON (c.id = cd.id)
LEFT JOIN `table3` sdsc ON (c.id = sdsc.id)
WHERE
c.status = 'NEW'
GROUP BY c.id
ORDER BY cd.updated_at DESC
) main_query
where main_query.cl_count > 1
GROUP BY main_query.id
ORDER BY main_query.updated_at DESC
limit 0,20
";
Due to the complexity of the actual query, I cannot convert it wholly into an eloquent query, so I am executing it using DB::select(DB::raw($searchQuery));
If I remove where main_query.cl_count > 1, the query runs just fine. What is causing this to fail, and how do I rewrite the code?
Try below code, generated by https://sql2builder.github.io/
DB::query()->fromSub(function ($query) {
$query->from('table')
->select('table.id', 'table.name', 'table.order', 'table2.case', 'table3.task')
->on(function ($query) {
$query->where('table.id','=','table2.id');
})
->on(function ($query) {
$query->where('table.id','=','table3.id');
})
->where('table.status','=','NEW')
->groupBy('table.id')
->orderBy('','desc');
},'main_query')
->select('main_query.*')
->where('main_query.cl_count','>',1)
->groupBy('main_query.id')
->orderBy('','desc')
->get();

How to write nested select sql query with doctrine query builder?

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

Making query from subquery

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.

Doctrine Query Language get Max/Latest Row Per Group

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

SQL Join question, return null if second table is empty

I have 2 tables.
table 1 (projects): id, name, type
table 2 (project_ratings): project_id, rating
Some projects don't have ratings.
SELECT `p`.`id`, `p`.`name`, AVG(pr.rating) FROM (`projects` p) JOIN `project_ratings` pr ON `p`.`id` = `pr`.`project_id` WHERE `p`.`type` = 'group' OR `p`.`type` = 'user';
I want to return all projects, and return NULL if there aren't any ratings. This query returns only the ones with ratings.
I tried left join, right join, full join, still same thing.
Using CodeIgniter Active Records:
$this->db->select("p.id, p.name, AVG(pr.rating)");
$this->db->from('projects p');
$this->db->join('project_ratings pr', 'p.id = pr.project_id');
$this->db->where('p.type', 'group');
$this->db->or_where('p.type', 'user');
$res = $this->db->get();
what am I missing?
When using an aggregate function (AVG() in this case), you need to specify a GROUP BY clause with the non-aggregate fields, eg
GROUP BY p.id, p.name
To ensure all project references are present regardless of joined ratings, use a LEFT JOIN.
The sql that you want would be
SELECT *
FROM projects
LEFT OUTER JOIN project_ratings ON projects.id = project_ratings.project_id
I'm not certain how to do that with code igniter.
Try this:
$this->db->select("p.id, p.name, AVG(pr.rating) as average_rating");
$this->db->from('projects p');
$this->db->join('project_ratings pr', 'p.id = pr.project_id', 'left');
$this->db->where('p.type', 'group');
$this->db->group_by('p.id');
$this->db->or_where('p.type', 'user');
$res = $this->db->get();
If there is no rating, average_rating will be NULL.
Query should be:
SELECT `p`.`id`, `p`.`name`, AVG(pr.rating)
FROM (`projects` p)
LEFT OUTER JOIN `project_ratings` pr ON `p`.`id` = `pr`.`project_id`
WHERE `p`.`type` = 'group' OR `p`.`type` = 'user';
(I don't know how to do that in CodeIgniter :)
You can get an outer join by do this:
$this->db->join('project_ratings pr', 'p.id = pr.project_id', 'outer');

Categories