【问题标题】:Get single row out of duplicate columns based on other column根据其他列从重复列中获取单行
【发布时间】:2019-08-17 06:18:40
【问题描述】:

假设我有这张桌子/IQueryable:

+------+------+------+------------+-------------+
| col1 | col2 | col3 | grouperCol | selectorCol |
+------+------+------+------------+-------------+
|    1 | John | Doe  | mail1      |             |
|    1 | John | Doe  | mail2      |           1 |
|    1 | John | Doe  | mail3_x    |             |
|    2 | Bob  | Ross | mail1      |           1 |
|    2 | Bob  | Ross | mail2_x    |             |
|    2 | Bob  | Ross | mail3_x    |             |
|    3 | Jane | Doe  | mail1      |             |
|    3 | Jane | Doe  | mail2      |             |
|    3 | Jane | Doe  | mail3      |             |
+------+------+------+------------+-------------+

我想得到这个结果:

+------+------+------+------------+-------------+
| col1 | col2 | col3 | grouperCol | selectorCol |
+------+------+------+------------+-------------+
|    1 | John | Doe  | mail2      |           1 |
|    2 | Bob  | Ross | mail1      |           1 |
|    3 | Jane | Doe  | mail1      |             |
+------+------+------+------------+-------------+

基本上,我需要保留一行,选择 selectorCol 不为空的行或第一行。

如何在 C# 中做到这一点?

我可能需要做类似的事情

var filtered =  context.table.GroupBy(x => x.col1).Where(... 

但是我已经坚持写得简短了。

我可以用 foreach 或其他东西创建一个新列表,但我想它可以用 1 行来完成?

谢谢!

【问题讨论】:

  • 您在寻找 SQL 或 Linq 答案吗?
  • var filters = context.table.Where(x=> x.selectorCol != null).GroupBy(x => x.col1),这样就可以得到所有不为空的寄存器,我不明白你的 OR 条件..
  • 在每个组上使用OrderBy 将非空selectorCol 排序为第一,然后选择每个组中的第一行。

标签: c# sql linq


【解决方案1】:

这是你的单线:

.GroupBy(x => x.col1, (k, g) => g.FirstOrDefault(x => x.selectorCol == 1) ?? g.FirstOrDefault())

但是,我很好奇这会生成什么样的数据库查询。很可能,分组减少将在内存中完成。

编辑:显然上面的 linq 会生成一个带有子查询的查询。最好将其拆分为 2 种方法以避免性能问题:

.OrderBy(x => x.selectorCol == null)
.GroupBy(x => x.col1, (k, g) => g.FirstOrDefault())

【讨论】:

  • 在 LINQ to SQL 中,这会生成一个非常奇怪的查询,该查询有一个具有两个相似条件的 CASE,一个是 NOT EXISTS,一个是 NOT NOT EXISTS,然后不得不通过其他查询来提取答案,每组一个。
  • @NetMage Ouf,子查询。我很惊讶 linq2sql 设法转换了那部分。
【解决方案2】:

基本上,我需要保留一行,选择 selectorCol 不为空的行或第一行。

您没有明确表示,但我假设如果两行具有相同的Col1,那么它们也具有相同的Col2Col3

要求给定MyRows 的序列,创建一个结果序列,该序列由MyRows 组创建,Col1 具有相同的值。从每个组中,我希望第一个元素具有非空值 SelectorCol

如果你准确地写出需求,看起来并不是很困难。唯一的问题是:组的第一个元素是什么?那是索引最低的那个吗?

由于 GroupBy 不保证保持原始顺序,我们必须记住原始项目的索引。

  • 在您记得原始项目索引的位置进行选择
  • 然后为Col1创建具有相同值的项目组
  • 从每个组中保留 SelectorCol 具有非空值的元素
  • 然后取索引最低的那个。

.

// first remember the original index
var result = myRows.Select( (row, index) => new
{
    Index = index
    Row = row,
}
// Then make groups of rows with same value for Col1
.GroupBy(selectResult => selectResult.Row.Col1,

// Parameter resultSelector: get the key of each group (= common Col1 value)
// and all rows that have this Col1 value
// keep only the groupElements that have a non-null value for SelectorCol
(col1, rowsWithThisCol1) => rows.WithThisCol1
     .Where(groupElement => groupElement.Row.SelectorCol != null)

     // from the remaining rows, keep the one with the lowest index
     .OrderBy(groupElement => groupElement.Index)

     // we don't need the Index anymore, select only the Row
     .Select(groupElement => groupElement.Row)

     // and keep the first:
     .FirstOrDefault();

虽然这个可行,但如果您只想要索引最低的那个,那么对所有组元素进行排序有点浪费。如果您只想枚举一次,请使用聚合。所以不是 OrderBy:

.Aggregate((groupElementWithLowestIndex, groupElement) =>
    // if the index of groupElement is lower,
    // then that element becomes the one with the lowest index

    (groupElement.Index < groupElementWithLowestIndex.Index) ?
     groupElement : groupElementWithLowestIndex)

// result: the one and only groupElement with the lowest index
// note: you are certain that no group is empty! So there is always one with lowest index
// get rid of the index, keep only the Row
.Row;

【讨论】:

    【解决方案3】:

    如果您只想基于col1 进行操作,那么:

    var result = context.table.GroupBy(x => x.col1)
        .Select(g => g.FirstOrDefault(x =>selectorCol != null)??g.First());
    

    对于名字和姓氏(col1col2);

    var result = context.table.GroupBy(x => {x.col1, x.col2})
        .Select(g => g.FirstOrDefault(x =>selectorCol != null)??g.First());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-09-27
      • 2018-10-13
      • 2018-12-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-30
      • 1970-01-01
      相关资源
      最近更新 更多