使用相同的 SQL 生成输出将 T-SQL 转换为 Fluent Linq C#

Posted

技术标签:

【中文标题】使用相同的 SQL 生成输出将 T-SQL 转换为 Fluent Linq C#【英文标题】:Convert T-SQL to Fluent Linq C# with the same SQL generated output 【发布时间】:2015-06-17 09:37:50 【问题描述】:

我的数据库中有以下架构:

重点是为每个用户设置每个线程的最后阅读日期时间。

如果需要,我还可以通过 ForumsUniverses 以及聚合来查看这些日期。

如果所有线程都是Read,则论坛被视为Read

如果用户Lastview大于或等于上次创建的发布日期(来自Post.CreatedAt),则将线程视为Read

我已根据论坛提出以下 T-SQL 请求以实现此目标:

SELECT
        F.Id,
        CASE WHEN SUM(V.IsRead) = COUNT(V.IsRead) THEN 1 ELSE 0 END AS IsRead
    FROM Forum F
    LEFT JOIN Thread T ON T.Id_Forum = F.Id
    LEFT JOIN
    (
        SELECT
                P.Id_Thread,
                CASE WHEN MAX(P.CreatedAt) < MAX(V.LastView) THEN 1 ELSE 0 END AS IsRead
            FROM Post P
            INNER JOIN Thread T ON P.Id_Thread = T.Id
            INNER JOIN Forum F ON T.Id_Forum = F.Id
            LEFT JOIN Thread_View V ON P.Id_Thread = V.Id_Thread AND V.Id_User = @Id_User
        WHERE F.Id_Universe = @Id_Universe
        GROUP BY P.Id_Thread
    ) V ON T.Id = V.Id_Thread
WHERE F.Id_Universe = @Id_Universe
GROUP BY F.Id
ORDER BY F.Id

它工作得很好,但是,我现在想使用EntityFrameworkLinq创建这个请求......我被卡住了......

这是我所做的,但它会生成一个非常复杂的查询,而且我担心性能......

var viewsQuery = context.Posts
    .Where(p => p.Thread.Forum.Id_Universe == idUniverse)
    .GroupJoin
    (
        context.Thread_Views.Where(v => v.Id_User == idUser),
        p => p.Id_Thread,
        v => v.Id_Thread,
        (p, v) => new  Id_Thread = p.Id_Thread, Id_Forum = p.Thread.Id_Forum, CreatedAt = p.CreatedAt, LastView = v.Max(_v => _v.LastView) 
    )
    .Select(r => new  Id_Thread = r.Id_Thread, Id_Forum = r.Id_Forum, IsRead = r.CreatedAt < r.LastView );

var forumQuery = context.Forums.Where(f => f.Id_Universe == idUniverse).GroupJoin
(
    viewsQuery.DefaultIfEmpty(),
    forum => forum.Id,
    view => view.Id_Forum,
    (forum, views) => new
    
        Forum = forum.Id,
        IsRead = views.Any() && views.All(v => v.IsRead),
    
);

linq 生成的 SQL 输出是这样的(稍微编辑一下更易读),实在是太难看了……

SELECT 
    [Project1].[Id] AS [Id], 
    CASE WHEN 
    (
        (
            EXISTS
            (
                SELECT 
                    1 AS [C1]
                FROM   ( SELECT 1 AS X ) AS [SingleRowTable1]
                INNER JOIN 
                (
                    SELECT [Extent3].[Id_Forum] AS [Id_Forum], [Extent4].[Id_Universe] AS [Id_Universe]
                    FROM   [dbo].[Post] AS [Extent2]
                    INNER JOIN [dbo].[Thread] AS [Extent3] ON [Extent2].[Id_Thread] = [Extent3].[Id]
                    INNER JOIN [dbo].[Forum] AS [Extent4] ON [Extent3].[Id_Forum] = [Extent4].[Id] ) AS [Join2] ON 1 = 1
                    WHERE ([Join2].[Id_Universe] = 3) AND ([Project1].[Id] = [Join2].[Id_Forum])
            )
        )
        AND
        (
            NOT EXISTS
            (
                SELECT 
                    1 AS [C1]
                FROM   ( SELECT 1 AS X ) AS [SingleRowTable2]
                INNER JOIN 
                (
                    SELECT 
                        [Project3].[CreatedAt] AS [CreatedAt], 
                        [Project3].[Id_Forum] AS [Id_Forum], 
                        (
                            SELECT  MAX([Extent8].[LastView]) AS [A1]
                            FROM    [dbo].[Thread_View] AS [Extent8]
                            WHERE ([Extent8].[Id_User] = 79775) AND ([Project3].[Id_Thread] = [Extent8].[Id_Thread])
                        ) AS [C1]
                    FROM
                    (
                        SELECT 
                            [Extent5].[Id_Thread] AS [Id_Thread], 
                            [Extent5].[CreatedAt] AS [CreatedAt], 
                            [Extent6].[Id] AS [Id], 
                            [Extent6].[Id_Forum] AS [Id_Forum]
                        FROM   [dbo].[Post] AS [Extent5]
                        INNER JOIN [dbo].[Thread] AS [Extent6] ON [Extent5].[Id_Thread] = [Extent6].[Id]
                        INNER JOIN [dbo].[Forum] AS [Extent7] ON [Extent6].[Id_Forum] = [Extent7].[Id]
                        WHERE [Extent7].[Id_Universe] = 3
                    )  AS [Project3]
                ) AS [Project4] ON 1 = 1
                WHERE
                (
                    (
                        (
                            CASE
                                WHEN ([Project4].[CreatedAt] < [Project4].[C1]) THEN cast(1 as bit)
                                WHEN ( NOT ([Project4].[CreatedAt] < [Project4].[C1])) THEN cast(0 as bit)
                            END
                        ) <> 1
                    )
                    OR
                    (
                        CASE
                            WHEN ([Project4].[CreatedAt] < [Project4].[C1]) THEN cast(1 as bit)
                            WHEN ( NOT ([Project4].[CreatedAt] < [Project4].[C1])) THEN cast(0 as bit)
                        END IS NULL
                    )
                ) AND ([Project1].[Id] = [Project4].[Id_Forum])
            )
        )
    ) THEN cast(1 as bit)
    ELSE cast(0 as bit) END AS [C1]
FROM 
(
    SELECT 
        [Extent1].[Id] AS [Id]
    FROM [dbo].[Forum] AS [Extent1]
    WHERE [Extent1].[Id_Universe] = 3
)  AS [Project1]

编辑:找到了一个具有相同结果的有效 linq 查询,但它太丑了,我非常担心性能......

【问题讨论】:

【参考方案1】:
var forumsWhichAreUnread = from forum on context.Forums
                           let isUnRead = forum.Threads.All(thread =>
                                thread.ThreadViews
                                     .Where(view => view.UserId = userId)
                                     .Max(view => view.Lastview)
                                <
                                thread.Posts.Max(post => post.CreatedAt))
                           select new forum, isUnRead;

翻译

获取context.Forums中的每个forum

只给我每个线程都在的论坛...

最大threadview.LastView时间小于Latest Post Date。

选出那些forum

【讨论】:

这不是我真正想要的,因为我不想要 UnRead 论坛,而是所有具有 IsRead 布尔属性的论坛。 谢谢,但仍然无法按预期工作,因为它不会返回未读论坛。但主要是,生成的 SQL 非常繁重且非常复杂,我想重现相同的 T-SQL 查询,但使用 linq。你觉得有可能吗? 编辑:我找到了一个有效的 linq 查询,但是生成的 SQL...嗯,看看它你就会明白 :(

以上是关于使用相同的 SQL 生成输出将 T-SQL 转换为 Fluent Linq C#的主要内容,如果未能解决你的问题,请参考以下文章

如何将 DateTime 转换为精度大于 T-SQL 中天数的数字?

使用 T-SQL 将表转换为 XML

需要帮助将 T-SQL 转换为 PostgreSQL

如何将 nvarchar 从 T-SQL 方言转换为 hiveQL?

将 SQL Server T-SQL 转换为 SQL

将 T-SQL 转换为 MySQL