簡體   English   中英

分組並從兩個表中選擇第一個Linq

[英]Group and Select First From Two Tables Linq

我正在嘗試使用EFCore創建一個簡單的查詢,返回我正在交談的人員列表,以及我們兩個人之間發送的最后一條消息(非常類似於它在Facebook Messenger或Whatsapp上的顯示方式)。 我創建了linq查詢,但它生成了一個sql查詢的地獄。 我正在嘗試優化linq查詢以生成更好的sql,所以這里有完整的故事:

0.兩個實體:訪客和聊天消息

訪問者包含訪問者信息,ChatMessages包含實際聊天。

在此輸入圖像描述

1.先試試

我嘗試了第一個查詢如下:

from c in ChatMessages
orderby c.CreatedAt descending 
group c by c.VisitorId  into x
select x.First()

這讓我獲得了按訪客ID分組的最新消息列表:

在此輸入圖像描述

這很酷,特別是生成的短sql查詢:

SELECT [t3].[test], [t3].[Id], [t3].[Message], [t3].[UserId], [t3].[VisitorId], [t3].[isDeleted] AS [IsDeleted], [t3].[CreatedAt], [t3].[CreatedBy], [t3].[LastUpdatedAt], [t3].[LastUpdatedBy], [t3].[isFromVisitor] AS [IsFromVisitor]
FROM (
    SELECT [t0].[VisitorId]
    FROM [ChatMessages] AS [t0]
    GROUP BY [t0].[VisitorId]
    ) AS [t1]
OUTER APPLY (
    SELECT TOP (1) 1 AS [test], [t2].[Id], [t2].[Message], [t2].[UserId], [t2].[VisitorId], [t2].[isDeleted], [t2].[CreatedAt], [t2].[CreatedBy], [t2].[LastUpdatedAt], [t2].[LastUpdatedBy], [t2].[isFromVisitor]
    FROM [ChatMessages] AS [t2]
    WHERE (([t1].[VisitorId] IS NULL) AND ([t2].[VisitorId] IS NULL)) OR (([t1].[VisitorId] IS NOT NULL) AND ([t2].[VisitorId] IS NOT NULL) AND ([t1].[VisitorId] = [t2].[VisitorId]))
    ORDER BY [t2].[CreatedAt] DESC
    ) AS [t3]
ORDER BY [t3].[CreatedAt] DESC

2.第二次嘗試,也加入訪客表

現在我想返回訪問者信息,所以我必須加入訪問者表:

from c in ChatMessages
join v in Visitors on  c.VisitorId equals v.Id 
orderby c.CreatedAt descending 
group new {Message = c, Visitor = v} by c.Visitor.Id  into x
select x

這產生了我想要的東西:

在此輸入圖像描述

問題是,生成SQL查詢變得非常混亂:

SELECT [t2].[Id] AS [Key]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
GROUP BY [t2].[Id]
GO

-- Region Parameters
DECLARE @x1 BigInt = 1
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 2
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 3
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 4
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 5
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 6
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 7
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 8
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 9
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 10
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 11
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 12
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 13
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 14
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 15
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 16
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 17
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 18
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 19
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC
GO

-- Region Parameters
DECLARE @x1 BigInt = 20
-- EndRegion
SELECT [t0].[Id], [t0].[Message], [t0].[UserId], [t0].[VisitorId], [t0].[isDeleted] AS [IsDeleted], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[isFromVisitor] AS [IsFromVisitor], [t1].[Id] AS [Id2], [t1].[Email], [t1].[Name], [t1].[Phone], [t1].[isDeleted] AS [IsDeleted2], [t1].[CreatedAt] AS [CreatedAt2], [t1].[CreatedBy] AS [CreatedBy2], [t1].[LastUpdatedAt] AS [LastUpdatedAt2], [t1].[LastUpdatedBy] AS [LastUpdatedBy2], [t1].[Fingerprint], [t1].[IP]
FROM [ChatMessages] AS [t0]
INNER JOIN [Visitors] AS [t1] ON [t0].[VisitorId] = ([t1].[Id])
LEFT OUTER JOIN [Visitors] AS [t2] ON [t2].[Id] = [t0].[VisitorId]
WHERE @x1 = [t2].[Id]
ORDER BY [t0].[CreatedAt] DESC

這似乎不是我想要在數據庫中觸發的查詢。 此外,當在asp.net核心應用程序內執行此代碼時,我得到一個異常EF.Property called with wrong property name. ,不確定原因:

crit: converse_app.Controllers.VisitorsController[0]
      There was an error on 'GetVisitorsAsync' invocation: System.InvalidOperationException: EF.Property called with wrong property name.
         at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
         at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.Translate(Expression expression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.WeakEntityExpandingExpressionVisitor.Expand(Expression source, MemberIdentity member)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.WeakEntityExpandingExpressionVisitor.VisitMember(MemberExpression memberExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.WeakEntityExpandingExpressionVisitor.VisitMember(MemberExpression memberExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.WeakEntityExpandingExpressionVisitor.Expand(SelectExpression selectExpression, Expression lambdaBody)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.RemapLambdaBody(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateGroupBy(ShapedQueryExpression source, LambdaExpression keySelector, LambdaExpression elementSelector, LambdaExpression resultSelector)
         at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
         at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
         at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass9_0`1.<Execute>b__0()
         at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
         at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
         at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
         at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetEnumerator()
         at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
         at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
         at converse_app.Controllers.VisitorsController.GetVisitorsAsync(Int32 pageSize, Int32 pageNumber

很抱歉這篇文章很長,所以我的問題是如何優化linq查詢以獲得更好的sql輸出,以及此錯誤可能觸發的原因。

我正在使用.NET Core 3 preview8和EF Core 3 preview8,並針對MSSQL運行。

您正在尋找的標准查詢以LINQ to Entities(EF)表示,類似於此(沒有join ,沒有GroupBy ,使用導航屬性):

var query = context.Visitors
    .Select(v => new
    {
        Visitor = v,
        Message = v.VisitorChatMessages
            .OrderByDescending(m => m.CreatedAt)
            .FirstOrDefault()
    });

但這是陷阱。 EF6創建了非常低效的SQL查詢,而EF Core直到現在產生(再次非常低效)N + 1 SQL查詢。

但這正在改變EF Core 3.0的積極方向! 通常(並且仍然)我不建議使用EF Core 3.0的預覽版(beta),因為它們正在重寫整個查詢轉換/處理管道,因此很多東西不能按預期工作。

但今天我已經將我的EF Core測試環境更新到EF Core 3.0 Preview 9 ,我很高興看到上面的查詢現在可以很好地轉換為以下單個SQL查詢:

  SELECT [v].[Id], [v].[CreatedAt], [v].[CreatedBy], [v].[Email], [v].[Fingerprint], [v].[IP], [v].[IsDeleted], [v].[LastUpdatedAt], [v].[LastUpdatedBy], [v].[Name], [v].[Phone], [t0].[Id], [t0].[CreatedAt], [t0].[CreatedBy], [t0].[IsDeleted], [t0].[IsFromVisitor], [t0].[LastUpdatedAt], [t0].[LastUpdatedBy], [t0].[Message], [t0].[UserId], [t0].[VisitorId]
  FROM [Visitors] AS [v]
  LEFT JOIN (
      SELECT [t].[Id], [t].[CreatedAt], [t].[CreatedBy], [t].[IsDeleted], [t].[IsFromVisitor], [t].[LastUpdatedAt], [t].[LastUpdatedBy], [t].[Message], [t].[UserId], [t].[VisitorId]
      FROM (
          SELECT [c].[Id], [c].[CreatedAt], [c].[CreatedBy], [c].[IsDeleted], [c].[IsFromVisitor], [c].[LastUpdatedAt], [c].[LastUpdatedBy], [c].[Message], [c].[UserId], [c].[VisitorId], ROW_NUMBER() OVER(PARTITION BY [c].[VisitorId] ORDER BY [c].[CreatedAt] DESC) AS [row]
          FROM [ChatMessages] AS [c]
      ) AS [t]
      WHERE [t].[row] <= 1
  ) AS [t0] ON [v].[Id] = [t0].[VisitorId]

注意ROW_NUMBER() OVER (PARTITION BY ORDER BY)構造的美妙利用。 這是EF查詢翻譯第一次執行此操作。 我很興奮。 干得好,EF核心團隊!


更新:第一個查詢的完全等價物(在預覽9中,btw因運行時異常而失敗)

from c in context.ChatMessages
orderby c.CreatedAt descending 
group c by c.VisitorId  into x
select x.First()

但附加信息是

from v in context.Visitors
from c in v.VisitorChatMessages
    .OrderByDescending(c => c.CreatedAt)
    .Take(1)
orderby c.CreatedAt descending
select new
{
    Visitor = v,
    Message = c
})

生成的SQL幾乎相同 - 只是LEFT OUTER JOIN變為INNER JOIN並且最后還有其他ORDER BY

看起來要做到這一點,必須避免使用GroupBy並使用GroupJoin (集合導航屬性在LINQ to Entities查詢中表示)或相關的SelectMany來實現所需的分組。

您應該能夠通過關聯獲取訪問者信息,而無需明確嘗試加入/分組。

對於切換到Fluent語法的道歉,我真的不喜歡Linq QL,它似乎總是被迫做任何事情...... :)

您的原始查詢:

from c in ChatMessages
orderby c.CreatedAt descending 
group c by c.VisitorId  into x
select x.First()

要么

var groupedMessages = context.ChatMessages
    .OrderByDescending(c => c.CreatedAt)
    .GroupBy(c => c.VisitorId)
    .First();

按訪客分組:

var groupedMessages = context.ChatMessages
    .OrderByDescending(c => c.CreatedAt)
    .GroupBy(c => c.Visitor)
    .First();

這將為您提供Key作為Visitor實體,以及該訪問者的消息。 但是,這有點引發了一個問題,為什么呢?

var visitorsWithMessages = context.Visitors.Include(v => v.VisitorChatMessages);

這會加載訪問者並急切加載其關聯的聊天消息。 實體滿足針對數據關系的查詢。 雖然我們關心的是確保聊天消息的訂購或可能的過濾等細節。

為了將其投影到合適的結構中,我將使用訪問者和聊天消息的視圖模型來優化查詢以覆蓋我關心的細節,並以我關心的方式呈現它們:

var visitorsWithMessages = context.Visitors
   // insert .Where() clause here to filter which Visitors we care to retrieve...
   .Select(v => new VisitorViewModel
   {
      Id = v.Id,
      Name = v.Name,
      RecentChatMessages = v.VisitorChatMessages
         .OrderByDescending(c => c.CreatedAt)
         .Select(c => new ChatMessageViewModel
         {
            Id = c.Id,
            Message = c.Message,
            CreatedAt = c.CreatedAt,
            CreatedBy = c.User.UserName ?? "Anonymous"
         }).Take(10).ToList()
    }).ToList();

這使用投影和EF的映射關系來獲取訪問者列表以及最多10個最近的聊天消息。 我填充簡單的POCO視圖模型類,其中包含我關心的字段。 這些視圖模型可以安全地從方法返回或序列化到視圖/ API使用者,而不會有延遲延遲加載的風險。 如果我只需要數據而不需要將其發送到任何地方,我可以使用匿名類型來獲取我關心的字段。 我們不需要將實體明確地連接在一起,您只需要為故意沒有映射FK關系的實體執行此操作。 我們也不需要刻意加載實體。 Select將為我們需要的列組成一個優化的SQL語句。

您可以在視圖中使用這些結果並根據訪客+消息進行渲染,如果您希望將每個訪問者的列表顯示為最近的消息/ w訪客詳細信息作為展開的消息列表,您甚至可以深入了解:

編輯:通過訪問“v”,以下查詢可能有問題。 .SelectMany 更正為“c.Visitor”。

var recentMessages = context.Visitors
   .SelectMany(v => v.VisitorChatMessages
      .OrderByDescending(c => c.CreatedAt)
      .Select(c => new VisitorChatMessageViewModel
      {
          Id = c.Id,
          VisitorId = c.Visitor.Id,
          Message = c.Message,
          CreatedAt = c.CreatedAt,
          CreatedBy = c.User.UserName ?? "Anonymous",
          Visitor = c.Visitor.Name
      }).Take(10)
    }).ToList();

不知道如何在Linq QL中做到這一點。 :)

編輯:最后一個示例將為您提供最后10條消息及其適用的訪客詳細信息。 (名稱)

要獲取最后100封郵件,請按訪問者對其進行分組:

var recentMessages = context.Visitors
   .SelectMany(v => v.VisitorChatMessages
      .OrderByDescending(c => c.CreatedAt)
      .Select(c => new VisitorChatMessageViewModel
      {
          Id = c.Id,
          VisitorId = c.Visitor.Id,
          Message = c.Message,
          CreatedAt = c.CreatedAt,
          CreatedBy = c.User.UserName ?? "Anonymous",
          Visitor = c.Visitor.Name // Visitor Name, or could be a ViewModel for more info about Visitor...
      }).Take(100)
    }).GroupBy(x => x.Visitor).ToList();

如果為Visitor而不是“.Visitor.Name”選擇VisitorViewModel,則無論您從關聯的Visitor中選擇什么,您的分組鍵都可以訪問Name,Id等。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM