LINQ / EF - 如何选择数据范围

时间:2014-01-27 09:22:16

标签: c# linq entity-framework

我在员工表中有一组员工(id, name, age)。 我想选择一系列员工,这些员工只是下一步而且在给定ID之前。

因此,例如,如果员工ID为{1, 4, 5, 6, 8, 10, 25, 26, 40},则会给出一个ID来搜索10和范围2.然后它应该选择10个之前和之后的2个项目。

输出应为{6, 8, 10, 25, 26}

我想尽可能少地调用数据库(最好只调用一次)。我尝试编写LINQ查询如下

问题是如何在以下查询中获取start index的值。

 var v = (from x in employeeList
                 where x.ID == itemToSearch
                 select x).ToList().GetRange(index-2,4);

7 个答案:

答案 0 :(得分:2)

你可以这样做。

int key=10;
int range=2;

var v= employeeList.GetRange((employeeList.FindIndex(x => x == key) - range), (range + range + 1));

这适合我。

答案 1 :(得分:0)

以下是另一种解决方案:

 var itemIndex = employeeList.IndexOf(itemToSearch);
 var result = employeeList.Select((item, index) => {
                                       var diff = Math.Abs(index - itemIndex);
                                       if(diff <= 2) return item;
                                       else return Int32.MinValue;
                                       })
                           .Where(x => x != Int32.MinValue)
                           .ToList();

enter image description here

答案 2 :(得分:0)

var v = (from x in employeeList select x).ToList().GetRange( 
((int)((from d in employeeList select d.ID).ToList().FindIndex(e => e == itemToSearch))) - 2 , 5);

答案 3 :(得分:0)

我不认为你可以在1个查询中执行此操作,因为SQL中没有“previous”或“next”记录的概念。无论如何,这都取决于结果的排序方式。

我想你可以:

  1. itemToSearch
  2. 之前,包括 n 记录
  3. itemToSearch
  4. 之后获取 n 记录
  5. 获得(1)和(2)的结合。
  6. 代码示例:

    var before = (from x in employeeList
                  where x.ID <= itemToSearch
                  select x).Take(2);
    
    var after = (from x in employeeList
                 where x.ID >= itemToSearch
                 select x).Take(2);
    
    var result = before.Union(after);
    

答案 4 :(得分:0)

您可以使用以下扩展方法返回与某个谓词匹配的第一个项目周围的项目范围:

public static IEnumerable<T> GetRange<T>(
    this IEnumerable<T> source, Func<T, bool> predicate, int range)
{
    int itemsToFetch = range * 2 + 1;
    Queue<T> queue = new Queue<T>(range + 1);
    bool itemFound = false;
    int itemsFetched = 0;

    using (var iterator = source.GetEnumerator())
    {
        while (iterator.MoveNext())
        {
            T current = iterator.Current;

            if (itemFound) // if item found, then just yielding all next items
            {
                yield return current; // we don't need queue anymore
                itemsFetched++;
            }
            else
            {
                if (predicate(current))
                {
                    itemFound = true;

                    while (queue.Any()) // yield all content of queue
                        yield return queue.Dequeue();

                    yield return current; // and item which matched predicate
                    itemsToFetch = range;
                }
                else
                {
                    queue.Enqueue(current);

                    if (queue.Count >= range)
                        queue.Dequeue();
                }
            }

            if (itemsFetched == itemsToFetch)
                break;
        }
    }
}

用法很简单

var result = employeeList.GetRange(e => e.ID == 10, 2);

它使用队列来跟踪已检查的最后项目。找到项匹配谓词时,会产生所有队列内容。然后我们返回下一个 range 项目数(如果源中有足够的项目)。

对于给定的ID {1,4,5,6,8,10,25,26,40},返回以下数据:

itemToSearch | range | result
---------------------------------------------
    10           2     { 6, 8, 10, 25, 26 }
    40           2     { 25, 26, 40 }
     1           2     { 1, 4, 5 }
    10           0     { 10 }     

答案 5 :(得分:0)

您可以使用此查询:

var index = 3;
var range = 2;
var query = employeeList
    .Where(c=>c.ID <= index)
    .OrderByDescending(c=>c.ID)
    .Take(range + 1)
    .Union(
             employeeList
               .Where(c=>c.ID >= index)
               .OrderBy(c=>c.ID)
               .Take(range + 1)
         );

在EF中,这将产生如下内容:

SELECT * 
FROM
    (SELECT TOP 2 *
     FROM employee
     WHERE ID <= 3
     ORDER BY ID DESC
     UNION
     SELECT TOP 2 *
     FROM employee
     WHERE ID >= 3
     ORDER BY ID) A

答案 6 :(得分:0)

试用此代码

    class Program
        {
            static void Main(string[] args)
            {
                int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
                var b = GetRange(a.AsEnumerable(), 2, 2);
                foreach (var i in b)
                {
                    Console.WriteLine(i);
                }
                Console.ReadLine();
            }

            private static List GetRange(IEnumerable intList, int current, int range)
            {
                List lst = new List();
                int newCurrent = current;
                for (int i = 0; i  intList, int current)
            {
                return intList.SkipWhile(i => !i.Equals(current)).Skip(1).First();
            }

            private static int GetPrevious(IEnumerable intList, int current)
            {
                return intList.TakeWhile(i => !i.Equals(current)).Last();
            }


        }