A query load and hydrate a main entity with it's joined entities, which are filtered.
Same query with a different filtering did not update the joined entities.
Some data:
Tiers:
| id | name |
| 1 | alpha |
| 2 | beta |
Container:
| id | tiers_id | category |
| 10 | 1 | A |
| 20 | 1 | A |
| 30 | 1 | B |
| 40 | 1 | B |
Execute 2 queries to get some tiers with theirs containers joined, category A first, then category B:
$dql = "select t, c
from Tiers t
join t.containers c
where t.id in (?1) and c.category = (?2)";
$result = $em->createQuery($dql)
->setParameter(1, array(1))
->setParameter(2, 'A')
->getResult();
$tiers = $result[0];
$containers = $tiers->getContainers(); // tiers 1 with containers 10 and 20, that's fine !
$result = $em->createQuery($dql)
->setParameter(1, array(1))
->setParameter(2, 'B')
->getResult();
$tiers = $result[0];
$containers = $tiers->getContainers(); // BAD HERE: still get containers 10 and 20, looking for containers 30 and 40.
After the 2nd query, tiers 1 preserve its containers loaded during first query. That's not what is expected.
So is there a way to get the containers 30 and 40 after 2nd query ?
Maybe a kind of "reset/detach" the containers of tiers entities after the first query ?
Or anything else...
Mutiple select in queries are used to hydrate tiers with required containers joined.
'getContainers' method gives the expected containers from each tiers.
And cost to BDD is only 1 SQL query, whatever the quantity of tiers searched.
I suppose tiers cannot be detach/reload because they are updated before, between and after the queries, it throw this kind of exception when flushing:
Uncaught Exception: Multiple non-persisted new entities were found through the given association graph
* A new entity was found through the relationship 'XXX' that was not configured to cascade persist operations for entity: XXX\Entity\Tiers#00000000257b87500000000018499b62.
Reset the tiers's containers before 2nd query:
foreach($result as $tiers)
$tiers->nullContainers();
Add method to Entity\Tiers:
public function nullContainers()
{
this->containers = null;
}
Then the 2nd query "refresh" tiers's containers.
Related
I am trying to write a query that will get me the average time between 2 custom events, sorted by user session. I have added custom tracking events throughout this application and I want to query the time it takes the user from 'Setup' event to 'Process' event.
let allEvents=customEvents
| where timestamp between (datetime(2019-09-25T15:57:18.327Z)..datetime(2019-09-25T16:57:18.327Z))
| extend SourceType = 5;
let allPageViews=pageViews
| take 0;
let all = allEvents
| union allPageViews;
let step1 = materialize(all
| where name == "Setup" and SourceType == 5
| summarize arg_min(timestamp, *) by user_Id
| project user_Id, step1_time = timestamp);
let step2 = materialize(step1
| join
hint.strategy=broadcast (all
| where name == "Process" and SourceType == 5
| project user_Id, step2_time=timestamp
)
on user_Id
| where step1_time < step2_time
| summarize arg_min(step2_time, *) by user_Id
| project user_Id, step1_time,step2_time);
let 1Id=step1_time;
let 2Id=step2_time;
1Id
| union 2Id
| summarize AverageTimeBetween=avg(step2_time - step1_time)
| project AverageTimeBetween
When I run this query it produces this error message:
'' operator: Failed to resolve table or column or scalar expression named 'step1_time'
I am relatively new to writing queries with AI and have not found many resources to assist with this problem. Thank you in advance for your help!
I'm not sure what the let 1id=step1_time lines are intended to do.
those lines are trying to declare a new value, but step1_time isn't a thing, it was a field in another query
i'm also not sure why you're doing that pageviews | take 0 and unioning it with events?
let allEvents=customEvents
| where timestamp between (datetime(2019-09-25T15:57:18.327Z)..datetime(2019-09-25T16:57:18.327Z))
| extend SourceType = 5;
let step1 = materialize(allEvents
| where name == "Setup" and SourceType == 5
| summarize arg_min(timestamp, *) by user_Id
| project user_Id, step1_time = timestamp);
let step2 = materialize(step1
| join
hint.strategy=broadcast (allEvents
| where name == "Process" and SourceType == 5
| project user_Id, step2_time=timestamp
)
on user_Id
| where step1_time < step2_time
| summarize arg_min(step2_time, *) by user_Id
| project user_Id, step1_time,step2_time);
step2
| summarize AverageTimeBetween=avg(step2_time - step1_time)
| project AverageTimeBetween
if I remove the things I don't understand (like union with 0 pageviews, and the lets, I get a result, but I don't have your data so I had to use other values than "Setup" and "Process" so I don't know if it is what you expect?
you might want to look at the results of the step2 query without the summarize to just see what you're getting matches what you expect.
I Have two tables : Post and Comment in a Many to One relation.
table post table comment
p_id | name | u_id c_id | p_id | comment | date
1 | post 1 | 1 1 | 1 | blah blah blah | 2017-01-01
2 | post 2 | 2 2 | 1 | blah blah blah | 2017-01-04
3 | post 3 | 1 3 | 2 | blah blah blah | 2017-01-07
... ...
I would like to retrieve all Posts by u_id with their 3 Last comments ordered by date.
I would do :
SELECT p, c
FROM p
LEFT JOIN p.comments c WITH c.date IN (SELECT c2.date) FROM App\Bundle\Entity\Comment as c2 WHERE c2.post = p.p_id ORDER BY date ASC LIMIT 3)
WHERE p.user = 1
But doctrine doesn't allow LIMIT, how i can do that ?
In my opinion the easiest way to to that is by using the function
setMaxResults($limit);
I give you an example :
$qb = $this->createQueryBuilder('a');
$qb
->innerJoin('a.advert', 'adv')
->addSelect('adv')
;
$qb->setMaxResults($limit);
return $qb
->getQuery()
->getResult()
I hope it will help you
As I known QueryBuilder::setMaxResult() work only on primary table not on subquery.
IMHO it's best to write it in classic SQL, where you have better control on subquery and what is joining. In this case you get plain array not array of objects with relation. But, this is better when you need only to show lists of posts and not interact with single entity. In this case getting entire object hydrating one by one populating from DQL, is quite slower then using plain array from SQL.
You could do this like that:
$conn = $this->getEntityManager()->getConnection();
$stmt = $conn->prepare('SELECT ...');
$stmt->execute($parameters);
$results = $stmt->fetchAll(\PDO::FETCH_ASSOC);
I am storing data in standard tables in a MariaDB, but would like to return records from related tables as a JSON string.
What I intend to do is have a function where I can pass in exerciseId and the function returns a JSON string of all related exerciseMuscle records, meaning each exercise record returned by a stored proc can also include nested data from child tables.
I have been able to create JSON records using COLUMN_JSON and COLUMN_CREATE but can only get this to return as a set of individual records, rather than an array of JSON values as a need. The SQL I'm using is:
select
e.id,
CONVERT(COLUMN_JSON(COLUMN_CREATE(
'role', em.muscleRoleName,
'muscle', em.muscleName
)) USING utf8) as musclesJson
from
exercise e
inner join exerciseMuscle em
on e.id = em.exerciseId
where
e.id = 96;
This returns:
| id | musclesJson
| 96 | {"role":"main","muscle":"biceps"}
| 96 | {"role":"secondary","muscle":"shoulders"}
When what I want is:
| id | musclesJson
| 96 | [{"role":"main","muscle":"biceps"},{"role":"secondary","muscle":"shoulders"}]
Is it possible to return multiple results in one row without having to iterate through the results and build it manually? If I add a group by to the SQL then the JSON only includes the first record.
Turns out it was GROUP_CONCAT that I needed, and specifying a comma as the delimiter. So changing my SQL to:
select
e.id,
CONVERT(
GROUP_CONCAT(
COLUMN_JSON(
COLUMN_CREATE(
'role', em.muscleRoleName,
'muscle', em.muscleName
)
)
SEPARATOR ','
) USING utf8) as muscles
from
exercise e
inner join exerciseMuscle em
on e.id = em.exerciseId
where
e.id = 96;
Returns:
| id | musclesJson
| 96 | {"role":"main","muscle":"biceps"},{"role":"secondary","muscle":"shoulders"}
I have a entity Article20000Information, with a few fields like: id, description, manufacturer_id, supplier_id
I have another entity, Organisation. It has a list of companies (both manufacturers & suppliers) each with an id.
I also have a page which renders a list of Article20000Information data. Currently, it simply displays the data in the table so:
| id | Description | Manufacturer | Supplier | Price |
|----|-------------|--------------|----------|--------|
| 1 | thing1 | 2 | 5 | 34 |
| 2 | thing2 | 5 | 2 | 23 |
| 3 | thing3 | 3 | 4 | 25 |
What I need is for the manufacturer and supplier column to display the name value from the organisation table, based on the id shown.
What is the best way to go about this?
Got it!
I needed multiple aliases, which I'd guessed, but I also needed to give them AS so that they come out with different column names. This in turn lets twig render the tags.
<?php
namespace Regenerys\QMSBundle\Entity;
use Doctrine\ORM\EntityRepository;
class Article20000InformationRepository extends EntityRepository
{
public function findStuff()
{
return $this->getEntityManager()
->createQuery(
'SELECT
A.id,
A.articleNumber,
A.description,
B.name as manufacturer,
C.name as supplier
FROM
RegenerysQMSBundle:Article20000Information A
LEFT OUTER JOIN RegenerysQMSBundle:Organisation B WITH B.id = A.manufacturer
LEFT OUTER JOIN RegenerysQMSBundle:Organisation C WITH C.id = A.supplier '
)
->getResult();
}
}
Thanks to #Alexandru for his DQL help.
You need to join the two tables based on id condition.
select A.id, A.Description, B.ManufacturName, B.supplierName
from Article20000Information A
left outer join Organisation B
ON B.id = A.id
More info on table joins.
If you are using doctrine, a proper way is to create a Repository class, and there write your joined code proposed by #K139 but in DQL:
class Article20000InformationRepository extends EntityRepository
{
public function findAll()
{
return $this->getEntityManager()
->createQuery(
'SELECT A.id, A.Description, B.ManufacturName, B.supplierName FROM AppBundle:Article20000Information A
LEFT OUTER JOIN AppBundle:Organisation B ON B.id = A.id '
)
->getResult();
}
}
Then in your controller you will use it:
$articles = $em->getRepository('AppBundle:Article20000Information')->findAll();
I am trying to place a variable into the field name in the query , so I have a schema:
id amazon - tesco - asda - happyshopper -
1 £5 - NULL - NULL - £4.99
2 NULL - £2.99 - NULL - NULL
and then
$store = 'amazon';
$qb = $em->createQueryBuilder();
$products = $qb->select('p')->from('MyBundle:Product', 'p')
->where('p.:store IS NOT NULL')
->setParameter('store', $store)
->add('orderBy', 'p.onSale DESC')
->setMaxResults(40)
->getQuery()
->getResult();
Would return row 1.
What I've done for :
->where('p.:store IS NOT NULL')
->setParameter('store', $store)
Is incorrect and it errors.
->where(':store IS NOT NULL')
->setParameter('store', $store)
does not error, but doesn't apply the store filter.
The short answer here is to just work the store name into the string manually:
->where("p.$store IS NOT NULL")
or
->where('p.' . $store . ' IS NOT NULL')
The long answer is that your database schema could use some work. What happens, for example, if/when you want to add a new store? Are you going to add a new column and re-code the whole thing? The better solution is to separate the concept of "store", put it in its own table, and join everything together in a different table. Something like this:
Product:
id | name | onSale
1 | foo | 1
2 | bar | 0
Store:
id | name
1 | amazon
2 | tesco
3 | asda
4 | happyshopper
Price:
id | productId | storeId | price
1 | 1 | 1 | 5
2 | 1 | 4 | 4.99
3 | 2 | 2 | 2.99
Once you configure your tables and your mappings properly, your query turns in to:
$qb = $em->createQueryBuilder();
$products = $qb
->select('product')
->from('MyBundle:Price', 'price')
->innerJoin('price.product', 'product')
->innerJoin('price.store', 'store')
->where('store.name = :store')
->setParameter('store', $store)
->add('orderBy', 'product.onSale DESC')
->setMaxResults(40)
->getQuery()
->getResult();