[AX]AX2012 AIF(十):Query service系统服务中处理大数据集
在查询服务中可以使用分页(pageing)和流(Streaming)两种方式处理查询返回的大量数据。
- 分页:客户端程序可以传入一个page对象,由它分页返回直至最大记录数上限的数据(默认每个数据源25000条记录)。分页在三种查询服务(静态查询、用户自定义查询、动态查询)中都可以使用,如果传入NULL的page对象,则直接返回直至记录数上限的数据。
分页由分基于位置的分页和基于值的分页两种方式,基于值的分页又分标准值分页、顶级数据源值分页和高级值分页三种,下面用实例代码来看看它们具体如何使用。
示例一:
using System; using System.Data; using System.Globalization; using TestQueryServicePosPage.ServiceReference1; namespace TestQueryServicePosPage { class Program { static void Main() { var client = new QueryServiceClient(); var i = 0; DataSet dataSet; Paging paging = new PositionBasedPaging { StartingPosition = 1, NumberOfRecordsToFetch = 10 }; do { // Call the CustTable query using the query service. dataSet = client.ExecuteStaticQuery("CustTable", ref paging); // Code to perform operations on the data returned. Console.WriteLine("Query service call: " + i.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTable: " + dataSet.Tables[0].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTrans: " + dataSet.Tables[1].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTransOpen: " + dataSet.Tables[2].Rows.Count.ToString(CultureInfo.InvariantCulture)); i++; } // Check if the last call returned any data. If so, then this is the next page of data. while (dataSet.Tables[0].Rows.Count > 0); Console.ReadLine(); } } }
在上面的例子中使用了基于位置的分页,每个数据源返回的记录每页不超过10个,通过传入相同的Paging对象多次执行查询顶级数据源以获取所有数据,如果需要按照一定顺序返回记录,需要在Query中显式的指定排序字符串。
示例二:
var client = new QueryServiceClient(); var i = 0; Paging paging = new ValueBasedPaging() { RecordLimit = 10 }; do { // Call the CustTable query using the query service. var dataSet = client.ExecuteStaticQuery("CustTable", ref paging); // Code to perform operations on the data returned. Console.WriteLine("Query service call: " + i.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTable: " + dataSet.Tables[0].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTrans: " + dataSet.Tables[1].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTransOpen: " + dataSet.Tables[2].Rows.Count.ToString(CultureInfo.InvariantCulture)); i++; } // Check if the returned bookmark is NULL. If so, there is no more data to be returned. while (((ValueBasedPaging)paging).Bookmark != null);
上面的例子演示如何使用标准值分页,通过查询ValgeBasedPaging对象的Bookmark检查是否还有下一页的数据,没个数据源都返回不超过RecordLimit指定的记录数。
示例三:
var client = new QueryServiceClient(); var i = 0; Paging paging = new TopLevelValueBasedPaging() { RecordLimit = 25, LimitTopLevelDataSourcesOnly = true }; do { // Call the CustTable query using the query service. var dataSet = client.ExecuteStaticQuery("CustTable", ref paging); // Code to perform operations on the data returned. Console.WriteLine("Query service call: " + i.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTable: " + dataSet.Tables[0].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTrans: " + dataSet.Tables[1].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTransOpen: " + dataSet.Tables[2].Rows.Count.ToString(CultureInfo.InvariantCulture)); i++; } // Check if the last call returned any data. If so, then this is the next page of data. while (((ValueBasedPaging)paging).Bookmark != null);
上面的例子演示顶级记录值分页的使用,参数LimitTopLevelDataSourcesOnly设为true时顶级数据源只返回RecordLimit指定的记录数,子级数据源则返回最大记录数上限的记录数;如果LimitTopLevelDataSourcesOnly设为false,则所有数据源返回RecordLimit指定记录数的数据源记录,这和标准值分页是一样的。
示例四:
var client = new QueryServiceClient(); var i = 0; var advPaging = new AdvancedValueBasedPaging(); Paging paging = advPaging; advPaging.RecordLimits = new[] { new DataSourceRecordLimit() { DataSourceName = "CustTable", RecordLimit = 2 }, new DataSourceRecordLimit() { DataSourceName = "CustTrans", RecordLimit = 10 }, new DataSourceRecordLimit() { DataSourceName = "CustTransOpen", RecordLimit = 10 }}; do { // Call the CustTable query using the query service. var dataSet = client.ExecuteStaticQuery("CustTable", ref paging); // Code to perform operations on the data returned. Console.WriteLine("Query service call: " + i.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTable: " + dataSet.Tables[0].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTrans: " + dataSet.Tables[1].Rows.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Number of Records in CustTransOpen: " + dataSet.Tables[2].Rows.Count.ToString(CultureInfo.InvariantCulture)); i++; } // Check if the last call returned any data. If so, then this is the next p
上面的例子演示高级值分页的使用,在高级值分页中可以对每个数据源设置返回记录数的限制。
在实际测试中以上的例子和预期的结果都有差异,为了简化测试,这里修改一下查询“CustTable”为:顶级数据源为表CustTable(包含39条记录),CustTable下包含表CustTrans(只有2条记录,分别对应2条CustTable的记录),子级CustTrans和父级CustTable数据源的关系分三种情况,一是Relation=No,两者无关联;二是Relation=Yes,FetchMode=1:1;三是Relations=Yes,FetchMode=1:n,测试结果如下:
样例一:基于位置分页 | 样例二:标准值分页 | 样例三:顶级记录值分页 | 样例四:高级值分页 | |
Relation=No |
NumberOfRecordsToFetch=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页78次 NumberOfRecordsToFetch=2:每次CustTable返回1条记录,CustTrans返回2条记录,分页39次 NumberOfRecordsToFetch=10:每次CustTable返回5条记录,CustTrans返回2条记录,分页8次 NumberOfRecordsToFetch=39:每次CustTable返回20条记录,CustTrans返回2条记录,分页2次 |
RecordLimit=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页78次
RecordLimit=2:每次CustTable返回2条记录,CustTrans返回2条记录,分页20次 RecordLimit=10:每次CustTable返回5条记录,CustTrans返回2条记录,分页8次 RecordLimit=39:每次CustTable返回20条记录,CustTrans返回2条记录,分页2次 |
RecordLimit=1,LimitTopLevelDataSourcesOnly = true:每次CustTable返回1条记录,CustTrans返回2条记录,分页39次
RecordLimit=2,LimitTopLevelDataSourcesOnly = true:每次CustTable返回2条记录,CustTrans返回2条记录,分页20次 RecordLimit=10,LimitTopLevelDataSourcesOnly = true:每次CustTable返回10条记录,CustTrans返回2条记录,分页4次 RecordLimit=39,LimitTopLevelDataSourcesOnly = true:每次CustTable返回39条记录,CustTrans返回2条记录,分页1次 LimitTopLevelDataSourcesOnly = false的情况和标准值分页相同 |
CustTable.RecordLimit=1,CustTrans.RecordLimit=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页78次 CustTable.RecordLimit=1,CustTrans.RecordLimit=2:每次CustTable返回1条记录,CustTrans返回2条记录,分页39次 CustTable.RecordLimit=2,CustTrans.RecordLimit=2:每次CustTable返回2条记录,CustTrans返回1条记录,分页20次 CustTable.RecordLimit=10,CustTrans.RecordLimit=2:每次CustTable返回10条记录,CustTrans返回2条记录,分页4次 CustTable.RecordLimit=10,CustTrans.RecordLimit=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页78次
|
Relation=Yes,FetchMode=1:1 | NumberOfRecordsToFetch=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页2次
NumberOfRecordsToFetch=2:每次CustTable返回2条记录,CustTrans返回2条记录,分页1次 NumberOfRecordsToFetch=10:每次CustTable返回2条记录,CustTrans返回2条记录,分页1次 NumberOfRecordsToFetch=39:每次CustTable返回2条记录,CustTrans返回2条记录,分页1次 |
同样例一,Relation=Yes,FetchMode=1:1 | 同样例一,Relation=Yes,FetchMode=1:1 |
CustTable.RecordLimit=1,CustTrans.RecordLimit=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页2次 CustTable.RecordLimit=1,CustTrans.RecordLimit=2:每次CustTable返回1条记录,CustTrans返回1条记录,分页2次 CustTable.RecordLimit=2,CustTrans.RecordLimit=2:每次CustTable返回2条记录,CustTrans返回2条记录,分页1次 CustTable.RecordLimit=10,CustTrans.RecordLimit=2:每次CustTable返回2条记录,CustTrans返回2条记录,分页1次 CustTable.RecordLimit=10,CustTrans.RecordLimit=1:每次CustTable返回1条记录,CustTrans返回1条记录,分页2次
|
Relation=Yes,FetchMode=1:n | 同Relation=Yes,FetchMode=1:1 | 同样例一,Relation=Yes,FetchMode=1:1 | 同样例一,Relation=Yes,FetchMode=1:1 | 同上 |
从以上得出的结论是如果设置父子数据源关系连接,无论是1:1还是1:n的取值方式,最终都只返回父记录的2条记录,这和我们想要的left join效果是不一致的,或者说分页就不支持1:n连接。其次可以看出值分页的recordlimit是在单个数据源上应用,而位置分页的NumberOfRecordsToFetch是应用在每次父子数据源乘起来的记录总数。上面的结果只是2级2个数据源的情况,如果更复杂些的Query不知道会是什么样的结果,总之,分页的结果不像MSDN说的那样确定,使用的时候还是依据具体的数据自行调试处理吧。
流:可以使用ExecuteStreamedQuery的ExecuteStreamedQuery()和ExecuteStreamedDynamicQuery()方法从自定义查询或者动态查询中返回一个Stream类型的流对象,但是如何从这个Stream中分解出记录的数据MSDN上没有说明,搜遍网络也没有找到有用的信息,等着微软更新再说吧。