我试图通过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;
}
}
所以我没有得到如何创建分页查询,也不知道我是否正在以正确的方式思考。
答案 0 :(得分:2)
您想用什么技术从数据库中获取数据有点不确定。
由于Linq关键字,我假设你想要一个Linq语句,它给你一个带有给定pageSize的项目页面。
然而,您不确定如何获取第X页的数据。您是否有想要分成页面的记录的IQueryable(如在实体框架中 - 强烈推荐),或者您想要更改SQL语句使它会给你页面X?
IQueryable方法
假设您需要类型为T的记录页,并且您有一个IQueryable<T>
来获取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(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<T>
而不是IQueryable<T>
。
没有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的想法来自stackoverflow上的Implement paging in SQL。