Assume I have the follow hasMany() models:
Country > State > City > Street > Person
And I want to retrieve person "John", but also know what country he belongs to:
$person = Person::with(['country'], ['state'], ['city'], ['street'],
['person'] = function ($query) {
$query->where('name', '=', 'John')
});
This generates a separate query for each model which does not seem efficient. Should I use join instead?
From my experience with MySQL 8+, there is not much difference, and I wouldn't use joins.
You can also use this package for nested relations
https://github.com/staudenmeir/eloquent-has-many-deep
Related
Is there a way I can write a derived query in for a CRM Plugin?
Newbie on CRM dev here.
Query looks like this:
SELECT * FROM table1
WHERE table1.ID1 = XXXX AND table1.ID2 NOT IN (
SELECT table2.ID1
FROM table2
WHERE table2.ID2 = XXXX)
Writing the code using a queryexpression.
Unfortunately these kind of complex sql queries cannot be achieved via fetchxml or queryexpression queries. Especially like Subqueries, Not In scenarios.
Probably you need multiple resultset (EntityCollection), one for table1 & another one for table2, then transversing through it.
Another choice is LINQ queries, you can try.
On a side note, you can vote this idea to improve the querying ability.
If you truly are using CRM 2011, last time I checked, this is not possible, newer versions (2013+) you can perform this type of query.
Please see this article: https://msdn.microsoft.com/en-us/library/dn481591.aspx?f=255&MSPPError=-2147217396
var qe = new QueryExpression("table1");
var link = qe.AddLink("table2", "id2", "id1", JoinOperator.LeftOuter);
link.LinkCriteria.AddCondition("id2", ConditionOperator.Equal "XXXX")
link.EntityAlias = "notIn";
qe.Criteria = new FilterExpression();
qe.Criteria.AddCondition("id1", ConditionOperator.Equal, "XXXX");
qe.Criteria.AddCondition("notIn", "id1", ConditionOperator.Null);
You can use this expression with LINQ for CRM:
OrganizationServiceContext oservice = new OrganizationServiceContext(service);
using (oservice)
{
var query = (from table1 in oservice.CreateQuery("new_table1")
join table2 in oservice.CreateQuery("new_table2") on table1["new_table1id"]
equals table2["new_table2id"]
where
table1.GetAttributeValue<EntityReference>("new_id1")
== new Guid("the equal guid or field")
where
table2.GetAttributeValue<EntityReference>("new_id2").Id
!= table1.GetAttributeValue<EntityReference>("new_id1").Id
&& table2.GetAttributeValue<EntityReference>("new_id2").Id
== new Guid("the not equal guid or field")
select table1).ToList();
}
This is another way to QueryExpression. oservice.CreateQuery("new_table1") is the name of your entity in CRM
This works on CRM 2011 too.
For instance, I have a query:
SELECT * FROM
persons
LEFT JOIN vehicles
ON persons.Id = vehicles.OwnerId
I would like execute this query on an EF data context and have array of pairs "person-vehicle". how do I do it?
Another example:
SELECT persons.*, COUNT(vehicles.*) as cnt FROM
persons
JOIN vehicles
ON persons.Id = vehicles.OwnerId
GROUP BY vehicles.Id
Here I want to have a dictionary of a person as a key and number of vehicles he owns as a value.
I know that these quesies are simple enough and it's better to avoid raw sql in these cases. But I want to know possibilities of raw query handling, because real life queries can be much more complex.
You probably want to do some reading ion LINQ to Entities. https://msdn.microsoft.com/en-us/library/vstudio/bb386964(v=vs.100).aspx
The first one is pretty basic:
var persons = context.Persons
.Include(p => p.Vehicles)
.ToList();
The second one is a little more advanced:
var persons = context.Persons
.Select(p => new { Person p, VehicleCount = p.Vehicles.Count() }
.ToList();
You could also do a group by which is described in the link.
This is my real world example.
I have 4 tables:
Person
Plan
Coverage
CoveredMembers
Each person can have many plans, each of those plans can have many coverages. Each of those coverages can have many CoveredMembers.
I need a query that will apply a filter on Plan.PlanType == 1 and CoveredMembers.TermDate == null.
This query should bring back any person who has a medical type plan that is not terminated.
This SQL statement would do just that:
SELECT Person.*, Plans.*, Coverages.*, CoveredMembers.*
FROM Person P
INNER JOIN Plan PL ON P.PersonID = PL.PersonID
INNER JOIN Coverage C on PL.PlanID = C.PlanID
INNER JOIN CoveredMember CM on C.CoverageID = CM.CoverageID
WHERE CM.TermDate = NULL AND PL.PlanType = 1
I have figured out how to do this using anonymous types, but I sometimes need to update the data and save back to the database - and anonymous types are read only.
I was given a solution that did work using JOIN but it only brought back the persons (albeit filtered the way I needed). I can then loop through each person:
foreach (var person in persons) {
foreach (var plan in person.Plans{
//do stuff
}
}
But wouldn't that make a db call for each iteration of the loop? I have 500 persons with 3 unterminated medical plans each, so it would call the db 1500 times?
This is why I want to bring the whole data tree from Persons to CoveredMembers back in one shot. Is this not possible?
I believe this is accomplished in two parts:
Your query to determine the people you wish to have returned based on your criteria as discussed in this question previously: Entity framework. Need help filtering results
Properly setting the navigation properties for entities you want brought together to be eagerly loaded: http://msdn.microsoft.com/en-us/data/jj574232.aspx
For example if your Person entity looks like:
public class Person {
public List<Plan> Plans {get; set;}
...
}
When returning data from the dbcontext you can also use explicit eager loading with the include option:
var people = context.People
.Include(p => p.Plans)
.ToList();
....
If these are nested - coverage is part of plan, etc (which it looks like, it goes something like):
var people = context.People
.Include(p => p.Plans.Select(pl=>pl.Coverage).Select(c=>c.CoveredMembers)))
.ToList();
....
I am making some assumptions about your data model here, and my code above probably needs a little tweaking.
EDIT:
I might need someone else to weigh in here, but I don't think you can add the where clause into an include like that (my example above leads you that way a bit by putting the include on the context object, instead return an IQueryable with your conditions set as solved in your first post (without a ToList() called on it) and then use the code you wrote above without the Where clauses:
From first post (you supplied different criteria in this one, but same concept)
var q = from q1 in dbContext.Parent
join q2 in dbContext.Children
on q1.key equals q2.fkey
join q3 in ........
where q4.col1 == 3000
select q1;
Then:
List<Person> people = q.Include(p => p.Plans
.Select(pl => pl.Coverages)
.Select(c => c.CoveredMembers).ToList();
Again, doing this without being able to troubleshoot - I am sure it would take me a few attempts to iron this one out too.
I've ported some of my Entity from JPA to document and now porting some of my queries.
here is the JPA query:
em.createQuery("select distinct c from CustomerImpl c left join fetch c.addresses ca where (:name is null or c.firstName LIKE :name or c.lastName LIKE :name) and (:ref is null or c.externalReference LIKE :ref) and (:city is null or ca.city LIKE :city) order by c.firstName").setParameter("name", name).setParameter("ref", customerRef).setParameter("city", city).getResultList();
below is my attempt :
Criteria orNameCriteria = new Criteria().orOperator(Criteria.where("firstName").is(null), Criteria.where("firstName").is(name), Criteria.where("lastName").is(name));
Criteria orCustomerRefCriteria = new Criteria().orOperator(Criteria.where("externalReference").is(null), Criteria.where("externalReference").regex(customerRef,"i"));
Criteria orAddress = new Criteria().orOperator(Criteria.where("addresses.city").is(null), Criteria.where("addresses.city").regex(city, "i"));
Query nameq = new Query(new Criteria().andOperator(orNameCriteria,orCustomerRefCriteria,orAddress));
this query return zero size arraylist. I've then changed the orNameCriteria to use is clause and making sure the data contained in name variable has / as suffix and prefix. That didn't work as well.
but queries from mongoVue and RockMongo clients :
{ firstName: /SAM/}
returns data.
Question 1: How do you write LIKE CLAUSE with spring-data-mongo Criteria?
Question 2 : is that the right way to use or and and clause with criteria
Thanks for reading
Criteria.where("field").regex(pattern) should work
Since I don't have the ability add comments...
If you do a static import on Criteria, it will make your where clauses look a lot better.
Criteria orAddress = new Criteria().orOperator(where("addresses.city").is(null), where("addresses.city").regex(city, "i"));
I'm quite a newbie in EF, so I'm sorry if my question has been answered before.. I just can't figure out the syntax..
I have two entities, Category & Product, where one category has many products.
I want to get all categories, with only their latest product (it has a date property named timestamp)
I have no idea how to do that. :-/
If possible I'd like to know the syntax of the two ways to write it, both the sql-like syntax, and the C# like syntax, e.g.:
ctx.Categories.Include("Products").ToList()
from c in ctx.Categories.Include("Products")
Thanks!
Here's the SQL-like way:
var categories =
from p in products
group p by p.Category into g
select new { Category = g.TheKey, LatestProduct = g.Max(p => p.TimeStamp) };
This is the Lambda-way (warning, untested):
var categories = products.GroupBy(p => p.Category)
.Select(g => new { Category = g.TheKey,
LatestProduct = g.Max(p => p.TimeStamp)});
A note on Categories.Include("Products"), you don't need this in your example. You use "Include" for eager-loading, so that for example if you had a list of Categories returned from EF, when you do Categories.Product you will get the associated product.
But all you require is a list of categories, and a single product for each one - which is already returned in the above LINQ query, so no need for Include.