根据其他列从重复的列中获取单行

时间:2019-03-26 18:51:48

标签: c# sql linq

让我们说我有这个表/ IQueriable

+------+------+------+------------+-------------+
| 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不为null或第一行不为空的行。

如何在c#中执行此操作?

我可能需要做类似

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

但是我已经很想写短片了。

我可以用foreach或其他内容创建一个新列表,但我想可以用1行完成?

谢谢!

3 个答案:

答案 0 :(得分:0)

如果您只想基于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());

答案 1 :(得分:0)

这是您的一线客:

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

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

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

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

答案 2 :(得分:0)

  

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

您没有明确地说过,但我假设如果两个行具有相同的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;