【问题标题】:How can I get the minimum values ​and date of each group?如何获得每组的最小值和日期?
【发布时间】:2020-09-20 04:20:47
【问题描述】:

我有两个相关的 MySQL 表。我的目的是获取每个组的最小值以及与找到的每个最小值相关联的日期。

                              TABLE 1
> ------------+--------------+--------------+-----------+-----------------+
> --  Code -- | - IdGroup1 - | - IdGroup2 - | - State - | - NameProtocol -|
> ------------+--------------+--------------+-----------+-----------------+
>    ZZ-100   |   11111111   |    1110000   |     1     |       OSM1      |
> ------------+--------------+--------------+-----------+-----------------+
>    ZZ-200   |   55555555   |    5550000   |     1     |       OSM1      |
> ------------+--------------+--------------+-----------+-----------------+
>    ZZ-300   |   99999999   |    9990000   |     1     |       OSM1      |

表 1 和表 3 是相关的。

                             TABLE 3
> ---------------------+-------------------+----------------+
> --  NameProtocol  -- | -- Description -- | -- Protocol -- |
> ---------------------+-------------------+----------------+
>         ATC0         |        d1         |      UDP       |
> ---------------------+-------------------+----------------+
>         OSM1         |        d2         |      TCP       |
> ---------------------+-------------------+----------------+

表 2 是记录新值的位置。

                                TABLE 2
> ---------+-----------------------+----------------+----------------+
> - Value -| ------- Date -------- | -- IdGroup1 -- | -- IdGroup2 -- |
> ---------+-----------------------+----------------+----------------+
>    10    |  2020-08-16 02:30:10  |    99999999    |     9990000    |
> ---------+-----------------------+----------------+----------------+
>    15    |  2020-08-16 02:31:10  |    99999999    |     9990000    |
> ---------+-----------------------+----------------+----------------+
>    20    |  2020-08-16 02:32:10  |    99999999    |     9990000    |
> ---------+-----------------------+----------------+----------------+
>    115   |  2020-08-16 02:31:20  |    55555555    |     5550000    |
> ---------+-----------------------+----------------+----------------+
>    120   |  2020-08-16 02:32:20  |    55555555    |     5550000    |
> ---------+-----------------------+----------------+----------------+
>    90    |  2020-08-16 02:35:20  |    11111111    |     1110000    |
> ---------+-----------------------+----------------+----------------+
>    100   |  2020-08-16 02:30:20  |    11111111    |     1110000    |

我已经做了很多测试,我仍然设法得到正确的答案,最好的近似值是通过以下查询获得的:

var query = Table2                                              //Outer Table
            .Join(Table1,                                       //Inner Table to join
                         p => new { p.IdGroup1, p.IdGroup2 },   //Condition from outer table
                         e => new { e.IdGroup1, e.IdGroup2 },   //Condition from inner table
                         (p, e) => new {                        //Result
                                          Code = e.Code,                                          
                                          Value = p.Value,
                                          Date = p.Date })         
            .GroupBy(gb => new { gb.Code })           
            .OrderBy(ob => ob.Key.Code)
            .Select(s => new {  Code = s.Key.Code, 
                                Value = (double?)s.Min(a => a.Value),
                                Date = "?" })  // TODO: The date remains to be implemented.
            .ToList();

我的查询结果:

> -------------+-------------+------------+
> --  Code  -- | -- Value -- | -- Date -- | 
> -------------+-------------+------------+
>    ZZ-100    |      90     |     ?      | 
> -------------+-------------+------------+
>    ZZ-200    |     115     |     ?      |
> -------------+-------------+------------+
>    ZZ-300    |      10     |     ?      |

我只需要为找到的每个最小值添加日期。我应该怎么做才能将它集成到我的查询中?

代码:

var query = Table1                      
        .Join(Table2.Where(w => (w.State == 1)),            
                         h => new { h.IdGroup1, h.IdGroup2 },   
                         p => new { p.IdGroup1, p.IdGroup2 },   
                         (h, p) => new { h, p })                    
        .Join(Table3.Where(w => (w.Protocol == "TCP")),
                                     pt => pt.p.NameProtocol,
                                     p => p.NameProtocol,
                                     (pt, p) => new { pt, p })          
        .GroupBy(gb => new { gb.pt.p.Code })            
        .OrderBy(ob => ob.Key.Code)
        .Select(s => new {  Code = s.Key.Code, 
                            Value = (double?)s.Min(a => a.pt.h.Value) })
        .ToList();

【问题讨论】:

    标签: c# entity-framework linq


    【解决方案1】:

    对此类请求最有效的查询是使用 Window Functions,EF 不支持,我认为这永远不会发生。 所以只需使用 SQL 并通过 Dapper 运行它,无论如何。

    SELECT 
        s.Code,
        s.Value,
        s.Date
    FROM
        (
            SELECT 
               t1.Code,
               t2.Value,
               t2.Date,
               ROW_NUMBER() OVER (PARTITION BY t1.Code ORDER BY t2.Value) AS RN
            FROM TABLE1 t1
            JOIN TBALE3 t3 ON t3.NameOfProtocol = t1.NameOfProtocol
            LEFT JOIN TABLE2 t2 ON t1.IdGroup1 = t2.IdGroup1 AND t1.IdGroup2 = t2.IdGroup2 AND t2.FechaCaudalHistorico <= @dateFilter
            WHERE t3.Protocol = 'TCP'
        ) s
    WHERE s.RN = 1
    

    如果您不是纯 EF Core 专家并且仍然需要 LINQ,您可以尝试linq2db.EntityFrameworkCore 扩展,它具有这种可能性,并且可以通过 LINQ 编写查询:

    var dateFilter = DateTime.Parse ("2020-09-16 03:00:00");
    var rnQuery =
       from t1 in Table1
       join t3 in Table3 on t1.NameOfProtocol equals t3.NameOfProtocol
       from t2 in Table2.Where(t2 => t1.IdGroup1 == t2.IdGroup1 && t1.IdGroup2 == t2.IdGroup2 && t2.FechaCaudalHistorico <= dateFilter)
          .DefaultIfEmpty()
       where t3.Protocol == "TCP"
       select new 
       {
          t1.Code,
          Value = Sql.ToNullable(t2.Value),
          Date  = Sql.ToNullable(t2.Date),
          RN    = Sql.Ext.RowNumber().Over().PartitionBy(t1.Code).OrderBy(t2.Value).ToValue()
       };
    
    var query = from s in rnQuery
       where s.RN == 1
       select new 
       {
          s.Code,
          s.Value,
          s.Date,
       };
    
    // switch to alternative LINQ parser
    query = query.ToLinqToDB();
    
    var result = query.ToList();
    

    因此,您将拥有与上述相同的 SQL。

    【讨论】:

    • 当记录不存在时,它应该显示空值。因为CODE存在于表1中。
    • 我尝试了这两个选项并且它们都有效,但它们没有提供我需要的答案。请在问题上方查看我想要的答案。
    • 更新了答案。如果您需要更多详细信息,只需输入评论即可。
    • 请您按日期添加以下过滤器。其中 t2.date
    • 按日期添加过滤器后,应该会出现 CODE 'ZZ-200' 并且伴随它的列 VALUE 和 DATE 应该显示为空值。
    【解决方案2】:

    您可以尝试以下方法:

    .Select(s => 
    {
        var values = s.OrderBy(x=>x.Value);
        var firstValue = values.First();
        return new 
        {
            Code = s.Key.Code;
            Value = (double?)firstValue.Value;
            Date = firstValue.Date;
        }
    })
    

    基本上,我们根据Value 属性对每个组中的项目进行排序。具有最小值的元素将是排序之后的第一个元素。然后我们选择那个元素并读取它的值和日期,我们就完成了。

    更新

    一个快速的解决方案是在Join 之后调用ToList。这会将所有数据带入应用程序的内存中,您将在那里执行所有需要的处理。

    .Join(Table1
          , p => new { p.IdGroup1, p.IdGroup2 }
          , e => new { e.IdGroup1, e.IdGroup2 }
          , (p, e) => new 
            {
               Code = e.Code,                                          
               Value = p.Value,
               Date = p.Date 
        })
       .ToList()  
       .GroupBy(gb => new { gb.Code })
       .OrderBy(ob => ob.Key.Code)
       .Select(s => 
       {
           var values = s.OrderBy(x=>x.Value);
           var firstValue = values.First();
           return new 
           {
               Code = s.Key.Code;
               Value = (double?)firstValue.Value;
               Date = firstValue.Date;
           }
       }).ToList();
    

    【讨论】:

    • 我试过了,但它给了我以下错误:CS0834 A lambda expression with a statement body cannot be convert to an expression tree.
    • 我在 EF Core 3.1 中使用的查询,它给了我以下错误: InvalidOperationException: Client side GroupBy is not supported。
    • @ingezone 我想我们在这里找到了罪魁祸首 :) docs.microsoft.com/en-us/ef/core/what-is-new/ef-core-3.x/…
    • @ingezone 试试更新的,请告诉我。
    • 为什么要存储过程?视图应该足够了。顺便说一句,linq 在你能做的事情上是有限的,所以你迟早会得到视图。它们既实用又强大。
    【解决方案3】:

    你可以这样做:

    var query = Table2                                              //Outer Table
        .Join(Table1,                                       //Inner Table to join
            p => new { p.IdGroup1, p.IdGroup2 },   //Condition from outer table
            e => new { e.IdGroup1, e.IdGroup2 },   //Condition from inner table
            (p, e) => new
            {                        //Result
                Code = e.Code,
                Value = p.Value,
                Date = p.Date
            })
        .GroupBy(gb => new { gb.Code })
        .OrderBy(ob => ob.Key.Code)
        .Select(s =>
        {
            var min = s.FirstOrDefault(x=> x.Value == s.Min(a => a.Value));
            return new {Code = s.Key.Code, Value = (double?)min.Value, Date = min.Date};
        })  // TODO: The date remains to be implemented.
        .ToList();
    

    【讨论】:

    • 我试过了,但它给了我以下错误:CS0834 A lambda expression with a statement body cannot be convert to an expression tree.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-21
    • 2019-11-02
    • 1970-01-01
    • 1970-01-01
    • 2020-01-02
    相关资源
    最近更新 更多