请原谅我的令人作呕的方法:我无法在SQL中执行此操作(因为我的使用是动态的,而且我对SQL的方式并不精通,无法使其创建我想要/需要的动态输出)或EF,因为我出于某种未知的原因而无法使其正常工作(如果有人举了例子,可以帮助EF和DataTables初学者,请分享)
我有一个DataTable(dt
),其值是我试图提取,计算然后输入到另一个DataTable(fDt
)中的值。
dt
的示例:
+-------------------------------------------------------+
| ID | CustName | 201501 | 201502 | 201503 | 201504 | ..|
+-------------------------------------------------------+
| 32 | CustOne | 100.00 | 200.00 | 400.00 | 700.00 | ..|
| 56 | CustTwo | | | | 500.00 | ..|
| 89 | CustThree| 222.22 | 333.33 | 444.44 | 555.55 | ..|
| .. | ... | .. | .. | .. | .. | ..|
+-------------------------------------------------------+
我想在之后 CustName
取值以用于计算。
然后,计算将得出上表中两列之间的百分比差异:
+-------------------------------------------------------+
| ID | CustName | PerDiff02 | PerDiff03 | PerDiff04 | ..|
+-------------------------------------------------------+
| 32 | CustOne | 0 | 100 | 200 | ..|
| 56 | CustTwo | | | 85.00 | ..|
| 89 | CustThree| 66.66 | 75.00 | 80.00 | ..|
| .. | ... | .... | .... | .... | ..|
+-------------------------------------------------------+
(这些百分比是伪造的,但它们显示了我想要达到的目标。)
PerDiff
应该从所示的第二个月(201502
)开始并持续到当前。
我相信我已经做到了,但是使用现在的代码:
for (i = 2; i <= (dt.Columns.Count - 2); i++)
{
for (int j = 0; j < (dt.Columns.Count); j++)
{
//decimal? month1 = dt.Rows[i].Field<decimal?>(j);
//decimal? month2 = dt.Rows[i].Field<decimal?>(j + 2);
decimal? month1 = (decimal?)dt.Rows[i][j];
decimal? month2 = (decimal?)dt.Rows[i][j + 2];
fDt.Rows[i][j - 1] = ((month1 - month2) / month1) * 100;
}
}
我加入了两条注释掉的行,因为它们会产生与该循环当前相同的错误:
指定的转换无效。
对于声明month1
和month2
的行。
问题:
如何从方程式中的一个Cells
中的一个DataTable
中获取数据,然后输入到另一个DataTable
中?
如果不清楚,请告诉我!
编辑:
这是获取数据表的整个方法:
public DataTable GetPerDiff(DataTable dt)
{
var fDt = new DataTable();
int i;
int fieldCount = dt.Columns.Count;
string[] colHeaders = new string[fieldCount];
for (i = 0; i < fieldCount; i++)
{
colHeaders[i] = dt.Columns[i].ToString();
}
fDt.Columns.Add(colHeaders[0]);
fDt.Columns.Add(colHeaders[1]);
//Get's the data into the new table
for (i = 1; i < dt.Rows.Count-1; i++)
{
fDt.Rows.Add(dt.Rows[i][0], dt.Rows[i][1]);
}
// Gets the column headers for dataTable fDt
for (i = 2; i <= (dt.Columns.Count - 2); i++)
{
string colName = "PerDiff" + dt.Columns[i + 1];
fDt.Columns.Add(colName);
}
for (i = 2; i <= (dt.Columns.Count - 2); i++)
{
for (int j = 0; j < (dt.Columns.Count); j++)
{
//decimal? month1 = dt.Rows[i].Field<decimal?>(j);
//decimal? month2 = dt.Rows[i].Field<decimal?>(j + 2);
decimal? month1 = (decimal?)dt.Rows[i][j];
decimal? month2 = (decimal?)dt.Rows[i][j + 2];
fDt.Rows[i][j - 1] = ((month1 - month2) / month1) * 100;
}
}
return dt;
}
答案 0 :(得分:1)
我认为这里的主要问题是循环索引和条件错误,这会导致在检索数据时强制转换无效。您有以下代码:
for (i = 2; i <= (dt.Columns.Count - 2); i++)
{
for (int j = 0; j < (dt.Columns.Count); j++)
{
//decimal? month1 = dt.Rows[i].Field<decimal?>(j);
//decimal? month2 = dt.Rows[i].Field<decimal?>(j + 2);
decimal? month1 = (decimal?)dt.Rows[i][j];
decimal? month2 = (decimal?)dt.Rows[i][j + 2];
fDt.Rows[i][j - 1] = ((month1 - month2) / month1) * 100;
}
}
索引表中各列的内部循环以j = 0
开始。但是列0包含客户ID,列1包含客户名称。这些都不是小数,因此当您检索dt.Rows[i][j]
并将其强制转换为decimal?
时,会导致InvalidCastException
。此循环应从索引2开始(以跳过ID和name列),并且循环条件应为j < dt.Columns.Count - 1
,因为目标表中的列比源表少一列。
下一个问题:看来您打算让外部循环变量i
索引到rows集合中,但是您要使用索引2而不是0开始循环,并且循环条件与列数相反,没有行。因此,您最终将跳过前两行,最后可能会错过很多行,具体取决于您与列比较的行数。看起来可能是复制粘贴错误之类的。
还有更多:
如果要从month1
列中检索j
,则month2
应该是下个月,即j + 1
。您将其作为j + 2
。这样会给您错误的结果,并最终在上个月抛出IndexOutOfRangeException
。
我认为您的减法可能落后。如果下个月(month2
)比前一个月(month1
)有所增加,那么您想将其显示为正数,对吗?因此,您将需要(month2 - month1) / month1 * 100
。您将其命名为(month1 - month2)...
。
您没有正确处理表中可能的空值。在DataTable
中,空值由值DBNull
表示,该值不能转换为decimal?
。因此,这可能是InvalidCastException
的另一个来源。 Field<T>
扩展方法可以为您处理DBNull
和null
之间的转换,因此建议您使用它。 (看起来您一次使用了此方法,但此后已将其注释掉。)但是请注意,如果您对两个可为空的小数进行计算,其中一个或另一个具有null
值,则结果也是null
。而且,当您尝试在计算之后在fDt
行中设置空值时,您会遇到同样的问题,因为DataTable
要求使用DBNull
而不是null
。因此,您应该使用SetField<T>
方法来将值存储回表中。
您也没有考虑month1
可能为零的可能性,这可能会导致计算中产生DivideByZeroException
。
现在您已经发布了其余代码,我注意到了其他一些问题:
您没有将列数据类型从第一个表复制到第二个表。
在将行添加到目标表并将客户ID和名称复制到其中的循环中,您将从行索引1而不是0开始。这将跳过第一行。您还将错过最后一行,因为循环条件是i < dt.Rows.Count - 1
而不是i < dt.Rows.Count
。 (为简单起见,您实际上可以将此代码与外部计算循环结合使用。)
您的方法返回了错误的表-它应该返回fDt
,而不是dt
。
解决所有这些问题(并简化了创建ID和名称列的操作)之后,代码应类似于以下内容,应该与您想要的内容非常接近:
public DataTable GetPerDiff(DataTable dt)
{
var fDt = new DataTable();
// Copy columns for customer name and ID
fDt.Columns.Add(dt.Columns[0].ColumnName, dt.Columns[0].DataType);
fDt.Columns.Add(dt.Columns[1].ColumnName, dt.Columns[1].DataType);
// Create the PerDiff columns
for (int j = 2; j < dt.Columns.Count - 1; j++)
{
string colName = "PerDiff" + dt.Columns[j + 1];
fDt.Columns.Add(colName, dt.Columns[j + 1].DataType);
}
for (int i = 0; i < dt.Rows.Count; i++)
{
fDt.Rows.Add(dt.Rows[i][0], dt.Rows[i][1]);
for (int j = 2; j < dt.Columns.Count - 1; j++)
{
decimal? month1 = dt.Rows[i].Field<decimal?>(j);
decimal? month2 = dt.Rows[i].Field<decimal?>(j + 1);
if (month1 != decimal.Zero)
{
fDt.Rows[i].SetField(j, (month2 - month1) / month1 * 100);
}
}
}
return fDt;
}
答案 1 :(得分:0)
尝试以下操作:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("CustName", typeof(string));
dt.Columns.Add("201501", typeof(decimal));
dt.Columns.Add("201502", typeof(decimal));
dt.Columns.Add("201503", typeof(decimal));
dt.Columns.Add("201504", typeof(decimal));
dt.Rows.Add(new object[] {32, "CustOne",100.00, 200.00, 400.00, 700.00});
dt.Rows.Add(new object[] {56, "CustTwo", 100.00 , 200.00 , 300.00, 500.00});
dt.Rows.Add(new object[] {89, "CustThree", 222.22 ,333.33, 444.44, 555.55});
DataTable dt2 = new DataTable();
dt2.Columns.Add("ID", typeof(int));
dt2.Columns.Add("CustName", typeof(string));
for (int i = 2; i < dt.Columns.Count - 1; i++)
{
dt2.Columns.Add("PerDiff" + (i).ToString("0#"), typeof(decimal));
}
foreach (DataRow row in dt.AsEnumerable())
{
DataRow newRow = dt2.Rows.Add();
newRow["ID"] = row.Field<int?>("ID");
newRow["CustName"] = row.Field<string>("CustName");
for (int i = 2; i < dt.Columns.Count - 1; i++)
{
newRow[i] = row.Field<decimal?>(i + 1) / row.Field<decimal?>(i);
}
}
}
}
}