如何从表中逐块获取数据?

Posted

技术标签:

【中文标题】如何从表中逐块获取数据?【英文标题】:How to fetch chunk by chunk data from table? 【发布时间】:2017-05-05 09:09:07 【问题描述】:

我正在尝试从 mysql 表中逐块获取数据。

我有一张如下表:

HistoryLogs = 10986119

现在我想从 MyQSL 中逐块获取数据并将其传递给 sqlbulk 副本进行处理。我已决定批量大小为 1000

例如,如果我有 10000 条记录,那么我的查询将如下所示:

SELECT * FROM tbl LIMIT 0,1000;
SELECT * FROM tbl LIMIT 1000,2000;
SELECT * FROM tbl LIMIT 2000,3000;
SELECT * FROM tbl LIMIT 9000,10000;

所以首先我将从表中获取总记录,然后尝试如下:

 private int FetchCount(string table)
        
            using (MySqlCommand cmd = new MySqlCommand("SELECT COUNT(*) FROM " + table, conn))
            
                cmd.CommandTimeout = 0;
                return cmd.ExecuteNonQuery();
            
        

string query = string.Empty;
string table ="HistoryLogs";
int records = FetchCount(table);
for (int i = 0; i < records / 1000 ; i++) //
 
     here I would like to create chunk by chunk query and pass it to Process method
 


 private MySqlDataReader Process(MySqlConnection conn, string query)
        
            using (MySqlCommand cmd = new MySqlCommand(query, conn))
            
                cmd.CommandTimeout = 0;
                MySqlDataReader reader = cmd.ExecuteReader();
                return reader;
            
        

所以我不知道如何创建分页查询,并且不确定我是否以正确的方式思考。

【问题讨论】:

【参考方案1】:

有点不确定您想使用什么技术从数据库中获取数据。

由于 Linq 关键字,我假设您需要一个 Linq 语句,该语句为您提供具有给定 pageSize 的项目页面。

但是,您不确定如何获取页面 X 的数据。您是否有要划分为页面的记录的 IQueryable(如在 Entity Framework 中 - 强烈推荐),或者您是否想更改您的SQL 语句,以便它会给你页面 X?

IQueryable 方法

假设您想要 T 类型的记录页面,并且您有一个 IQueryable&lt;T&gt; 来获取 T 类型的所有记录。

 IQueryable<T> allRecords = ...;

您想将此序列分成页面。每个页面都有一个PageSize、一个PageNr和一个记录序列:

class Page<T>

    public int PageSize get; set;
    public int PageNr get; set;
    public IEnumerable<T> Contents get; set;

现在把AllRecords分成一个页面序列,我用了一个扩展方法:

public static class PagingExtensions

    public static IQueryable<Page<T>> ToPages<T>(this IQueryable<T> allRecords, int pageSize)
    
        return allRecords.Select( (record, i) => new
        
            PageNr = i / pageSize,
            Record = record,
        )
        .GroupBy(item => item.PageNr)
        // intermediate result: sequence of IGrouping<int, T>
        // where key is pageNr
        // and each element in the group are the records for this page
        .Select(group => new Page<T>
        
            PageNr = group.Key,
            PageSize = pageSize,
            Contents = (IEnumerable<T>) group
        );
    

将 MyRecords 序列划分为页面的代码将是:

const int pageSize = 1000;
IQueryable<MyRecord> allMyRecords = ...
IQueryable<Page<MyRecord>> pages = allMyRecords.ToPages(1000);

// do what you want with the pages, for example:
foreach (Page<MyRecord> page in pages)

    Console.WriteLine($"Page page.PageNr");
    foreach (MyRecord record in Page.Contents)
    
       Console.WriteLine(record.ToString());
    

请注意,所有使用的函数都使用延迟执行。在您枚举它们之前不会获取记录。

如果您希望能够在本地内存而不是数据库中的页面中划分集合,请使用 IEnumerable&lt;T&gt; 而不是 IQueryable&lt;T&gt;

没有 IQueryable 的方法

如果您没有 IQueryable 来获取所有记录,您要么必须自己创建一个实现此功能的类,要么根据要获取的页面调整 SQL 查询。不过我不推荐第一种方法。

class Page<T>

    public Page(SqlConnection conn, int pageNr, int pageSize)
    
        this.PageNr = pageNr;
        this.PageSize = pageSize;
    
    private readonly SqlConnection conn;
    public int PageSize get; private set;
    public int PageNr get; private set;

    public IEnumerable<T> ReadContents()
    
        int offset = this.PageNr * this.PageSize;
        int fetch = this.PageSize;
        string cmdText = "SELECT col1, col2, ..."
          + " FROM ... "
          + " WHERE ... "
          + " ORDER BY -- " 
          // this is a MUST there must be ORDER BY statement
          //-- the paging comes here
          + $" OFFSET offset ROWS"
          + $" FETCH NEXT fetch ROWS ONLY;";

        using (SqlCommand cmd = new SqlCommand("cmdText, conn))
        
            using (var sqlDataReader = cmd.ExecuteQuery())
            
                List<T> readItems = sqlDataReader...;
                // you know better than I how to use the SqlDataReader                    
                return readItems
            
        
    

Fetch / Offset 而不是 Enumerable Skip / Take 的想法来自 *** 上的 Implement paging in SQL。

【讨论】:

这真是太棒了。非常感谢您提供答案,我非常感谢您的努力 IQueryable 版本存在一些问题。第一个很容易修复; Contents = (IEnumerable&lt;T&gt;) group 应该是 Contents = group.Select(g =&gt; g.Record)。如果您使用的是 EF,则第二个不是那么多;您不能在查询中使用 Select(T, int) 重载。

以上是关于如何从表中逐块获取数据?的主要内容,如果未能解决你的问题,请参考以下文章

Lua如何从表中获取数据

如何从表中获取所有数据,包括表 ID

如何使用 CodeIgniter 3 中的外键从表中获取列数据

如何从表中获取数据并将其插入另一个表?

如何根据参考值从表中获取数据

Rails:为循环的每次迭代从表中获取数据