C#LINQ查询如何在开始和结束索引之间进行选择

时间:2019-01-07 10:36:16

标签: c# linq

我在SQL Server中有10万多行。我想以1000行为单位进行选择,例如1-1000,然后1001-2000,然后2001-3000 ...直到完成。

我使用此代码

var ff = ((from a in db.Customers select).Skip(i).Take(i+1000));

但是它不起作用。如果您还有其他解决方案,请给我建议。

3 个答案:

答案 0 :(得分:2)

您没有选择任何东西,应该是

  printArr(stuData:ObservableArray<StudentData>){
    for(let i=0; i<stuData.length; i++)
    console.log("PrintData:", stuData.getItem(i));
    }

或流利地

var ff = ((from a in db.Customers select a).Skip(i * 1000).Take(1000));

答案 1 :(得分:1)

有人建议查询您本地流程中的所有项目,然后使用“跳过/获取”将结果分为页面。

这样做的缺点是,如果在第三页之后发现您不再需要其他页面,则将浪费大量的处理能力:您什么也没拿到1000页。 LINQ的宏伟想法是尽可能延迟枚举:仅获取您实际需要的项目(也许更多)。

除此之外,每次请求下一页时,“跳过”都将从第一个元素开始,因此,如果您要求页面大小为100的页面1000,它将从第一个元素开始,然后跳过1000 * 100个元素在采取下一个步骤之前:多么浪费处理能力!

另一项建议是每次您要求页面时都要对完整的收藏进行排序。如果要获取第3页,为什么还要对第1000页中的元素进行排序?

显然您对客户的顺序不感兴趣,所以让我们按主键对其进行排序:宾果游戏:您的客户已经按主键进行了排序!

当请求页面时,我们会记住最后返回的元素的主键;当请求下一页时,我们将从第一个客户开始,其主键大于此最后一个。

我将其写为IQueryable<Customer>的扩展功能,仅当您在枚举时要求下一页时,我才精确地获取一页。因此,如果您在获取第3页后停止枚举,则不会获取第4页及更高版本。

private static IEnumerable<ICollection<Customer>> ToPages(
    this IQueryable<Customer> customers, int pageSize)
{
    int lastFetchedCustomerId = 0; // no primary key fetched yet

    // get the first page:
    var page = customers.Where(customer => customer.Id > lastFetchedCustomerId)
        .Take(pageSize)
        .ToList();

    // as long as there is a Customer in the page, return the page
    while (page.Count != 0)
    {   // there are customers
        yield return page;

        lastFetchedCustomerId = page[page.Count-1].Id;
        page = customers.Where(customer => customer.Id > lastFetchedCustomerId)
            .Take(pageSize)
            .ToList();
    }
}

用法:

using (var dbContext = new MyDbContext(...))
{
     const int pageSize = 100
     IEnumerable<ICollection<Customer>> customerPages = dbContext.Customers.ToPages(pageSize);
     // note: nothing has been enumerated yet, no data has been fetched

     foreach (var customerPage in customerPages)
     {
         // one page has been fetched, we can do something with the Customers in the page
        foreach (Customer customer in customerPage)
        {
            ProcessCustomer(customer);
            bool continueProcessingCustomers = ...;

            if (!continueProcessingCustomers) return;
            // so if you break in page 3, the other pages are not fetched!
        }
    }
}

请注意,您将始终每页获取客户。因此,如果您在处理完第三位客户后决定不需要该页面的其余客户,那么他们将一无所获。但这总是比吸引所有顾客更好。

最后,如果您知道所有DbSet在属性ID中都具有主键,请考虑实现接口IId,以便ToCustomerPages可用于所有表:

interface IId
{
    public int Id {get;}
}
class Customer : IId
{
    public int Id {get; set; }
    ...
}  

class Order : IId
{
    public int Id {get; set; }
    ...
}   

ToCustomerPages的通用版本:

private static IEnumerable<ICollection<TSource>> ToPages<TSource>(
    this IQueryable<TSource> source, int pageSize)
    where TSource: IId
{
    ...
}

现在您也可以在页面中获取订单了:

var orderPages = dbContext.Orders.ToPages(100);

答案 2 :(得分:0)

在Linq to Entities中,您必须对项目进行排序才能使用Skip。比起Skip,您可以跳过一些记录,而Take中,您可以使用其他记录。

在下面的示例中,While循环中,您每次都可以访问1000条记录(part)。记录按Id排序,必要时可以使用任何其他属性。 ToList()是可选的,以实现您的结果。

根据Marnus的建议,您可以跳过获取所有物品的计数。

 foreach (var page in db.Customers .OrderBy(x => x.Id).GetPages(1000))
 {
     // page is IEnumerable<Customer> with count 1000 or less
 }

    public static IEnumerable<IEnumerable<T>> GetPages<T>(this IEnumerable<T> source, int pageSize)
    {
        int i = 0;
        IEnumerable<T> page = null;
        while (page == null || page.Count() == pageSize)
        {
            page = source.Skip(i).Take(pageSize);
            i += pageSize;
            yield return page;
        }
    }