【问题标题】:Return certain record based on criteria (2)根据条件返回某条记录 (2)
【发布时间】:2017-03-30 09:45:37
【问题描述】:

我之前问过这个问题,但错过了我问题的一个重要部分。

Return certain record based on criteria

获取此结果列表

Client  |   Date     | YESorNO
-------------------------------
A1      | 01/01/2001 | NO
A1      | 01/01/2002 | NO
A1      | 01/01/2003 | YES
A1      | 01/01/2004 | NO
A1      | 01/01/2005 | NO
A1      | 01/01/2006 | NO
A1      | 01/01/2007 | YES
A1      | 01/01/2008 | YES
A1      | 01/01/2009 | YES

A2      | 01/01/2001 | NO
A2      | 01/01/2002 | NO
A2      | 01/01/2003 | YES
A2      | 01/01/2004 | NO
A2      | 01/01/2005 | YES
A2      | 01/01/2006 | YES

A3      | 01/01/2001 | NO

           ...etc...

列表是按时间顺序排列的,除了降序/升序之外,我无法以任何其他方式排序。

我无法排序是 |否并找到 First() 或 Last() 因为这不会给我所需的值。

我希望能够在每个客户的所有“否”都被考虑后返回第一个“是”。

在上面的 Client[A1] 示例中,第 7 行是我想要返回的记录(2007 年 1 月 1 日)。

客户[A2] - 第 5 行 (01/01/2005) ..etc

我的代码如下

var query = 
(
    from m in db.MyTable
    where m.Criteria == XYZ
    select new
    {
      Client = m.Client,
      Date = m.Date, 
      YESorNO = m.YESorNO
    }
).OrderBy(x => x.Date);

使用.FirstOrDefault(x => x.YesOrNO == "YES") 返回第三条记录。

用户@RenéVogt 建议

var result = query.AsEnumerable()
                  .TakeWhile(x => x.YESorNO == "YES")
                  .LastOrDefault();

会完成这项工作,但我忘了补充一点,查询将返回许多客户,我需要为每个客户提供第一个“是”,因此上面的代码是不够的。

迭代我的结果将非常耗时,虽然这是一种解决方案,但我希望此逻辑位于数据库查询本身中(如果可能)

非常感谢

【问题讨论】:

  • 您可以在 2 个查询中轻松完成。获取每个客户的所有“否”列表......只为每个客户选择最后一个否。然后使用它来要求按日期列出最后一个“NO”之后的第一个“YES”列表。

标签: c# sql linq


【解决方案1】:

你要做的就是按client分组,然后从末尾开始找到每一个的最后一个YES。像这样的东西(ClientListList<>,您可能需要根据您的数据在哪里进行更改):

var query = ClientList.OrderBy(x => x.client).ThenBy(x => x.date).GroupBy(x => x.client);
foreach (var client in query)
{
    var lastYES=client.Reverse().TakeWhile(x => x.YESorNO == "YES")
              .LastOrDefault();
    Console.WriteLine(String.Format("{0} {1}",client.Key,lastYES.date));
}
//Output: A1 01/01/2007 0:00:00
//        A2 01/01/2005 0:00:00

编辑

Mansur Anorboev 正确地建议按日期降序排序,从而消除了Reverse 的需要,因此代码为:

var query = ClientList.OrderBy(x => x.client).ThenByDescending(x => x.date).GroupBy(x => x.client);
foreach (var client in query)
{
    var lastYES=client.TakeWhile(x => x.YESorNO == "YES")
              .LastOrDefault();
    Console.WriteLine(String.Format("{0} {1}",client.Key,lastYES.date));
}

编辑 2

我仍然对我的解决方案不完全满意,因为它使用的是 foreach。这在一个 Linq 命令中完成所有操作:

var query = ClientList.OrderBy(x => x.client)
                      .ThenByDescending(x => x.date)
                      .GroupBy(x => x.client, (key, g) => g.TakeWhile(x => x.YESorNO == "YES").LastOrDefault())
                      .ToList();

这将返回一个列表,其中每个客户一个元素和正确的日期。

【讨论】:

  • 不错的解决方案。我认为最好使用 ThenByDescending(x => x.date) 并删除 Revers()
  • 你完全正确@MansurAnorboev。答案已编辑
  • 现在我看到 OP 链接的第一个问题,这似乎不是很原始,因为@RenéVogt 接受的答案就是这样做的。但是GroupBy 客户端部分可能是 OP 需要的,所以我会留下这个答案
【解决方案2】:

我可以提供一点sql查询

;WITH cte AS (
    SELECT *, ROW_NUMBER() OVER (ORDER BY Client DESC) AS rn  
    FROM [dbo].[tblSkaterhaz]
)
,gte AS (
    SELECT Client,max(rn) mx FROM cte 
    WHERE YesOrNo = 'NO'  
    GROUP BY Client 
)
SELECT cte.* FROM gte
INNER JOIN cte on cte.Client = gte.Client and cte.rn = gte.mx + 1

虽然它不是所需的解决方案,但它会产生所需的结果。您可以创建一个存储过程并在您的代码中使用它。

注意:这是针对上面提到的同一张表(和数据)进行测试的

希望对你有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多