Linq Take is being ignored in SQL statement if a group join is included - linq-to-sql

Essentially, the generated SQL from Linq appears to be ignoring the "Take" command when we include a group join of the following Invoices table.
var queryCommand = from m in _context.CustomerInfo
join sq in customerAnalysisGroupQuery on m.CustomerInfoId equals sq.customerAnalysisGroupQueryCustomerInfoId
join igq in _context.Invoices on m.CustomerInfoId equals igq .invoicesGroupQueryCustomerInfoId into igq2
from igq in igq2.DefaultIfEmpty()
select new TableModelForServerPagination()
{
reportInfo = sq,
customerInfo = m,
invoicesInfo = igq
};
var queryList = queryCommand.Take(50).ToList();
The invoices table may contain multiple invoices for each customer. The analysis query generates one analysis for each customer.
The generated SQL code results in
one very long sql query which ends incorrectly with
Order by m.CustomerInfoId
If we ommit the invoices table, the generated code results in
one very long sql query which ends correctly with
LIMIT 50
Taking the wrongly generated SQL query and adding "LIMIT 50" to it produces the same results as the correctly generated SQL query.
We are using MariaDB and entity .net core 3.1. Might anyone have an insight as to what happened here?

It turns out we were using a IEnumerable to store invoicesInfo. Once that's removed, it generates the query correctly with the Limit 50.

Related

Why does the SUM function in MySQL get rid of several columns of data

I am trying to make a mock database that displays the equipment data for three separate game characters. When I run the following SQL I am able to pull up the full table
SELECT customerID, characters.characterID, characterName, headName, chestName, legsName, armsName
FROM characters
JOIN character_equipmentv ON characters.characterID = character_equipment.characterID
JOIN gear_head ON character_equipment.headID = gear_head.headID
JOIN gear_chest ON character_equipment.chestID = gear_chest.chestID
JOIN gear_legs ON character_equipment.legsID = gear_legs.legsID
JOIN gear_arms ON character_equipment.armsID = gear_arms.armsID
ORDER BY characterID
However when I add the SUM function to get the character's gearscore:
SUM(gear_head.gearScore + gear_chest.gearScore + gear_legs.gearScore + gear_arms.gearScore) AS gearScore
To the end of my SELECT statement, it only displays the first entry with the combined gearscore for every character! How can I fix this?
Check out SQL Grouping.
Okay: SUM is an aggregate function and requires a GROUP BY clause to produce meaningful results based on another column.
SUM is an aggregating function, that means is takes multiple rows and merges them.
Your SQL will tell you the sum of all the gear scores for all characterIDs
if you want the sum for a given characterID add GROUP BY characterID to the end of your query.

Group By in stored procedure error

I created this stored procedure which is basically to return a list of offices with the type of activities that happen within each office. The results i reported to reportviewer but i noticed that for each activity return it creates a table - so i can have 5 different tables each with its own activity but all happen in the same office. I want the report to be a table for each office which will contain as many activites as there are for each office. So i thought that if i grouped in my stored procedure my results will be as what i want but i am getting column error saying: "...is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause."
I am not sure how to go about that but here is my select, from, where, group by statements:
SELECT
O.OfficeId,
O.OfficeName AS Office,
HT.Description AS HearingType,
H.HearingDate AS HearingDate,
CR.Description AS Court,
CT.[Description]AS CaseType
FROM Activity H
INNER JOIN ActivityEntry HE ON H.ActivityEntryId = HE.ActivityEntryId
INNER JOIN ActivityType HT ON H.ActivityTypeId = HT.ActivityTypeId
INNER JOIN [Case] C ON H.CaseId = C.CaseId
INNER JOIN [Office] O ON HE.CreatedByOfficeId = O.OfficeId
INNER JOIN [User] U ON C.CreatedByUserId = U.UserId
LEFT OUTER JOIN CaseType CT ON C.CaseTypeId = CT.CaseTypeId
LEFT OUTER JOIN Court CR ON C.CourtId = CR.CourtId
WHERE .dbo.DateOnly(HE.HearingDate)BETWEEN #BeginDate AND #EndDate
GROUP BY
O.OfficeId,
O.OfficeName,
HT.Description
ORDER BY O.OfficeId, HT.Description
GROUP BY requires that you have some kind of an aggregate function in your list of columns - a SUM, an AVG, a COUNT. GROUP BY only makes sense in combination with an aggregate.
Otherwise, just simply order your data with an ORDER BY statement.
You aren't using any aggregate functions (on first glance anyway) so you don't need a group by clause. You can do all your ordering in the order by and then extract it into different datasets as you process it on the application side.
Example:
select ... from ... order by OfficeID, Description
This returns a single result for all offices. Now you need to parse it in code
int OfficeID=-1;
while(recordset.moveToNextRow())
{
if(currentRecord.OfficeID!=OfficeID)
{
//This is a new office, do whatever you need to do to split the data up here
OfficeID=currentRecord.OfficeID;
}
//Process the record as a part of the current office here
}
So if you were building a table on a webpage, you'd maybe end the last table and start a new table every time you hit a new office ID. There's some additional logic you'll need here, but this should give you the idea.
Note that your problem has nothing to do with using a stored procedure and everything to do with how you are selecting and processing data.
I actually realized that my problem cannot be solved through my stored procedure as mentioned from some of the members. Since i am displaying results in my report so i re-organized my report and dataset information so that there is a parent and child relationship and from the dataset my information was organized properly. I used the solutions offered from this post to help guide me: post used to help guide me.

Why are CodeIgniter Active Record and phpMyAdmin giving different results?

SELECT accounts.NameSurname, Projects.Name, personnels.NameSurname
FROM accounts
JOIN projects ON ( accounts.Id = projects.AccountId )
JOIN projectpersonnels ON ( projects.Id = projectpersonnels.ProjectId )
JOIN accounts AS personnels ON ( projectpersonnels.AccountId = personnels.Id )
results in NameSurname Name NameSurname colums
Why would the queries above and below result in different "columns" titles and number of columns?
I am running the query above in phpmyadmin.
using mysql and codeigniter 1.7.3
$this->db->select('accounts.NameSurname,projects.Name,personnels.NameSurname');
$this->db->from('accounts');
$this->db->join('projects','accounts.Id = projects.AccountId' );
$this->db->join('projectpersonnels','projects.Id = projectpersonnels.ProjectId');
$this->db->join('accounts as personnels','projectpersonnels.AccountId = personnels.Id');
$q=$this->db->get();
results in: NameSurname (which is personnel) Name (which is project)
Thank you for reading and replying.
It has to do with how CI creates results. You have a name collision. Both of the NameSurnames are trying to be assigned to the same "key" in your object/array result. Since personnels.NameSurname is second, it is overwriting the value of accounts.NameSurname (because it is being assigned to the same key). If you were to use accounts.NameSurname as aNameSurname (not 100% sure if that is correct syntax for MySQL) instead of accounts.NameSurname that would likely make your results consistent.
This would actually cause the same problem with the PDO classes in certain circumstances too: FETCH_ASSOC and FETCH_OBJ would both only show two columns, but FETCH_ARRAY would show three. FETCH_BOTH would result in there being three numeric indexes and two associative indexes. (Not that you asked about PDO's, but I thought it might further illustrate the point).

Accessing Joined Tables in Linq2SQL When Returning IQueryable

I have a linq query which draws a data set out of a database, but unfortunately due to the legacy nature of the database it requires a manual "join" to be enacted as part of the query.
Now I need to add further optional filters to the data set, some of which rely on the joined table - if I return the data set from the first query as a .AsQueryable() how do I go about accessing the joined table, as it doesn't seem to be available from the IQuerable?
Example:
var myQuery = (from o in db.FirstTable
join t in db.SecondTable on o.SecondID equals t.ID
select o).AsQueryable();
// (optional - if statement excluded for brevity)
myQuery = from x in myQuery where t.SomeField.Equals("My Filter Value");
Whichever properties you need out of the table, you have to grab in the select. So, change your select from "select o" to "select o.Property1, o.Property2, t.Property1, t.Property2"

How to optimize Entity Framework Queries

I am using Linq-To-Entities to do a query which is returning only 947 rows but taking 18 seconds to run. I have done a "ToTraceString" to get the underlying sql out and ran the same thing directly on the database and get the same timing.
I have used the tuning advisor and created a couple of indexes although with little impact.
Looking at the query execution plan there are a couple of nested loops which are taking up 95% of the time but these are already working on the indexes?
Does anyone have any ideas on how to force some optimisation into the EF query??
EDIT: Supplying additional information
A basic ER diagram with for the three tables is as follows:
People >----People_Event_Link ----< Events
P_ID P_ID E_ID
E_ID
The linq that I am running is designed to get all Events back for a particular Person (using the P_ID):
var query = from ev in genesisContext.Events
join pe in genesisContext.People_Event_Link
on ev equals pe.Event
where pe.P_ID == key
select ev;
return query;
Here is the generated SQL (deep breath!):
SELECT
1 AS [C1],
[Extent1].[E_ID] AS [E_ID],
[Extent1].[E_START_DATE] AS [E_START_DATE],
[Extent1].[E_END_DATE] AS [E_END_DATE],
[Extent1].[E_COMMENTS] AS [E_COMMENTS],
[Extent1].[E_DATE_ADDED] AS [E_DATE_ADDED],
[Extent1].[E_RECORDED_BY] AS [E_RECORDED_BY],
[Extent1].[E_DATE_UPDATED] AS [E_DATE_UPDATED],
[Extent1].[E_UPDATED_BY] AS [E_UPDATED_BY],
[Extent1].[ET_ID] AS [ET_ID],
[Extent1].[L_ID] AS [L_ID]
FROM [dbo].[Events] AS [Extent1]
INNER JOIN [dbo].[People_Event_Link] AS [Extent2] ON EXISTS (SELECT
1 AS [C1]
FROM ( SELECT 1 AS X ) AS [SingleRowTable1]
LEFT OUTER JOIN (SELECT
[Extent3].[E_ID] AS [E_ID]
FROM [dbo].[Events] AS [Extent3]
WHERE [Extent2].[E_ID] = [Extent3].[E_ID] ) AS [Project1] ON 1 = 1
LEFT OUTER JOIN (SELECT
[Extent4].[E_ID] AS [E_ID]
FROM [dbo].[Events] AS [Extent4]
WHERE [Extent2].[E_ID] = [Extent4].[E_ID] ) AS [Project2] ON 1 = 1
WHERE ([Extent1].[E_ID] = [Project1].[E_ID]) OR (([Extent1].[E_ID] IS NULL) AND ([Project2].[E_ID] IS NULL))
)
WHERE [Extent2].[P_ID] = 291
Yes. Rewrite the LINQ query. Most LINQ to Entities queries can be written in many different ways, and will be translated to SQL differently. Since you show neither the LINQ nor the SQL nor the query plan, that's about all I can say.
You are smart, though, to try executing the SQL directly. Query compilation can also take time, but you've ruled that out by determining that the SQL accounts for all of the measured time.
Try:
var query = from pe in genesisContext.People_Event_Link
where pe.P_ID == key
from ev in pe.Event // presuming one to many
select ev;
or if pe.Event is one to one:
var query = from pe in genesisContext.People_Event_Link
where pe.P_ID == key
select pe.Event;
return query;
Since 95% of the time is in the nested loops, eliminating them should solve the problem.
There are a few things you can look at:
Are the nested loops necessary. If you wrote a query directly in SQL could you get the same result without using nested loops. If the answer to this is that it could be written without nested loops, what is it in the model or the linq query that is causing it.
Is it an option for you to place some of the logic in views, thereby reducing the complexity of the linq query, and may be removing the need for the nested loops.
Normally I use the SQL server profiler to look at what SQL linq produces, I find this easier especially if you have two screens.
If you still have problems, post your linq query.
#Craig
I couldn't get your query to work as I get an error message saying Type Inference failed in the call to SelectMany.
However I took your advice and went from using the join to an "olde style" pre ANSI type query:
var query = from pe in genesisContext.People_Event_Link
from ev in genesisContext.Events
where pe.P_ID == key && pe.Event == ev
select ev;
Which produces fairly decent sql!