0

我在 c# 中有一个 linq to sql 查询可以工作 - 但在将数据摘要输出到网页的 ASP.NET 应用程序中使用时会导致性能非常差。此查询尝试对来自单个 log4net 表的日志进行分组。没有连接。

.GroupBy(l => new
{
Level = l.Level,
Period = ((DateTime.Now - l.Created).TotalMinutes <= 60)
             ? "Within last hour"
             : (((DateTime.Now - l.Created).TotalMinutes > 60) &&
                ((DateTime.Now - l.Created).TotalMinutes <= 360)
               )
                   ? "Within last 6 hours"
                   : (((DateTime.Now - l.Created).TotalMinutes > 360) &&
                      ((DateTime.Now - l.Created).TotalMinutes <= 1440)
                     )
                         ? "Within last 24 hours"
                         : (((DateTime.Now - l.Created).TotalMinutes >
                             1440) &&
                            ((DateTime.Now - l.Created).TotalMinutes <=
                             10080)
                           )
                               ? "Within last week"
                               : (((DateTime.Now - l.Created).
                                       TotalMinutes > 10080) &&
                                  ((DateTime.Now - l.Created).
                                       TotalMinutes <= 302400)
                                 )
                                     ? "Within last month"
                                     : (((DateTime.Now - l.Created).
                                             TotalMinutes > 302400) &&
                                        ((DateTime.Now - l.Created).
                                             TotalMinutes <= 907200)
                                       )
                                           ? "Within last quarter"
                                           : (((DateTime.Now -
                                                l.Created).
                                                   TotalMinutes >
                                               907200) &&
                                              ((DateTime.Now -
                                                l.Created).
                                                   TotalMinutes <=
                                               3628800)
                                             )
                                                 ? "Current year"
                                                 : ((DateTime.Now -
                                                     l.Created).
                                                        TotalMinutes >
                                                    3628800)
                                                       ? "Before current year"
                                                       : String.Empty,
PeriodType = ((DateTime.Now - l.Created).TotalMinutes <= 60)
                 ? 1
                 : (((DateTime.Now - l.Created).TotalMinutes > 60) &&
                    ((DateTime.Now - l.Created).TotalMinutes <= 360)
                   )
                       ? 2
                       : (((DateTime.Now - l.Created).TotalMinutes >
                           360) &&
                          ((DateTime.Now - l.Created).TotalMinutes <=
                           1440)
                         )
                             ? 3
                             : (((DateTime.Now - l.Created).
                                     TotalMinutes > 1440) &&
                                ((DateTime.Now - l.Created).
                                     TotalMinutes <= 10080)
                               )
                                   ? 4
                                   : (((DateTime.Now - l.Created).
                                           TotalMinutes > 10080) &&
                                      ((DateTime.Now - l.Created).
                                           TotalMinutes <= 302400)
                                     )
                                         ? 5
                                         : (((DateTime.Now - l.Created)
                                                 .TotalMinutes >
                                             302400) &&
                                            ((DateTime.Now - l.Created)
                                                 .TotalMinutes <=
                                             907200)
                                           )
                                               ? 6
                                               : (((DateTime.Now -
                                                    l.Created).
                                                       TotalMinutes >
                                                   907200) &&
                                                  ((DateTime.Now -
                                                    l.Created).
                                                       TotalMinutes <=
                                                   3628800)
                                                 )
                                                     ? 7
                                                     : ((DateTime.Now -
                                                         l.Created).
                                                            TotalMinutes >
                                                        3628800)
                                                           ? 8
                                                           : 0
                                              }
                            ).ToList();

当我运行 SQL 探查器时,我发现实际正在运行的查询是这样的:

SELECT 
[Extent1].[LogID] AS [LogID], 
[Extent1].[Created] AS [Created], 
[Extent1].[RoleInstance] AS [RoleInstance], 
[Extent1].[DeploymentId] AS [DeploymentId], 
[Extent1].[Machine] AS [Machine], 
[Extent1].[Thread] AS [Thread], 
[Extent1].[Level] AS [Level], 
[Extent1].[Logger] AS [Logger], 
[Extent1].[Message] AS [Message], 
[Extent1].[Exception] AS [Exception]
FROM [dbo].[Logs] AS [Extent1]

本质上,看起来所有的分组等都是在客户端完成的。由于 Web 服务器和数据库服务器之间的链接很慢 - 这导致网页实际显示结果之前需要很长时间。此外,日志数据库越大,响应越慢。

奇怪的是——当我在 LinqPad 中运行类似的查询时——我得到了一个完全不同的计划——像这样(略有不同的查询):

exec sp_executesql N'SELECT COUNT(*) AS [Count], [t1].[Level], [t1].[value] AS [Period], [t1].[value2] AS [PeriodType]
FROM (
    SELECT [t0].[Level], 
        (CASE 
            WHEN ((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p0))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p0), [t0].[Created]), @p0)) * 10000))) / 600000000) <= @p1 THEN CONVERT(NVarChar(20),@p2)
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p3))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p3), [t0].[Created]), @p3)) * 10000))) / 600000000) > @p4) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p5))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p5), [t0].[Created]), @p5)) * 10000))) / 600000000) <= @p6) THEN CONVERT(NVarChar(20),@p7)
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p8))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p8), [t0].[Created]), @p8)) * 10000))) / 600000000) > @p9) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p10))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p10), [t0].[Created]), @p10)) * 10000))) / 600000000) <= @p11) THEN @p12
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p13))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p13), [t0].[Created]), @p13)) * 10000))) / 600000000) > @p14) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p15))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p15), [t0].[Created]), @p15)) * 10000))) / 600000000) <= @p16) THEN CONVERT(NVarChar(20),@p17)
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p18))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p18), [t0].[Created]), @p18)) * 10000))) / 600000000) > @p19) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p20))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p20), [t0].[Created]), @p20)) * 10000))) / 600000000) <= @p21) THEN CONVERT(NVarChar(20),@p22)
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p23))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p23), [t0].[Created]), @p23)) * 10000))) / 600000000) > @p24) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p25))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p25), [t0].[Created]), @p25)) * 10000))) / 600000000) <= @p26) THEN CONVERT(NVarChar(20),@p27)
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p28))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p28), [t0].[Created]), @p28)) * 10000))) / 600000000) > @p29) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p30))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p30), [t0].[Created]), @p30)) * 10000))) / 600000000) <= @p31) THEN CONVERT(NVarChar(20),@p32)
            WHEN ((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p33))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p33), [t0].[Created]), @p33)) * 10000))) / 600000000) > @p34 THEN CONVERT(NVarChar(20),@p35)
            ELSE CONVERT(NVarChar(20),@p36)
         END) AS [value], 
        (CASE 
            WHEN ((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p37))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p37), [t0].[Created]), @p37)) * 10000))) / 600000000) <= @p38 THEN @p39
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p40))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p40), [t0].[Created]), @p40)) * 10000))) / 600000000) > @p41) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p42))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p42), [t0].[Created]), @p42)) * 10000))) / 600000000) <= @p43) THEN @p44
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p45))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p45), [t0].[Created]), @p45)) * 10000))) / 600000000) > @p46) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p47))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p47), [t0].[Created]), @p47)) * 10000))) / 600000000) <= @p48) THEN @p49
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p50))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p50), [t0].[Created]), @p50)) * 10000))) / 600000000) > @p51) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p52))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p52), [t0].[Created]), @p52)) * 10000))) / 600000000) <= @p53) THEN @p54
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p55))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p55), [t0].[Created]), @p55)) * 10000))) / 600000000) > @p56) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p57))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p57), [t0].[Created]), @p57)) * 10000))) / 600000000) <= @p58) THEN @p59
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p60))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p60), [t0].[Created]), @p60)) * 10000))) / 600000000) > @p61) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p62))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p62), [t0].[Created]), @p62)) * 10000))) / 600000000) <= @p63) THEN @p64
            WHEN (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p65))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p65), [t0].[Created]), @p65)) * 10000))) / 600000000) > @p66) AND (((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p67))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p67), [t0].[Created]), @p67)) * 10000))) / 600000000) <= @p68) THEN @p69
            WHEN ((CONVERT(Float,CONVERT(BigInt,(((CONVERT(BigInt,DATEDIFF(DAY, [t0].[Created], @p70))) * 86400000) + DATEDIFF(MILLISECOND, DATEADD(DAY, DATEDIFF(DAY, [t0].[Created], @p70), [t0].[Created]), @p70)) * 10000))) / 600000000) > @p71 THEN @p72
            ELSE @p73
         END) AS [value2]
    FROM [Logs] AS [t0]
    ) AS [t1]
GROUP BY [t1].[Level], [t1].[value], [t1].[value2]',N'@p0 datetime,@p1 float,@p2 nvarchar(4000),@p3 datetime,@p4 float,@p5 datetime,@p6 float,@p7 nvarchar(4000),@p8 datetime,@p9 float,@p10 datetime,@p11 float,@p12 nvarchar(4000),@p13 datetime,@p14 float,@p15 datetime,@p16 float,@p17 nvarchar(4000),@p18 datetime,@p19 float,@p20 datetime,@p21 float,@p22 nvarchar(4000),@p23 datetime,@p24 float,@p25 datetime,@p26 float,@p27 nvarchar(4000),@p28 datetime,@p29 float,@p30 datetime,@p31 float,@p32 
nvarchar(4000),@p33 datetime,@p34 float,@p35 nvarchar(4000),@p36 nvarchar(4000),@p37 datetime,@p38 float,@p39 int,@p40 datetime,@p41 float,@p42 datetime,@p43 float,@p44 int,@p45 datetime,@p46 float,@p47 datetime,@p48 float,@p49 int,@p50 datetime,@p51 float,@p52 datetime,@p53 float,@p54 int,@p55 datetime,@p56 float,@p57 datetime,@p58 float,@p59 int,@p60 datetime,@p61 float,@p62 datetime,@p63 float,@p64 int,@p65 datetime,@p66 float,@p67 datetime,@p68 float,@p69 int,@p70 datetime,@p71 float,@p72 int,@p73 int',@p0='2012-07-11 23:46:56.457',@p1=60,@p2=N'Within last hour',@p3='2012-07-11 23:46:56.457',@p4=60,@p5='2012-07-11 23:46:56.457',@p6=360,@p7=N'Within last 6 hours',@p8='2012-07-11 23:46:56.457',@p9=360,@p10='2012-07-11 23:46:56.457',@p11=1440,@p12=N'Within last 24 hours',@p13='2012-07-11 23:46:56.457',@p14=1440,@p15='2012-07-11 23:46:56.457',@p16=10080,@p17=N'Within last week',@p18='2012-07-11 23:46:56.457',@p19=10080,@p20='2012-07-11 23:46:56.457',@p21=302400,@p22=N'Within last month',@p23='2012-07-11 23:46:56.457',@p24=302400,@p25='2012-07-11 23:46:56.457',@p26=907200,@p27=N'Within last quarter',@p28='2012-07-11 23:46:56.457',@p29=907200,@p30='2012-07-11 23:46:56.457',@p31=3628800,@p32=N'Current year',@p33='2012-07-11 23:46:56.457',@p34=3628800,@p35=N'Before current year',@p36=N'',@p37='2012-07-11 23:46:56.460',@p38=60,@p39=1,@p40='2012-07-11 23:46:56.460',@p41=60,@p42='2012-07-11 23:46:56.460',@p43=360,@p44=2,@p45='2012-07-11 23:46:56.460',@p46=360,@p47='2012-07-11 23:46:56.460',@p48=1440,@p49=3,@p50='2012-07-11 23:46:56.460',@p51=1440,@p52='2012-07-11 23:46:56.460',@p53=10080,@p54=4,@p55='2012-07-11 23:46:56.460',@p56=10080,@p57='2012-07-11 23:46:56.460',@p58=302400,@p59=5,@p60='2012-07-11 23:46:56.460',@p61=302400,@p62='2012-07-11 23:46:56.460',@p63=907200,@p64=6,@p65='2012-07-11 23:46:56.460',@p66=907200,@p67='2012-07-11 23:46:56.460',@p68=3628800,@p69=7,@p70='2012-07-11 23:46:56.460',@p71=3628800,@p72=8,@p73=0

现在,如果这是我在 c# 中编写的查询的结果,那么输出到网页所需的时间会大大减少。

在调试 c# 代码时,我尝试了几个选项 - 围绕在 linq 查询的各个点添加 .ToList() 语句。我什至尝试在 EF4 设计器中更改 LazyLoadingEnabled。当我查看 SQL 探查器时,似乎没有什么可以使用正确的计划。我也尝试过 DataLoadOptions - 但它们似乎也没有任何不同,因为看起来这些选项在使用表连接时会更有用。

我究竟做错了什么?如何让 group by 和任何其他聚合发生在数据库服务器上,以便将更少的记录传输到网络服务器?这是由于 Linq 中的延迟加载造成的吗?我该如何纠正?谢谢

4

1 回答 1

4

如果无法访问您的完整查询,至少我认为您可以对日期和三元运算符进行更清洁和更高效的计算。

通过使用该let命令,您应该只在 SQL 中进行一次计算,而不是每次比较。

此外,通过反转从最早时间段到最早时间段搜索时间段的方式,您只需检查它是否更大。您可以将相同类型的思维应用于您的时期类型。

我不能确定这是否会转化为服务器端,但这应该是朝着正确方向迈出的一步:

var results = (from u in YOURTABLE
    let totalMinutes = (DateTime.Now - u.Created).TotalMinutes
    orderby u.Created
    select new
    {
       u,
       Period = totalMinutes > 3628800 ? "Before current year" :
                (totalMinutes > 907200 ? "Current year" :
                (totalMinutes > 302400 ? "Within last quarter" :
                (totalMinutes > 10080 ? "Within last month" :
                (totalMinutes > 1440 ? "Within last week" :
                (totalMinutes > 360 ? "Within Last 24 hours" :
                (totalMinutes > 60 ? "Within last 6 hours" :
                "Within last hour"
                ))))))
    }).GroupBy (l =>
    new
    {
    Level = l.u.Level,
    l.Period
    }
    );
于 2012-07-13T17:25:35.127 回答