使用DataTable值插入另一个DataTable

时间:2018-11-29 21:11:19

标签: c# datatable

请原谅我的令人作呕的方法:我无法在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;

    }            
} 

我加入了两条注释掉的行,因为它们会产生与该循环当前相同的错误:

  

指定的转换无效。

对于声明month1month2的行。

问题:

如何从方程式中的一个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;
    }

2 个答案:

答案 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>扩展方法可以为您处理DBNullnull之间的转换,因此建议您使用它。 (看起来您一次使用了此方法,但此后已将其注释掉。)但是请注意,如果您对两个可为空的小数进行计算,其中一个或另一个具有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);
                }
            }


        }
    }
}