Is it possible to combine fetch joins and COUNT in Doctrine? - symfony

I want to fetch join some entities to avoid a lot of additional queries and I also want to get count of related collection.
Something like this:
SELECT u, a, count(p) properties_count
FROM User u
JOIN u.address a
LEFT JOIN u.properties p
group by u.id
That is I want to get a collection like [[0 => User, 'properties_count' => 42], [0 => ...], ...].
It works without fetch join (SELECT u, count(p) properties_count) but with SELECT u, a, count(p) properties_count it seems to not include count in the
result.
Am I doing something wrong?

Looks like it was just a mistake in the original query.
It was using joins like this:
LEFT JOIN Address a WITH a.id = u.address
When I replaced this to
LEFT JOIN u.address a
it started working. (I thought they were equivalent)

Related

EF Core - Count from a specific column

I almost have my EF Core query working... This is the SQL getting produced (notice the Count(*):
SELECT [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn], COUNT(*) AS [Clicks]
FROM [URLs] AS [u]
LEFT JOIN [OwnerUrls] AS [o] ON [u].[Key] = [o].[ShortUrlKey]
LEFT JOIN [Clicks] AS [c] ON [u].[Key] = [c].[ShortUrlKey]
GROUP BY [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn]
What I need is (have Count look at a specific column/table)
SELECT [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn], COUNT(c.ID) AS [Clicks]
FROM [URLs] AS [u]
LEFT JOIN [OwnerUrls] AS [o] ON [u].[Key] = [o].[ShortUrlKey]
LEFT JOIN [Clicks] AS [c] ON [u].[Key] = [c].[ShortUrlKey]
GROUP BY [u].[Key], [u].[Url], [u].[CreatedBy], [u].[CreatedOn]
Here is the EF Query that I'm using...
query = (from u in db.URLs
join ou in db.OwnerUrls on u.Key equals ou.ShortUrlKey into urlOwners
from subSet in urlOwners.DefaultIfEmpty()
join c in db.Clicks on u.Key equals c.ShortUrlKey into urlClicks
from subClicks in urlClicks.DefaultIfEmpty()
group subClicks by new { u.Key, u.Url, u.CreatedBy, u.CreatedOn } into g
select new ShortURL()
{
Key = g.Key.Key,
Url = g.Key.Url,
CreatedBy = g.Key.CreatedBy,
CreatedOn = g.Key.CreatedOn,
Clicks = g.Count()
});
I've tried changing the g.Count() to g.Select(x=>x.Id).Count() and that just causes EF Core to barf and complain about client side evaluation vs server side evaluation etc..
I should mention that the reason I'm joining the first model (OwnerUrls) is to support a where clause that I didn't include here...
Thanks!
I'm not a EF developer, but have worked with SQL Server for a while now. In SQL Server i would use COUNT(DISTINCT c.ID) to eliminate any duplicates you might get from JOINS.
If duplicates are impossible due to the model the COUNT(*) shoud be sufficient.
Maybe this might help:
https://entityframeworkcore.com/knowledge-base/51892585/linq-select-distinct-count-performed-in-memory

how can i connect my table to another table in asp.vb?

Sorry if this seems simple. Either I'm not asking the right question, or understanding the answers I've found so far.
i need to join columns
(Ex. tbl_lockers has Employee and tbl_employee has Name column)
I need to join the same data from tbl_employee.Name to tbl_lockers.Employee
Any thoughts? Thanks.
I'm a newbie in SQL.
Try using:
SELECT * FROM
tbl_employee, tbl_lockers
WHERE tbl_employee.Name = tbl_lockers.Employee
Specifing the exact columns may be needed if both tables contain columns with the same names.
To show all employees who have a locker (ie. all criteria match), do this...
SELECT e.*, l.*
FROM tbl_employee e
INNER JOIN tbl_lockers l ON l.Employee = e.Name
To show all employees whether they have a locker or not, change the JOIN Criteria to...
LEFT JOIN tbl_lockers l ON l.Employee = e.Name
To show all lockers whether they have a employee or not, change JOIN Criteria to...
RIGHT JOIN tbl_lockers l ON l.Employee = e.Name

Sqlite double left outer join with count

I have the following DB structure:
tbl_record(_id,_id_user,...)
tbl_photo(_id,_id_record,...)
tbl_note(_id,_id_record,...)
When listing the records of a specific user while counting the number of photos a record has, I use the following query, which works fine:
SELECT tbl_record._id, COUNT(tbl_photo._id_record) AS photo_count FROM tbl_record
LEFT OUTER JOIN tbl_photo ON tbl_record._id=tbl_photo._id_record
WHERE tbl_record._id_user=? GROUP BY tbl_record._id;
Now, I'd like to do the same as above, but also count the number of notes a record has:
SELECT tbl_record._id, COUNT(tbl_photo._id_record) AS photo_count, COUNT(tbl_note._id_record) AS note_count FROM tbl_record
LEFT OUTER JOIN tbl_photo ON tbl_record._id=tbl_photo._id_record
LEFT OUTER JOIN tbl_note ON tbl_record._id=tbl_note._id_record
WHERE tbl_record._id_user=? GROUP BY tbl_record._id;
The count of the 2nd query does not work properly when a record has >0 photos & >0 notes, e.g. 3 photos & 5 photos which results in a count of 15 (3*5) for each.
Any idea how to make the 2nd query return the proper counts?
Thanks!!
You might be able to filter out duplicates by using COUNT(DISTINCT some_id), but this would be inefficient.
Better use correlated subqueries:
SELECT _id,
(SELECT COUNT(*)
FROM tbl_photo
WHERE _id_record = tbl_record._id
) AS photo_count,
(SELECT COUNT(*)
FROM tbl_note
WHERE _id_record = tbl_record._id
) AS note_count
FROM tbl_record
WHERE _id_user = ?

Doctrine2 - need some help modifying working dql

I have the following dql SELECT query, which works:
SELECT q AS question, AVG(r.score) AS average
FROM MyBundle:Question q
JOIN q.ratings r
WHERE q.deleted IS NULL
GROUP BY q.id
ORDER BY q.id ASC
What I'd like to do is make it return 0 if AVG(r.score) is either 0 or null. I'm just not sure how to do it. Is it possible to do something like:
SELECT q AS question, (AVG(r.score) OR 0) AS average FROM....
Since you can't use aggregate function in a WHERE clause, you must use HAVING instead. That should do the trick:
SELECT q AS question, AVG(r.score) AS average
FROM MyBundle:Question q
JOIN q.ratings r
WHERE q.deleted IS NULL
GROUP BY q.id
HAVING average IN (0, NULL)
ORDER BY q.id ASC
Are you looking for an alternative of mysql IFNULL to a doctrine query builder. Look at this question.
Changing my (INNER) JOIN to a LEFT OUTER JOIN gave me the results I was looking for.

LINQ - EF join difficulty

I have two tables:
Phase :
long ID
string Name
and another Activity :
long ID
string Name
long PhaseID
I already know the name of the phases and I want to get the activity for those particular phases. Do i add PhaseName to the activity table or do I do it through join in LINQ?
Maybe something like this?
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
... and here im not sure how to finish this query..
Thanks for your help!
The code that you've provided will use an Inner Join to find all Activities where the Phase with Name "Preplanning" exists.
To finish your query you need to add a select clause.
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
select a.Name
will return IEnumerable<string> of all activity names.
Just select activity, as you want:
var query = from a in entities.Activities
join p in entities.Phases on a.PhaseId equals p.Id
where p.Name == "Preplanning"
select a;
Here is how query expression should look like:
A query expression must begin with a from clause and must end with a select or group clause. Between the first from clause and the last select or group clause, it can contain one or more of these optional clauses: where, orderby, join, let and even additional from clauses. You can also use the into keyword to enable the result of a join or group clause to serve as the source for additional query clauses in the same query expression.
Same as puzzling image:
With method syntax you don't need to end query with something special:
var query = entities.Phases
.Where(p => p.Name == "Preplanning")
.Join(entities.Activities, p => p.Id, a => a.PhaseId, (p,a) => a);
No need to do a join if you only need data from one of the tables. You can apply a filter instead:
var q = entities.Activities.Where(a =>
entities.Phases.Any(p => a.PhaseId == p.Id && p.Name == "Preplanning"));

Resources