因為程序的特殊情景,需要在sql查出來的DataTable進行分組,DataTable分組可以使用linq,也可以自己寫分組程序。
linq相對簡單:
cmd.CommandText = sql;
cmd.CommandType = CommandType.Text;
cmd.CommandTimeout = 300;
MySqlDataReader reader = cmd.ExecuteReader();
DataTable tb = new DataTable();
detailTable.Load(reader);
reader.Close();
但是在linq中分組的字段是寫死的,而我的需求是選了哪些列對哪些列進行分組,這樣的話需要自己寫對DataTable 的分組。
dt 是分組前的DataTable,dt_result 是分組后的DataTable,strGroups是分組列以逗號隔開,numColumsStr是以#分割的匯總列
dt_result=AddGroup(dt, strGroups, numColumsStr);
/// <summary>
/// 返回分組匯總后的datatable
/// </summary>
/// <param name="dt">要分組匯總的DataTable</param>
/// <param name="strGroupsField">分組列</param>
/// <param name="numColumsStr">需要求和的列,用#分割</param>
/// <returns></returns>
public DataTable AddGroup(DataTable dt, string strGroupFields, string numColumsStr)
{
if (dt.Rows.Count==0)
return dt;
DataTable table = TableSort(dt, strGroupFields);
DataTable result = dt.Clone();//結果
result.Columns["cinema_pay_type_name"].AllowDBNull = true;
result.Columns["consume_type"].AllowDBNull = true;
IList<string> numColumns = new List<string>();//存放匯總的數量或金額列
string[] columns = numColumsStr.Split('#');
foreach (string str in columns)
{
numColumns.Add(str);
}
string[] strGroupField = strGroupFields.Split(',');
string prevDept = "";//此組合列值
if (strGroupField.Length > 1)//按多個字段分組
{
for (int i = 0; i < strGroupField.Length; i++)
{
prevDept = prevDept + ","+table.Rows[0][strGroupField[i]].ToString();
}
prevDept = prevDept.Trim(',');
}
else if (strGroupFields != "" && strGroupField.Length == 1)//只按一列分組
{
prevDept = table.Rows[0][strGroupFields].ToString();//前一組的分組列值
}
else
{
return dt;
}
int NoOwn = 0;//同組項的第一行記錄的位置
int index = -1;//索引
foreach (DataRow item in table.Rows)
{
index++;//當前行索引
//如果相鄰兩行 分組字段相同 則繼續尋找下行,否則該行為同組的最后一行。進行數據處理
//currGroup == prevDept 表示為同組 則繼續比較下一行
string currGroup = GetGroupStr(table, item, strGroupFields);//此行分組字段組合,如 系統管理員,支付寶,票房
if (currGroup == prevDept && index != table.Rows.Count - 1)
{
continue;
}
else if (index == table.Rows.Count - 1)//到最后一行了
{
if (currGroup.ToString() != prevDept)//最后一行與上一行不是一組
{
//上一組結束,將此組行創建出來
DataRow row = CreateGroupRow(table, strGroupFields, numColumns, NoOwn, index - 1);//新建一組行
result.Rows.Add(row.ItemArray);
//最后一行是下一組
DataRow row2 = table.NewRow();
for (int i = 0; i < strGroupField.Length; i++)
{
row2[strGroupField[i]] = table.Rows[index][strGroupField[i]];
}
foreach (string columnName in numColumns)
{
if (columnName == "ratio_amount")
{
row2[columnName] = decimal.Parse(item[columnName].ToString() == "" ? "0" : item[columnName].ToString());
}
else
row2[columnName] = item[columnName];
}
result.Rows.Add(row2.ItemArray);
}
else//最后一行與上一行同組
{
DataRow row = CreateGroupRow(table, strGroupFields, numColumns, NoOwn, index);
result.Rows.Add(row.ItemArray);
}
}
else//與上一行不同
{
prevDept = GetGroupStr(table, item, strGroupFields);//當前分組標記,新的組開始查找
DataRow row = CreateGroupRow(table, strGroupFields,numColumns, NoOwn, index-1);
NoOwn = index;
result.Rows.Add(row.ItemArray);
}
}
return result;
}
/// <summary>
/// 將DataTable按分組列排序
/// </summary>
/// <param name="oldTable">排序前DataTable</param>
/// <param name="FieldName">分組字段</param>
/// <returns>排序后DataTable</returns>
public DataTable TableSort(DataTable oldTable, string FieldNames)
{
DataTable table = oldTable.Clone();//復制表結構
DataRow[] sortRows = oldTable.Select(null,FieldNames);
table = sortRows.CopyToDataTable();
//foreach(DataRow item in sortRows)
//{
// table.ImportRow(item);
//}
return table;
}
/// <summary>
/// 獲得DataTable中需要匯總的數字列
/// </summary>
/// <param name="table"></param>
/// <returns></returns>
private static IList<string> GetNumColumns(DataTable table)
{
IList<string> numColumns = new List<string>();//存放數字行
foreach (DataColumn column in table.Columns)
{
if (column.DataType == typeof(Decimal) || column.DataType == typeof(Int32) || column.DataType == typeof(Int64)
|| column.DataType == typeof(float) || column.DataType == typeof(Double))
{
numColumns.Add(column.ColumnName);
}
}
return numColumns;
}
/// <summary>
/// 返回這一組匯總行
/// </summary>
/// <param name="table">分組表</param>
/// <param name="numColumns">需要匯總的列</param>
/// <param name="startRowIndex">開始行</param>
/// <param name="endRowIndex">結束行</param>
/// <returns></returns>
private DataRow CreateGroupRow(DataTable table,string strGroupFields, IList<string> numColumns, int startRowIndex, int endRowIndex)
{
DataRow row = table.NewRow();//匯總行
//非匯總列取第一行值
string[] strGroupField = strGroupFields.Split(',');
for (int i = 0; i < strGroupField.Length; i++)
{
if (strGroupField[i] == "cinema_sell_date")
{
row[strGroupField[i]] = table.Rows[startRowIndex][strGroupField[i]];
}
else
{
row[strGroupField[i]] = table.Rows[startRowIndex][strGroupField[i]].ToString();
}
}
//從同組項的第一行記錄的位置 到 同組的最后一行
foreach (string columnName in numColumns)
{
object tempt = 0 ;
long suml=0;
decimal sumd = 0;
for (int i = startRowIndex; i < endRowIndex + 1; i++)
{
//object count = row[columnName];//匯總行 列值
tempt = table.Rows[i][columnName];//當前行 列值
//如果值為null 或 "" 則默認為 0
if (tempt == null || tempt.ToString().Trim() == "")
{
tempt = 0;
}
if (columnName == "sum_count" )
{
suml += Int64.Parse(tempt.ToString());//累加
}
else
sumd += decimal.Parse(tempt.ToString());//累加
}
if (columnName == "sum_count" || columnName == "ratio_amount")
{
row[columnName] = suml;
}
else
row[columnName] = sumd;
}
return row;
}
/// <summary>
/// 獲得此行分組字段的組合,以,隔開
/// </summary>
/// <param name="table"></param>
/// <param name="item"></param>
/// <param name="strGroupFields"></param>
/// <returns></returns>
private string GetGroupStr(DataTable table,DataRow item,string strGroupFields)
{
string[] strGroupField = strGroupFields.Split(',');
string currGroupStr = "";
for(int i = 0; i < strGroupField.Length; i++)
{
currGroupStr = currGroupStr + "," + item[strGroupField[i]].ToString();
}
currGroupStr = currGroupStr.Trim(',');
return currGroupStr;
}