因为项目原因,需要对关系数据表查询并输出交叉表,这个问题网上资料很多,但基本思路雷同,通过拼sql,用case。。when生成表头。邹建有一个很好的过程,输入简单的参数就可以转换和汇总求和。但是存储过程方式受具体数据库影响,不易扩展。所以想有个方法对内存表进行处理,生成交叉表。网上找了很久,终于发现了老外的一个方法,不敢独享,共同学习。(求和有问题,修改了一下,可以多加一列”合计”)
/// <summary>
/// 行列转换数据表
/// </summary>
/// <param name="table">要转换的源表</param>
/// <param name="columnX">显示在行的字段,类似mdx中rows</param>
/// <param name="columnY">显示在列的字段,类似mdx中columns</param>
/// <param name="columnZ">指标名</param>
/// <param name="nullValue">空值表示方式</param>
/// <param name="sumxValues">是否取x轴合计</param>
/// <param name="sumyValues">是否取y轴合计</param>
/// <returns></returns>
public DataTable GetInversedDataTable(DataTable table, string columnX, string columnY, string columnZ, string nullValue, bool XsumValues,bool YsumValues)
{
//Create a DataTable to Return
DataTable returnTable = new DataTable();
if (string.IsNullOrEmpty(columnX))
{
columnX = table.Columns[0].ColumnName;
}
//Add a Column at the beginning of the table
returnTable.Columns.Add(columnY);
//Read all DISTINCT values from columnX Column in the provided DataTale
List<string> columnXValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
string columnXTemp = dr[columnX].ToString();
if (!columnXValues.Contains(columnXTemp))
{
//Read each row value, if it's different from others provided, add to the list of values and creates a new Column with its value.
columnXValues.Add(columnXTemp);
returnTable.Columns.Add(columnXTemp);
}
}
//如果有合计列则增加合计列
if (XsumValues)
{
columnXValues.Add("合计");
returnTable.Columns.Add("合计",Type.GetType("System.Decimal"));
}
//Verify if Y and Z Axis columns re provided
if (!string.IsNullOrEmpty(columnY) && !string.IsNullOrEmpty(columnZ))
{
//Read DISTINCT Values for Y Axis Column
List<string> columnYValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
if (!columnYValues.Contains(dr[columnY].ToString()))
{
columnYValues.Add(dr[columnY].ToString());
}
}
//Loop all Column Y Distinct Value
foreach (string columnYValue in columnYValues)
{
decimal sumx=decimal.Zero;
//Creates a new Row
DataRow drReturn = returnTable.NewRow();
drReturn[0] = columnYValue;
//foreach column Y value, The rows are selected distincted
DataRow[] rows = table.Select((columnY + "='") + columnYValue + "'");
//Read each row to fill the DataTable
foreach (DataRow dr in rows)
{
string rowColumnTitle = dr[columnX].ToString();
//Read each column to fill the DataTable
foreach (DataColumn dc in returnTable.Columns)
{
if (dc.ColumnName == rowColumnTitle)
{
//If Sum of Values is True it try to perform a Sum
//If sum is not possible due to value types, the value displayed is the last one read
drReturn[rowColumnTitle] = dr[columnZ];
sumx+=decimal.Parse(dr[columnZ].ToString());
}
}
}
if(XsumValues)
{
drReturn["合计"] = sumx;
}
returnTable.Rows.Add(drReturn);
}
}
else
{
throw new Exception("The columns to perform inversion are not provided");
}
//如果行总计则做汇总计算
if (YsumValues)
{
DataRow dr=returnTable.NewRow();
dr[0] = "总计";
for (int i = 1; i < returnTable.Columns.Count; i++)
{
for (int j = 0; j < returnTable.Rows.Count; j++)
{
decimal result1=decimal.Zero;
decimal.TryParse(dr[i].ToString(), out result1);
decimal result2=decimal.Zero;
if (decimal.TryParse(returnTable.Rows[j][i].ToString(), out result2))
dr[i] = (result1 + result2).ToString();
}
}
returnTable.Rows.Add(dr);
}
//if a nullValue is provided, fill the datable with it
if (!string.IsNullOrEmpty(nullValue))
{
foreach (DataRow dr in returnTable.Rows)
{
foreach (DataColumn dc in returnTable.Columns)
{
if (string.IsNullOrEmpty(dr[dc.ColumnName].ToString()))
{
dr[dc.ColumnName] = nullValue;
}
}
}
}
return returnTable;
}
代码蛮多,不知道用linq to dataset有没有简洁的写法,期待linq达人!