EF Query: How to constraint nested items - entity-framework

Please help me write a query for EF.
I have the following tables:
Products
ProductId
Name
Items
ProductId
Cost
How to select product with name 'AAA' and ONLY with items which cost equals to 100?
I wrote the following:
ctx.Products.Include("Items").Where(p=>p.Name == "AAA" && p.Items.Any(i=>i.Cost == 100)).FirstOrDefault()
but as a result I got Product with name "AAA" and with ALL items.
Thanks,
Dmitriy

That's because you are loading all items for products. In any case, I'd try going the other way, i.e. the main query might be something like this
from i in items
where i.Product.Name == "AAA" && i.Cost == 100
select i
All these items should have the same product and you can eager load the product too if you need.
ctx.Items.Include("Products")

Related

How can I order an IQueryable based on a field that is in another table?

I am a newbie to LINQ and c# and I have two tables :
Products
ProductStoreds
ProductStore has a foreignKey to Products with a field named ProductId(same for both tables).
I have an IQueryable named result from Products table. how can I sort it based on a field named status in ProductStore Table.
this is what I have tried and I have not been successful:
result = result.Where(p =>
p.ProductId == DbContext.ProductStores.Select(m => m.Product)
.OrderByDescending(m => m.Status).ToInt());
You can join and order by Product like this
var stores = DbContext.ProductStores;
result = from store in stores
join product in result on product.ProductId equals store.ProductId
orderby store.Status
select product;

Rails where.not with multiple arguments

I have an array of ID's I would like to preserve and then delete the rest, we have 3 types of users, Admins, Investors and Students. The list I have is for Students id's, so I would like to delete all .
If I try run this
User.where.not(id: GOOD_LIST).count(:all)
I will get 15k records. but if I try add an additional condition
User.where.not(id: GOOD_LIST).where(type: "Student").count(:all)
or
User.where.not('id IN ? AND type = ?', GOOD_LIST, "Student").count(:all)
It will return 0 records.
I tried just using the where method
User.where('id NOT IN ?', GOOD_LIST).where(type: "Student").count(:all)
but got the following error message
(10.7ms) SELECT COUNT(*) FROM "users" WHERE "users"."type" = 'Student' AND (id NOT IN 39999,40000,40001,40002,40003,40004,40005,40006,40007,40008)
PG::SyntaxError: ERROR: syntax error at or near "39999"
LINE 1: ... WHERE "users"."type" = 'student' AND (id NOT IN 39999,4000...
What is the best way of doing this?
That generated SQL is missing parentheses around the list. So we should add them in your template, like this
.where('id NOT IN (?)', GOOD_LIST)

Select most reviewed courses starting from courses having at least 2 reviews

I'm using Flask-SQLAlchemy with PostgreSQL. I have the following two models:
class Course(db.Model):
id = db.Column(db.Integer, primary_key = True )
course_name =db.Column(db.String(120))
course_description = db.Column(db.Text)
course_reviews = db.relationship('Review', backref ='course', lazy ='dynamic')
class Review(db.Model):
__table_args__ = ( db.UniqueConstraint('course_id', 'user_id'), { } )
id = db.Column(db.Integer, primary_key = True )
review_date = db.Column(db.DateTime)#default=db.func.now()
review_comment = db.Column(db.Text)
rating = db.Column(db.SmallInteger)
course_id = db.Column(db.Integer, db.ForeignKey('course.id') )
user_id = db.Column(db.Integer, db.ForeignKey('user.id') )
I want to select the courses that are most reviewed starting with at least two reviews. The following SQLAlchemy query worked fine with SQlite:
most_rated_courses = db.session.query(models.Review, func.count(models.Review.course_id)).group_by(models.Review.course_id).\
having(func.count(models.Review.course_id) >1) \ .order_by(func.count(models.Review.course_id).desc()).all()
But when I switched to PostgreSQL in production it gives me the following error:
ProgrammingError: (ProgrammingError) column "review.id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT review.id AS review_id, review.review_date AS review_...
^
'SELECT review.id AS review_id, review.review_date AS review_review_date, review.review_comment AS review_review_comment, review.rating AS review_rating, review.course_id AS review_course_id, review.user_id AS review_user_id, count(review.course_id) AS count_1 \nFROM review GROUP BY review.course_id \nHAVING count(review.course_id) > %(count_2)s ORDER BY count(review.course_id) DESC' {'count_2': 1}
I tried to fix the query by adding models.Review in the GROUP BY clause but it did not work:
most_rated_courses = db.session.query(models.Review, func.count(models.Review.course_id)).group_by(models.Review.course_id).\
having(func.count(models.Review.course_id) >1) \.order_by(func.count(models.Review.course_id).desc()).all()
Can anyone please help me with this issue. Thanks a lot
SQLite and MySQL both have the behavior that they allow a query that has aggregates (like count()) without applying GROUP BY to all other columns - which in terms of standard SQL is invalid, because if more than one row is present in that aggregated group, it has to pick the first one it sees for return, which is essentially random.
So your query for Review basically returns to you the first "Review" row for each distinct course id - like for course id 3, if you had seven "Review" rows, it's just choosing an essentially random "Review" row within the group of "course_id=3". I gather the answer you really want, "Course", is available here because you can take that semi-randomly selected Review object and just call ".course" on it, giving you the correct Course, but this is a backwards way to go.
But once you get on a proper database like Postgresql you need to use correct SQL. The data you need from the "review" table is just the course_id and the count, nothing else, so query just for that (first assume we don't actually need to display the counts, that's in a minute):
most_rated_course_ids = session.query(
Review.course_id,
).\
group_by(Review.course_id).\
having(func.count(Review.course_id) > 1).\
order_by(func.count(Review.course_id).desc()).\
all()
but that's not your Course object - you want to take that list of ids and apply it to the course table. We first need to keep our list of course ids as a SQL construct, instead of loading the data - that is, turn it into a derived table by converting the query into a subquery (change the word .all() to .subquery()):
most_rated_course_id_subquery = session.query(
Review.course_id,
).\
group_by(Review.course_id).\
having(func.count(Review.course_id) > 1).\
order_by(func.count(Review.course_id).desc()).\
subquery()
one simple way to link that to Course is to use an IN:
courses = session.query(Course).filter(
Course.id.in_(most_rated_course_id_subquery)).all()
but that's essentially going to throw away the "ORDER BY" you're looking for and also doesn't give us any nice way of actually reporting on those counts along with the course results. We need to have that count along with our Course so that we can report it and also order by it. For this we use a JOIN from the "course" table to our derived table. SQLAlchemy is smart enough to know to join on the "course_id" foreign key if we just call join():
courses = session.query(Course).join(most_rated_course_id_subquery).all()
then to get at the count, we need to add that to the columns returned by our subquery along with a label so we can refer to it:
most_rated_course_id_subquery = session.query(
Review.course_id,
func.count(Review.course_id).label("count")
).\
group_by(Review.course_id).\
having(func.count(Review.course_id) > 1).\
subquery()
courses = session.query(
Course, most_rated_course_id_subquery.c.count
).join(
most_rated_course_id_subquery
).order_by(
most_rated_course_id_subquery.c.count.desc()
).all()
A great article I like to point out to people about GROUP BY and this kind of query is SQL GROUP BY techniques which points out the common need for the "select from A join to (subquery of B with aggregate/GROUP BY)" pattern.

How to write query in Entity Framework

I'm new to Entity Framework. I have a database query which I need to convert to Entity Framework. how to write the query in LinQ to Enity
Can someone help me on that?
SELECT
FLD1,
FLD2,
SUM(FLD3),
(TO_CHAR(FLD4,'MM/DD/YYYY'))
FROM
TABLE1
WHERE
(FLD2=XXX ) AND
(FLD3 BETWEEN TO_DATE(VARDATE,'MMDDYYYY') AND TO_DATE(VARDATE1,'MMDDYYYY'))
GROUP BY
FLD1,
FLD2,
FLD4
Well...info is sparse and you filled it with a lot of different cases something like this would do.
_context.SomeObject
.Where(x=>x.SomeField == "SomeValue" && x.SomeField > 5 && x.SomeField < 10)
.Select(x=>new { x.SomeField1, x.SomeField2, x.SomeField2, SomeField4 = x.SomeChildCollection.Sum(y=>y.SomeChildvalue)
.GroupBy(x=>new {x.SomeField1, x.SomeField2, x.SomeField3})
.ToList()
This would result in a group where the key was an object with the values SomeField1, SomeField2, SomeField3, and the object would be the an anonymous projection with the 4 properties in the Select.
In some kinds of comparisions regarding dates you might need to use EntityFunctions.

Entity Sql for a Many to Many relationship

Consider two tables Bill and Product with a many to many relationship. How do you get all the bills for a particular product using Entity Sql?
Something like this
SELECT B FROM [Container].Products as P
OUTER APPLY P.Bills AS B
WHERE P.ProductID == 1
will produce a row for each Bill
Another option is something like this:
SELECT P, (SELECT B FROM P.Bills)
FROM [Container].Products AS P
WHERE P.ProductID == 1
Which will produce a row for each matching Product (in this case just one)
and the second column in the row will include a nested result set containing the bills for that product.
Hope this helps
Alex
You need to use some linq like this;
...
using (YourEntities ye = new YourEntities())
{
Product myProduct = ye.Product.First(p => p.ProductId = idParameter);
var bills = myProduct.Bill.Load();
}
...
This assumes that you have used the entitiy framework to build a model for you data.
The bills variable will hold a collection of Bill objects that are related to your product object.
Hope it helps.