Why does this EF query produce such crazy generated SQL? - sql

var statusId = db.WorkOrder.Where(w => w.OrderType.Name == "ReadAudit" && w.WorkOrderMapping.MeterOldTag == meterTag && w.OrderStatusId != 80)
.OrderByDescending(w => w.CreationDatetime)
.Select(r => r.OrderStatusId)
.FirstOrDefault();
That produces this crazy sql:
SELECT TOP (1)
[Project1].[OrderStatusId] AS [OrderStatusId]
FROM ( SELECT
[Filter1].[OrderStatusId] AS [OrderStatusId],
[Filter1].[CreationDatetime] AS [CreationDatetime]
FROM (
SELECT [Extent1].[OrderStatusId] AS [OrderStatusId],
[Extent1].[CreationDatetime] AS [CreationDatetime],
[Extent3].[MeterOldTag] AS [MeterOldTag]
FROM [dbo].[WorkOrder] AS [Extent1]
INNER JOIN [dbo].[OrderType] AS [Extent2] ON [Extent1].[OrderTypeKey] = [Extent2].[OrderTypeKey]
LEFT OUTER JOIN [dbo].[WorkOrderMapping] AS [Extent3] ON [Extent1].[WorkOrderKey] = [Extent3].[WorkOrderMappingKey]
WHERE (80 <> [Extent1].[OrderStatusId])
AND (N'ReadAudit' = [Extent2].[Name])
) AS [Filter1]
WHERE ([Filter1].[MeterOldTag] = #p__linq__0) OR (([Filter1].[MeterOldTag] IS NULL) AND (#p__linq__0 IS NULL))
) AS [Project1]
ORDER BY [Project1].[CreationDatetime] DESC
And I'm told that its hitting the database pretty hard:
Table 'WorkOrder'. Scan count 30, logical reads 84403
Table 'WorkOrderMapping'. Scan count 9, logical reads 16516
The EF query doesn't seem that complicated. Is there a way to get the generated SQL to be more efficient?

The only thing "crazy" about that SQL Query is the predicate on MeterOldTag. It's written like that because EF, by default, writes queries to emulate C# comparison semantics for LINQ queries. If you want a simple equality comparison in the databse, set UseDatabaseNullSemantics for your DbContext.

It is just how Entity Framework works.
If you want more control, you can write the query yourself via Entity Framework Raw Queries, see here and below a (not complete) example how your query might look like.
DbRawSqlQuery<Int32> query = db.Database.SqlQuery<Int32>("SELECT OrderStatusId FROM ... ");
var statusId = query.FirstOrDefault();
Edit:
Also have a look at the actual query plan that gets executed for this query in order to find if whether the appropriate indexes are present on the tables involved.
Consider moving the constants RealAuditand 80 to variables, like below:
var orderType = "RealAudit";
var orderStatusId = 80;
var statusId = db.WorkOrder.Where(w => w.OrderType.Name == orderType
&& w.WorkOrderMapping.MeterOldTag == meterTag
&& w.OrderStatusId != orderStatusId
)
.OrderByDescending(w => w.CreationDatetime)
.Select(r => r.OrderStatusId)
.FirstOrDefault();
By doing so, these will appear as SQL parameters in the query, something like:
#p__linq__1 <> [Extent1].[OrderStatusId])
AND (#p__linq__2 = [Extent2].[Name])
This allows that a single query plan can be used for all variations of this query, whereas now you get a queryplan per separate value of the the MeterOldTag argument.

Related

Linq .Except behavior not as expected

I have two tables: tableA and tableB, both have an attribute "CommissionNumber" which contains strings in the form of D123456789 (one letter followed by a fixed number of digits).
I need to find the commissionnumbers in table A, that are not in table B.
In SQL, this could look like:
SELECT *
FROM tableA
WHERE CommissionNumber NOT IN
(
select CommissionNumber from tableB
)
Which gives me no results. However, if i try this:
var tableA= dbContext.tableA.Select(x => x.CommissionNumber).ToList();
var tableB= dbContext.tableB.Select(x => x.CommissionNumber).ToList();
IEnumerable<string> missingFiles = tableA.Except(tableB);
I get 92 hits. I don't understand what's wrong, my SQL query of the use of the .Except function.
Any ideas?
You have created two LINQ queries which retrieves all data from two tables into the memory and applied Except. It is wrong if you care about performance.
If you want to create SQL in the same way, LINQ query should be written accordingly. LINQ equivalent for IN operator is Contains.
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Select(b => b.CommissionNumber).Contains(x.CommissionNumber));
Or by EXISTS which have analogue Any
var query = dbContext.tableA
.Where(x => !dbContext.tableB.Any(b => b.CommissionNumber == x.CommissionNumber));
Also the same result you can achieve by LEFT JOIN
var query =
from a in dbContext.tableA
join b in dbContext.tableB on a.CommissionNumber equals b.CommissionNumber into gj
from b in gj.DefaultIfEmpty()
where b.CommissionNumber == null
select a;
gsharp was on the right track. I had some case sensitivity issues in my data which was ignored in the native SQL query but taken seriously by EF core.

nhibernate group by and join query

I need nhiberante query (not HQL) equivalent following SQL:
SELECT ur.*
FROM (SELECT MAX(requestTime) rt, macAddress ma
FROM UpdateRequests
GROUP BY macAddress) mur
JOIN dbo.UpdateRequests ur
ON mur.ma = ur.macAddress AND mur.rt = ur.requestTime
I had no luck with other similar examples on stackoverflow.
Having UpdateRequest mapping, it seems that is not possible with Query API, how about QueryOver?
Finally one Guru suggested me to change SQL query without changing execution plan:
SELECT ur.*
FROM [dbo].[UpdateRequests] AS ur
WHERE ur.[RequestTime] = (SELECT MAX(mur.[RequestTime])
FROM [dbo].[UpdateRequests] mur
WHERE mur.[MacAddress] = ur.[MacAddress])
So in code it transforms into:
session
.Query<UpdateRequest>()
.Where(ur => ur.RequestTime == session.Query<UpdateRequest>()
.Where(mur => mur.MacAddress == ur.MacAddress)
.Max(mur => mur.RequestTime))
.ToList();
And this is exactly what i need.

Create a LINQ for SQL query

I'm learning Linq and using MVC. I have written a SQL query which I need to convert to a LINQ query.
select TokenID,TokenAsset,packet from TokenTable where id = 6 and packet = ''
and TokenID not in (select TokenID from TokenTable where id=6 and packet <> '')
group by TokenID,TokenAsset,Packet
I kindly ask help to convert the above query to a LINQ query. I know that the SQL query isn't efficient. It would better if you can help me to fix it.
Try this one:
var result = Tokens.Where(x=>x.Id==6 &&
x.Packet=="" &&
!Tokens.Exists(y=>y.TokenID==x.TokenID &&
y.Id==6 &&
y.Packet!="")
)
.GroupBy(x=>x.ID)
.ThenGroupBy(x=>x.TokenAsset)
.ThenGroupBy(x=>x.Packet);
Note I suppose that collection Tokens holds all the tokens you have.
Firstly your SQL query can just be
select distinct TokenID, TokenAsset, packet
from TokenTable
where id = 6 and packet = ''
the group by is not that useful since there are no aggregated columns. All selected columns are in the group by clause. Use distinct to achieve the same.
the secondary AND condition for tokenid is also redundant. It is exclusive to the first condition and hence doesn't change the result.
use this LINQ query:
var results = dbcontext.TokenTables
.Where(t => t.id == 6 && t.Packet == "")
.Select(t => new { t.TokenId, t.TokenAsset, t.Packet }).Distinct();
project only columns you need for performant calls by avoiding extra data transfer.

Giving different record set after changing simple SQL query to LINQ query

I have write the below query in simple SQL,
I want to change it to use LINQ, I have tried, but my LINQ query and the original SQL statement are giving different record sets.
Simple SQL Query:
select *
from Paymentadvices
where status = 3
and Id in (select PaymentAdviceId from Approvals where ApprovedById = 13)
LINQ:
var myPaymentAdviceList = from pa in db.PaymentAdvices
where pa.Status == 3
join Ap in db.Approvals on pa.Id equals
Ap.PaymentAdviceId
where Ap.EmployeeId == 13
orderby pa.PaidDate descending
select pa;
I am not supposed to use join I guess, What should I use ?
var a = db.Approvals.Where( x => x.ApprovalById = 13).PaymentAdviceId;
var b = db.Paymentadvices.Where(x => x.status ==3 && a.Contains(x.Id);
.Contains() makes the WHERE IN () , you don't need a join there
var a = from a in db.Approvals
where a.ApprovedById == 3
select a.PaymentAdviceId;
var b = (from p in db.PaymentAdvices
where p.Status == 3 &&
a.Contains(p.Id)
select p).ToList();
those are both linq , the top is just lambda expressions which are commonly used in Linq queries. I would reccommend that you get used to reading/ writing both styles . The majority of code you'll come across in lambda style
I believe something like the below would work:
var query = from p in db.PaymentAdvices
where p.Status == 3 && db.Approvals
.Select(a => a.Id)
.Where(a => a.ApprovedById == 13)
.Contains(p.Id)
select p;
Though it's worth noting that #Scott Selby and #axrwkr solutions above are essentially the exact same thing in another form.

LINQ translation doesn't give the same results as my SQL query

Hi guys I have this SQL query (MSSQL), I'm doing a query where the result of joins are giving me the "top" row of newest row by date without having duplicates of results, you can find here information of what I'm doing http://goo.gl/Uv0FR The thing is this, I accomplished already the SQL query, Is Working as I'm expecting, I'm getting 1 row for each IDKEY uses in the clause "where pi.PlazaIe in ('','') without duplication
Select * from PlazaI pi
join (
Select * from PlazaE pe where
NOT EXISTS(SELECT 1 FROM PlazaE pe1
WHERE pe.Id_plaza = pe1.Id_plaza AND pe1.Fecha > pe.Fecha AND pe1.Fecha < GETDATE() and pe1.Id_Emp != 0)
) pe on pe.Id_plaza = pieepo.Id_plaza
join Emp e on pe.Id_Emp = e.Id_Emp
join View ct on ct.Id_Nodo = pe.id_nodo
where pi.PlazaIe in ('value1','value2')
The PROBLEM is when I'm trying to convert from SQL to LINQ is just can't make to happened. (I'm new in this world of Linq)
the following is my linq query.
var q1 = (from pe in db.PlazaEmpleados
where !db.PlazaEmpleados.Any
(
pe1 => (pe1.Id_plaza.Equals(pe.Id_plaza) && pe1.Fecha > pe.Fecha && pe1.Id_Emp != 0 && pe1.Fecha > DateTime.Now)
) select pe);
var q2 = (from pi in db.Context
join pe in (q1) on pi.Id_plaza equals pe.Id_plaza
select new EmpVO
{
Id_Nodo = pe.id_nodo,
Id_plaza = pi.PlazaSome,
Num_Plaza = pi.Id_plaza,
});
When I run this linq2sql query I'm getting duplicate results instead of just 1 for each value. So the thing is, I would like to know if someone can convert in a good way the SQL query to LINQ Query or point me where is the error.
thanks in advance.
Your check for the Date is different:
LINQ:
pe1.Fecha > DateTime.Now
SQL:
pe1.Fecha < GETDATE()
Isnt your LINQ supposed to be:
pe1.Fecha < DateTime.Now
I didn't find answer which resolve my problem, so what I finally did is to use the
db.ExecuteQuery<ObjectVO>(sqlQuery);
I know this is not the best practice and also don't resolve the question why my sql query and my linq query don't get the same result set, but non of the previous answer did.
The other thing is my query grown in complexity (new business logic requirement) have to join 7 table and search for Max dates and movement is some of them, so now is more complicated to transform the query to a linq to sql.
Thanks for the support.
this part:
var q1 = from pe in db.PlazaEmpleados
where !db.PlazaEmpleados.Any
(pe1 =>
pe1.Id_plaza.Equals(pe.Id_plaza) &&
pe1.Fecha > pe.Fecha &&
pe1.Id_Emp != 0 &&
pe1.Fecha < DateTime.Now
)
select pe;
In SQL you first use PlazaI then PlazaE- in Linq you both times use PlazaEmpleados.
Put your SQL query to stored procedure an add it to context. Then just call:
var q = context.MyProcedure(new object[] {"value1","value2"});