我对SQL有很多经验,但对LINQ来说还是比较新的,我很难将以下MySQL查询转换为LINQ。 任何人都可以帮助将以下内容转换为LINQ,以便在具有Entity框架的ASP.net MVC项目中使用吗?
SELECT
S.Submission_ID,
P.Photo_ID,
C2.Contract_Name,
J.Job_Number,
D.Device_Name,
A.`Display_Name`,
S.Submission_Status,
S.Submission_JobRef,
S.Created,
TRUE
FROM
Submission S
LEFT JOIN Job J ON S.`Job_ID` = J.`Job_ID`
LEFT JOIN Contract C2 ON J.`Contract_ID` = C2.`Contract_ID`
INNER JOIN Submission_Status SS ON S.`Submission_Status` = SS.`ID`
INNER JOIN Device D ON S.`Device_ID` = D.`Device_ID`
INNER JOIN ACTION A ON S.`Action_ID` = A.`Action_ID`
INNER JOIN (
SELECT
MIN(P.Photo_ID) AS Photo_ID,
P.Submission_ID
FROM
Photo P
GROUP BY
P.`Submission_ID`) P ON S.`Submission_ID` = P.Submission_ID
WHERE
S.`Submission_Status` <> 3 AND
(LOCATE(@Criteria, C2.`Contract_Name`) > 0 OR
LOCATE(@Criteria, J.`Job_Number`) > 0 OR
LOCATE(@Criteria, D.`Device_Name`) > 0 OR
LOCATE(@Criteria, A.`Display_Name`) > 0 OR
LOCATE(@Criteria, SS.`Value`) > 0 OR
LOCATE(@Criteria, S.`Submission_JobRef`) > 0)
ORDER BY
S.`Submission_ID` DESC
我试图绕过多个连接和子查询,但后来卡住了。这就是我到目前为止......显然,它不起作用或完整!!
Dim results = From S In db.Submissions
Join P In db.Photos On S.Submission_ID Equals P.Submission_ID
Group Join J In db.Jobs On S.Job_ID Equals J.Job_ID
Into Job = Group
Join J In db.Jobs On S.Job_ID Equals J.Job_ID
Group By P.Submission_ID
Into SubmissionPhotoID = Min(P.Photo_ID)
Select New With {.Submission_ID = Submission_ID,
.Photo_ID = SubmissionPhotoID,
.Contract_Name = If(IsNothing(S.Job), "", S.Job.Contract.Contract_Name),
.Job_Number = If(IsNothing(S.Job), "", S.Job.Job_Number),
.Device_Name = S.Device.Device_Name,
.Action_Name = S.Action.Display_Name,
.Submission_Status = S.Submission_Status1.ID,
.Submission_JobRef = S.Submission_JobRef,
.Created = S.Created,
.CanEdit = bolCanEdit}
Order By S.Submission_ID
Skip param.iDisplayStart
Take param.iDisplayLength
非常感谢您对上述任何帮助或指导!
为了帮助您,以下是定义上述查询中使用的实体的模型中的类。 (我省略了一些与问题无关的字段。)
Partial Public Class Submission
Public Property Submission_ID As Integer
Public Property Job_ID As Nullable(Of Integer)
Public Property Device_ID As Integer
Public Property Action_ID As Integer
Public Property Submission_Status As Nullable(Of Integer)
Public Property Submission_JobRef As String
Public Property Created As Nullable(Of Date)
Public Overridable Property Action As Action
Public Overridable Property Device As Device
Public Overridable Property Job As Job
Public Overridable Property Photos As ICollection(Of Photo) = New HashSet(Of Photo)
Public Overridable Property Submission_Status1 As Submission_Status
End Class
Partial Public Class Job
Public Property Job_ID As Integer
Public Property Contract_ID As Nullable(Of Integer)
Public Property Job_Number As String
Public Overridable Property Contract As Contract
Public Overridable Property Submissions As ICollection(Of Submission) = New HashSet(Of Submission)
End Class
Partial Public Class Contract
Public Property Contract_ID As Integer
Public Property Contract_Name As String
Public Overridable Property Jobs As ICollection(Of Job) = New HashSet(Of Job)
End Class
Partial Public Class Submission_Status
Public Property ID As Integer
Public Property Value As String
Public Overridable Property Submissions As ICollection(Of Submission) = New HashSet(Of Submission)
End Class
Partial Public Class Device
Public Property Device_ID As Integer
Public Property Device_Name As String
Public Overridable Property Submissions As ICollection(Of Submission) = New HashSet(Of Submission)
End Class
Partial Public Class Action
Public Property Action_ID As Integer
Public Property Display_Name As String
Public Overridable Property Submissions As ICollection(Of Submission) = New HashSet(Of Submission)
End Class
Partial Public Class Photo
Public Property Photo_ID As Integer
Public Property Submission_ID As Integer
Public Overridable Property Submission As Submission
End Class
答案 0 :(得分:2)
这是一个相当复杂的SQL,具有左选择和内连接的子选择和混合。 一些快速建议:
将其分解为一系列linq语句,从核心对象开始,并在后续步骤中添加相关部分。如果你把结果保存为IQueryable,编译器会把它们全部放在一起,然后作为一个查询发送到db(即直到最后一步才发送ToList())。
就我个人而言,我使用两个from和一个扩展方法连接而不是使用连接运算符。我更容易知道你正在获得左连接或内连接。
例如:
FROM Submission S LEFT JOIN Job J ON S.`Job_ID` = J.`Job_ID`
我会这样做(对不起我是c#所以VB的语法可能不太正确)
Dim results = from s in db.Submissions
from j in db.Jobs.Where(j=> j.Job_Id == s.Job_Id).DefaultIfEmpty()
因此,连接条件位于Jobs上的.Where()内部.DefaultIfEmpty()告诉它左连接(实质上,如果连接失败,Job将是默认值)。
进一步编辑:
经过实验,我得到了这段代码来返回结果(正确的结果是另一个问题)。再次,抱歉c#语法。
[TestMethod]
public void Query()
{
const string conStr = "Data Source=(local);Initial Catalog=ComplexSqlToLinq; Integrated Security=True";
var db = new MyDbContext(conStr);
const string criteria = "Contract1";
var minPhotos = from p in db.Photos
group p by p.SubmissionId
into g
select new {SubmissionId = g.Key, PhotoId = g.Min(p=>p.PhotoId)};
var query = from s in db.Submissions
from j in db.Jobs.Where(j => j.JobId == s.JobId).DefaultIfEmpty()
from c in db.Contracts.Where(c => c.ContractId == j.ContractId).DefaultIfEmpty()
from ss in db.SubmissionStatuses.Where(ss => ss.Id == s.SubmissionStatus)
from d in db.Devices.Where(d => d.DeviceId == s.DeviceId)
from a in db.Actions.Where(a => a.ActionId == s.ActionId)
from p in minPhotos.Where(p => p.SubmissionId == s.SubmissionId)
where s.SubmissionStatus != 3 &&
( c.ContractName.Contains(criteria) ||
j.JobNumber.Contains(criteria) ||
d.DeviceName.Contains(criteria) ||
a.DisplayName.Contains(criteria) ||
ss.Value.Contains(criteria) ||
s.SubmissionJobRef.Contains(criteria))
select new
{
s.SubmissionId,
p.PhotoId,
c.ContractName,
j.JobNumber,
d.DeviceName,
a.DisplayName,
s.SubmissionStatus,
s.SubmissionJobRef,
s.Created,
SomeBool = true
};
var result = query.ToList();
Assert.IsTrue(result.Any());
}
显然,您可以在测试中改变标准常量以应用于不同的项目,我选择匹配合同 - 我假设只有一个表格会匹配。
此查询生成以下SQL,看起来有点不错,但功能与原始版本非常相似。
SELECT
[Filter1].[SubmissionId] AS [SubmissionId],
[GroupBy1].[A1] AS [C1],
[Filter1].[ContractName] AS [ContractName],
[Filter1].[JobNumber] AS [JobNumber],
[Filter1].[DeviceName] AS [DeviceName],
[Filter1].[DisplayName] AS [DisplayName],
[Filter1].[SubmissionStatus] AS [SubmissionStatus],
[Filter1].[SubmissionJobRef] AS [SubmissionJobRef],
[Filter1].[Created] AS [Created],
cast(1 as bit) AS [C2]
FROM
(
SELECT
[Extent1].[SubmissionId] AS [SubmissionId],
[Extent1].[SubmissionStatus] AS [SubmissionStatus],
[Extent1].[SubmissionJobRef] AS [SubmissionJobRef],
[Extent1].[Created] AS [Created],
[Extent2].[JobNumber] AS [JobNumber],
[Extent3].[ContractName] AS [ContractName],
[Extent4].[Value] AS [Value],
[Extent5].[DeviceName] AS [DeviceName],
[Extent6].[DisplayName] AS [DisplayName]
FROM
[dbo].[Submissions] AS [Extent1]
LEFT OUTER JOIN [dbo].[Jobs] AS [Extent2] ON [Extent2].[JobId] = [Extent1].[JobId]
LEFT OUTER JOIN [dbo].[Contracts] AS [Extent3] ON [Extent3].[ContractId] = [Extent2].[ContractId]
INNER JOIN [dbo].[SubmissionStatus] AS [Extent4] ON [Extent4].[Id] = [Extent1].[SubmissionStatus]
INNER JOIN [dbo].[Devices] AS [Extent5] ON [Extent5].[DeviceId] = [Extent1].[DeviceId]
INNER JOIN [dbo].[Actions] AS [Extent6] ON [Extent6].[ActionId] = [Extent1].[ActionId]
WHERE
3 <> [Extent1].[SubmissionStatus]
) AS [Filter1]
INNER JOIN (
SELECT
[Extent7].[SubmissionId] AS [K1],
MIN([Extent7].[PhotoId]) AS [A1]
FROM
[dbo].[Photos] AS [Extent7]
GROUP BY
[Extent7].[SubmissionId] ) AS [GroupBy1]
ON [GroupBy1].[K1] = [Filter1].[SubmissionId]
WHERE
(
[Filter1].[ContractName] LIKE @p__linq__0 ESCAPE N'~') OR
([Filter1].[JobNumber] LIKE @p__linq__1 ESCAPE N'~') OR
([Filter1].[DeviceName] LIKE @p__linq__2 ESCAPE N'~') OR
([Filter1].[DisplayName] LIKE @p__linq__3 ESCAPE N'~') OR
([Filter1].[Value] LIKE @p__linq__4 ESCAPE N'~') OR
([Filter1].[SubmissionJobRef] LIKE @p__linq__5 ESCAPE N'~')
)
答案 1 :(得分:1)
用一句话来回应戴夫约翰逊的评论 - 可扩展性。
最近我试图提高应用程序的性能,我的第一个想法是在John Henry的样本中添加一些类似于复杂性的SQL - 多个连接和过滤器。毕竟,它在我的开发机器上像火箭一样表现。
架构师断然禁止在数据库服务器上使用复杂的SQL,因为有几个大型应用程序与100个用户挂钩。就像我喜欢构建摇滚的snappy SQL一样,我不得不同意。将逻辑转移到消耗它的机器上是一种很好的架构。
因此,对于我们精通声明性SQL的人来说,学习linq技能的翻译很重要。
当然,我之前提供的解决方案并没有实现这一点,因为相同的SQL被发送到服务器。但是拥有一个linq等价物是一个可以进一步优化的开始。
答案 2 :(得分:0)
经过大量的搜索和阅读各种文章之后,我放弃了尝试用LINQ查询语法编写这个查询,而不是使用方法语法。
非常感谢Ackroydd对于将复杂的SQL转换为LINQ的建议和支持。当你知道你可以在几分钟内完成SQL的某些事情但需要使用LINQ来实现可扩展性并保持现有代码时,它会变得相当令人沮丧!
这是我最终得到的结论,因为我确信这对其他人有用:
Dim query As IQueryable(Of Submission)
' Initialise the new query
query = db.Submissions.Include(Function(s) s.Action) _
.Include(Function(s) s.Photos) _
.Include(Function(s) s.Device) _
.Include(Function(s) s.Job) _
.Include(Function(s) s.Submission_Status1) _
.Include(Function(s) s.Job.Contract) _
.Include(Function(s) s.Comments) _
.AsNoTracking
' Apply initial filters
query = query.Where(Function(S) Not S.Submission_Status1.ID.Equals(3))
' Apply search criteria if passed
If Not String.IsNullOrEmpty(param.sSearch) Then
query = query.Where(Function(S) S.Job.Contract.Contract_Name.Contains(param.sSearch) OrElse
S.Job.Job_Number.Contains(param.sSearch) OrElse
S.Device.Device_Name.Contains(param.sSearch) OrElse
S.Action.Display_Name.Contains(param.sSearch) OrElse
S.Submission_Status1.Value.Contains(param.sSearch) OrElse
S.Submission_JobRef.Contains(param.sSearch))
End If
' Order the results
query = query.OrderByDescending(Function(S) S.Submission_ID)
' Paginate the results
query = query.Skip(param.iDisplayStart).Take(param.iDisplayLength)
' Return only the required columns
Dim resultData = query.AsEnumerable.Select(Function(S) New AjaxSubmissionOverview With { _
.Submission_ID = S.Submission_ID,
.Photo_ID = S.Photos.First.Photo_ID,
.Contract_Name = If(IsNothing(S.Job), "", S.Job.Contract.Contract_Name),
.Job_Number = If(IsNothing(S.Job), "", S.Job.Job_Number),
.Device_Name = S.Device.Device_Name,
.Action_Name = S.Action.Display_Name,
.Submission_Status = S.Submission_Status,
.Submission_JobRef = S.Submission_JobRef,
.Latest_Comment = If(S.Comments.Count = 0, "", HtmlHelpers.Truncate(S.Comments.Last.Comment1, 100)),
.Created = S.Created,
.CanEdit = bolCanEdit})