我有一个名为Engine(EngineId,Make,Model,SerialNumber等)的数据库表和一个名为Test(TestId,EngineId,TestDate,Value1,Value2等)的表。我想选择每个引擎记录加上最新的测试结果(如果有的话,可能没有)。
我正在使用ASP.NET Core 1.1和Entity Framework,我有引擎和测试模型实体。
我正在尝试将以下SQL转换为LINQ查询表达式:
SELECT e.*, t.Value1, t.Value2
FROM Engine e
LEFT JOIN Test t on e.EngineID = t.EngineID
AND t.TestDate= (SELECT MAX(TestDate) FROM Test t2)
我可以开始使用左连接,但我不知道如何执行子查询:
var engines = from e in _context.Engine
join t in _context.Test on e.EngineId equals t.EngineId into td
from c in td.DefaultIfEmpty()
select new EngineVM
{
EngineId = e.EngineId,
Unit = e.Unit,
Make = e.Make,
Model = e.Model,
SerialNumber = e.SerialNumber
};
这是我的观点模型:
public class EngineVM
{
public long EngineId { get; set; }
public string Make { get; set; }
public string Model { get; set; }
public string SerialNumber { get; set; }
public decimal? LastTestValue1 { get; set; }
public decimal? LastTestValue2 { get; set; }
}
以下是解决方案:
var lastTestQuery = _context.Test
.FromSql(@"
SELECT t.* FROM Test t
WHERE t.TestDate = (SELECT MAX(t2.TestDate) FROM Test t2
WHERE t2.EngineId = t.EngineId)"
);
// Query to get engine records to display along with last test data
var engines = (from e in _context.Engine
orderby e.SerialNumber
join t in lastTestQuery on e.EngineId equals t.EngineId into lastEngineTest
from t in lastEngineTest.DefaultIfEmpty() // forces left join
let lastTest = (from x in lastEngineTest select x).FirstOrDefault()
select new EngineVM
{
EngineId = e.EngineId,
Unit = e.Unit,
Make = e.Make,
Model = e.Model,
SerialNumber = e.SerialNumber,
Status = CalculateEngineStatus(e.EngineId,
e.PermittedNox,
lastTest.Nox)
}).OrderByDescending(x => x.Status);
答案 0 :(得分:1)
我设法使用EF Core中的FromSql
机制生成单个查询看起来这是子查询的方法:(如果你想通过SQL引擎尽可能多地运行
var lastTestQuery = _context.Test
.FromSql(@"
SELECT t.* FROM Test t
WHERE t.TestDate = (SELECT MAX(t2.TestDate) FROM Test t2 WHERE t2.EngineId = t.EngineId)"
);
var enginesWithLastTest = _context.Engine
.GroupJoin(lastTestQuery, engine => engine.EngineId, test => test.EngineId, (engine, tests) => new {
Engine = engine,
LastTests = tests.DefaultIfEmpty()
})
.ToList();
foreach (var item in enginesWithLastTest)
{
var eng = item.Engine;
var test = item.LastTests.FirstOrDefault();
Console.WriteLine($"{eng.EngineId} {eng.Make} {eng.Model} {eng.SerialNumber} {test?.TestValue}");
}
查看SQL Server Profiler,这是生成的查询:
SELECT [engine].[EngineId],
[engine].[AllowedAmount],
[engine].[Make],
[engine].[Model],
[engine].[SerialNumber],
[test].[TestId],
[test].[EngineId],
[test].[TestDate],
[test].[TestValue]
FROM [Engine] AS [engine]
LEFT JOIN
(SELECT t.*
FROM Test t
WHERE t.TestDate =
(SELECT MAX(t2.TestDate)
FROM Test t2
WHERE t2.EngineId = t.EngineId) ) AS [test]
ON [engine].[EngineId] = [test].[EngineId]
ORDER BY [engine].[EngineId]
一个纯粹的LINQ查询表达式方法(没有lambdas),没有FromSQL
特点:
以下是代码:
var query =
from engine in _context.Engine
join test in (
from t in _context.Test
where t.TestDate == (from t2 in _context.Test where t2.EngineId == t.EngineId select t2.TestDate).Max()
select t
) on engine.EngineId equals test.EngineId into td
from latestTest in td.DefaultIfEmpty()
orderby engine.SerialNumber ascending
select new EngineVM {
EngineId = engine.EngineId,
Make = engine.Make,
Model = engine.Model,
SerialNumber = engine.SerialNumber,
LastTestValue1 = latestTest != null ? (decimal?) latestTest.TestValue : null
};
var enginesWithLastTest = query.ToList();
foreach (var eng in enginesWithLastTest)
{
Console.WriteLine($"{eng.EngineId} {eng.Make} {eng.Model} {eng.SerialNumber} {eng.LastTestValue1}");
}
生成的SQL:
SELECT [engine].[EngineId], [engine].[AllowedAmount], [engine].[Make], [engine].[Model], [engine].[SerialNumber], [t1].[TestId], [t1].[EngineId], [t1].[TestDate], [t1].[TestValue]
FROM [Engine] AS [engine]
LEFT JOIN (
SELECT [t0].[TestId], [t0].[EngineId], [t0].[TestDate], [t0].[TestValue]
FROM [Test] AS [t0]
WHERE [t0].[TestDate] = (
SELECT MAX([t20].[TestDate])
FROM [Test] AS [t20]
WHERE [t20].[EngineId] = [t0].[EngineId]
)
) AS [t1] ON [engine].[EngineId] = [t1].[EngineId]
ORDER BY [engine].[SerialNumber], [engine].[EngineId]
注2:撰写LINQ怪物时,请务必检查SQL执行计划。你可能最好运行两个查询并构建字典
注意:我很想听到有关此答案的反馈和改进。我刚刚开始使用EF Core
答案 1 :(得分:0)
var listOfEnginesWithTestData = engineList.Join(testList,
engine => engine.EngineID,
test => test.EngineID,
(engine, test) => new
{
Engine = engine,
Test = test
}).Select(x => new { x.Engine, x.Test.Value1, x.Test.Value2 }).ToList();
此代码段执行以下操作:
Join
将两个表合并到匹配的EngineId
上。 Select
仅从组合数据中选择您要搜索的值:Engine
,Test.Value1
和Test.Value2
以下是使用List
的示例控制台应用。您正在查询数据库,您的代码看起来会略有不同。
using System;
using System.Collections.Generic;
using System.Linq;
namespace SQLLeftJoin
{
class MainClass
{
public static void Main(string[] args)
{
var engineList = new List<Engine>
{
new Engine{ EngineID = "1"},
new Engine{ EngineID = "2"},
new Engine{ EngineID = "3"}
};
var testList = new List<Test>
{
new Test{TestID = "1", EngineID = "1", TestDate = DateTime.Now},
new Test{TestID = "2", EngineID = "1", TestDate = DateTime.Now},
new Test{TestID = "3", EngineID = "2", TestDate = DateTime.Now},
new Test{TestID = "4", EngineID = "2", TestDate = DateTime.Now},
new Test{TestID = "5", EngineID = "3", TestDate = DateTime.Now},
new Test{TestID = "6", EngineID = "3", TestDate = DateTime.Now},
new Test{TestID = "7", EngineID = "3", TestDate = DateTime.Now},
};
var listOfEnginesWithTestData = engineList.Join(testList,
engine => engine.EngineID,
test => test.EngineID,
(engine, test) => new
{
Engine = engine,
Test = test
}).Select(x => new { x.Engine, x.Test.Value1, x.Test.Value2 }).ToList();
}
}
class Engine
{
public string EngineID { get; set; }
}
class Test
{
public string TestID { get; set; }
public string EngineID { get; set; }
public DateTime TestDate { get; set; }
public object Value1 { get; set; }
public object Value2 { get; set; }
}
}
答案 2 :(得分:0)
尝试代码:
DateTime maxTastdate=db.Test.Max(c=>c.TestDate);
var result=( from e in db.Engine
join t in db.Test.where(c=>c.TestDate=maxTastdate)
on e.EngineID equlas t.EngineID into jj from kk in jj.DefaultIfEmpty()
select new {
EngineId=e.EngineId,
Make=e.Make,
Model=e.Model,
SerialNumber=e.SerialNumber
Value1=kk.Value1,
Value2=kk.Value2
}).Tolist()