I've got a event table with title and description of an event, and i've got a eventInstance table where the dates and venues of an event are stored. So it's a one to n relation: one event can have many instances.
What i'm trying now is to select only the most current event instance for an event. In SQL the query is:
select e.id, e.title, ei.start_date
from event e
LEFT join event_instance ei on ei.id =
(SELECT id FROM event_instance ei where ei.event_id = e.id ORDER BY start_date asc, start_time asc LIMIT 1)
ORDER BY start_date asc, start_time asc LIMIT 20;
I'm trying to rewrite the sql command in dql. So far i've got this:
SELECT e, ei
FROM AppBundle:Event e LEFT JOIN e.eventInstances ei WITH ei =
(SELECT a FROM AppBundle:EventInstance a WHERE a.event = e ORDER BY a.startDate asc, a.startTime asc)
My problem is, there's no LIMIT command in dql, so i cannot limit the subquery to give me one result. So the error i'm getting when executing this query is:
SQLSTATE[21000]: Cardinality violation: 7 ERROR: more than one row returned by a subquery used as an expression
Is there any way i can make this work?
Create a subquery then set max results on that.
$subDql = 'SELECT a
FROM AppBundle:EventInstance a
WHERE a.event = e
ORDER BY a.startDate asc, a.startTime asc';
$subQuery = $this->getEntityManager()
->createQuery($subDql)
->setMaxResults(1)
;
$dql = 'SELECT e, ei FROM AppBundle:Event e
LEFT JOIN e.eventInstances ei
WITH ei = (' .$subQuery . ')'
;
$query = $this->getEntityManager()
->createQuery($dql)
->setMaxResults($yourOtherLimit)
;
$resultCollection = $query->getResult();
An equivalent DQL for your solution (to get latest row per group) will be something like
SELECT e,a
FROM AppBundle:Event e
JOIN e.eventInstances a
LEFT JOIN AppBundle\Entity\Score b
WITH a.event = b.event
AND a.startDate < b.startDate
AND a.startTime < b.startTime
WHERE b.event IS NULL
ORDER BY a.startTime DESC
OR
SELECT e,a
FROM AppBundle:Event e
JOIN e.eventInstances a
LEFT JOIN AppBundle\Entity\Score b
WITH a.event = b.event
AND a.startDate = b.startDate
AND a.startTime < b.startTime
WHERE b.event IS NULL
ORDER BY a.startTime DESC
Related
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 have a query that LEFT JOINS another table to group rows. I am trying to only select records from the first table "googleimage" where the user_id is a certain number. (user_id is a column in "googleimage" table.
SELECT g.*
FROM googleimage g
LEFT JOIN (SELECT image_id, COUNT(*) AS cnt
FROM googleimagefound WHERE status = 0
GROUP BY image_id) gf ON gf.image_id = g.id
ORDER BY COALESCE(cnt, 0) DESC");
I have tried adding a new ON statement beiside the
gf ON gf.image_id = g.id
I have also tried changing
SELECT g.*
FROM googleimage g
to
SELECT g.*
FROM googleimage WHERE user_id = 1 g
but none of them seem to work, any help will be appriciated
Try changing you query a bit like below
SELECT g.*
FROM googleimage g
LEFT JOIN (SELECT image_id, COUNT(*) AS cnt
FROM googleimagefound WHERE status = 0
GROUP BY image_id) gf ON gf.image_id = g.id
WHERE g.user_id = 1 <-- add this condition
ORDER BY COALESCE(gf.cnt, 0) DESC;
In my mysql query, I try to get all threads with their most recent row.
$query = "SELECT th.id, tm.message, tm.date_sent, tm.date_sent>tu.last_read_date AS new
FROM thread th
JOIN thread_user tu ON th.id=tu.thread_id AND tu.user_id={$user_id}
JOIN thread_message tm ON th.id=tm.thread_id
JOIN (
SELECT thread_id, MAX(date_sent) date_sent
FROM thread_message
GROUP BY thread_id
) q ON tm.thread_id = q.thread_id AND tm.date_sent = q.date_sent
ORDER BY tm.date_sent DESC";
This works, but the problem is, if there is two or more rows who's date is the most recent and they are the same date, then it will join with both of them. I need that third join statement to join with at most 1 row.
I also don't want to assume that the biggest id implies its the most recent row, because I could always change the dates manually later.
Does anyone know how to fix this?
Thanks
One way to do this is to establish a row number per group, in this case your group is thread_id and date_sent. With MySql, you need to use user-defined variables to do this:
SELECT th.id,
tm.message,
tm.date_sent,
tm.date_sent>tu.last_read_date AS new
FROM thread th
JOIN thread_user tu ON th.id=tu.thread_id AND tu.user_id={$user_id}
JOIN (
SELECT id,
thread_id,
message,
date_sent,
#rn:=IF(#prevthread_id=thread_id, #rn+1, 1) rn,
#prevthread_id:=thread_id
FROM thread_message, (SELECT #rn:=1, #prevthread_id:=0) t
ORDER BY thread_id, date_sent DESC, id
) tm ON th.id=tm.thread_id
AND tm.rn = 1
ORDER BY tm.date_sent DESC
Perhaps this is easier for you (but only because you're using mysql):
SELECT th.id,
tm.message,
tm.date_sent,
tm.date_sent>tu.last_read_date AS new
FROM thread th
JOIN thread_user tu ON th.id=tu.thread_id AND tu.user_id={$user_id}
JOIN thread_message tm ON th.id=tm.thread_id
JOIN (
SELECT thread_id,
id,
MAX(date_sent) date_sent
FROM thread_message
GROUP BY thread_id
) q ON tm.thread_id = q.thread_id
AND q.id = tm.id
AND tm.date_sent = q.date_sent
ORDER BY tm.date_sent DESC
This will return an arbitrary id to join on.
Seems to me that if the query produces what you expect, with exception of the last JOIN you can just modify the GROUP BY which will only return one row.
JOIN (
SELECT thread_id, MAX(date_sent) date_sent
FROM thread_message
GROUP BY thread_id
) q ON tm.thread_id = q.thread_id AND tm.date_sent = q.date_sent
GROUP BY tm.date_sent
ORDER BY tm.date_sent DESC";
I am trying to fetch record of a customer's latest transaction. The query I am trying is this:
SELECT
food_rate,
ambiance_rate,
service_rate,
cost_rate
FROM
tbl_transaction t
INNER JOIN tbl_feedback f
ON t.fid = f.fid
WHERE date_time IN
(SELECT
MAX(date_time)
WHERE c_id = 1)
The output will be a single row only. But it is giving syntax error.
Try this:
SELECT
food_rate,
ambiance_rate,
service_rate,
cost_rate
FROM
tbl_transaction t
INNER JOIN tbl_feedback f
ON t.fid = f.fid
WHERE c_id = 1
ORDER BY date_time DESC
LIMIT 1;
//you have not written table name in your subquery, i have rewritten the query
SELECT
food_rate,
ambiance_rate,
service_rate,
cost_rate
FROM
tbl_transaction t
INNER JOIN tbl_feedback f
ON t.fid = f.fid
WHERE date_time IN
(SELECT
MAX(date_time) from tbl_transaction
WHERE c_id = 1)
Try this
SELECT food_rate, ambiance_rate, service_rate, cost_rate
from tbl_transaction t
inner join tbl_feedback f ON t.fid=f.fid
where c_id=1
order by date_time desc
limit 1