【问题标题】:Using DataTable values to insert into another DataTable使用 DataTable 值插入另一个 DataTable
【发布时间】:2018-11-29 21:11:18
【问题描述】:

原谅我恶心的方法:我无法在 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 | ..|
| .. | ...      |   ..   |   ..   |   ..   |   ..   | ..|
+-------------------------------------------------------+

我想将 after CustName 的值用于我的计算。

然后计算得出上表中两列之间的百分比差异:

+-------------------------------------------------------+
| ID | CustName | PerDiff02 | PerDiff03 | PerDiff04 | ..|
+-------------------------------------------------------+
| 32 | CustOne  |     0     |     100   |     200   | ..|
| 56 | CustTwo  |           |           |    85.00  | ..|
| 89 | CustThree|   66.66   |    75.00  |    80.00  | ..|
| .. | ...      |   ....    |    ....   |    ....   | ..|
+-------------------------------------------------------+

(百分比是假的,但它们显示了我想要达到的目标。)

PerDiffs 应该从显示的第二个月 (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;
    }

【问题讨论】:

  • 第二个表是否已经存在并且您正在尝试填充它,或者您是否尝试从第一个表创建它?
  • @BrianRogers ,第二个表已经存在,并且所需的前两列数据(ID,CustName)和其余列所需的列标题都已创建。

标签: c# datatable


【解决方案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 和名称列),循环条件应为 j &lt; dt.Columns.Count - 1,因为目标表中的列比源表中的少一列。

下一个问题:您似乎打算将外部循环变量 i 索引到行集合中,但是您以索引 2 而不是 0 开始循环,并且循环条件与列数相反,而不是行数.因此,您最终会跳过前两行,最后可能会错过很多行,具体取决于您与列相比的行数。这看起来可能是复制粘贴错误或其他问题。

还有更多:

  • 如果您从列j 检索month1,那么month2 应该是下个月,或者j + 1。你有它作为j + 2。这会给你错误的结果,最后还会在上个月抛出IndexOutOfRangeException

  • 我认为你的减法可能是倒退的。如果下个月 (month2) 比上个月 (month1) 有所增加,那么您希望将其显示为正数,对吗?所以你需要(month2 - month1) / month1 * 100。你有它作为(month1 - month2)...

  • 您没有正确处理表中可能的空值。在DataTable 中,null 由值DBNull 表示,它不能转换为decimal?。所以这可能是InvalidCastException 的另一个来源。 Field&lt;T&gt; 扩展方法为您处理DBNullnull 之间的转换,所以我建议使用它。 (看起来您曾经使用过此方法,但后来已将其注释掉。)但是请注意,如果您对两个可空小数进行计算,其中一个或另一个具有 null 值,则结果为还有null。而当你在计算后尝试在fDt行中设置空值时,反过来你也会遇到同样的问题,因为DataTable需要DBNull而不是null。因此,您应该使用SetField&lt;T&gt; 方法来处理将值存储回表中。

  • 您也没有考虑month1 可能为零的可能性,这可能导致您的计算中出现DivideByZeroException

既然您已经发布了其余代码,我注意到了其他一些问题:

  • 您没有将列数据类型从第一个表复制到第二个表。

  • 在将行添加到目标表并将客户 ID 和名称复制到其中的循环中,您从行索引 1 而不是 0 开始。这将跳过第一行。您还将错过最后一行,因为您的循环条件是i &lt; dt.Rows.Count - 1 而不是i &lt; 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;
}

【讨论】:

  • 谢谢!这似乎 100% 有效!我已经为此工作了几个月! (我是一个初级开发人员,有点被扔进去了,有很多小错误,我已经看过了!)感谢您花时间在这个答案和协助上!我投票不够!
【解决方案2】:

尝试以下:

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);
                }
            }


        }
    }
}

【讨论】:

  • 感谢您的回答!我应用了你的foreach,因为这是我唯一需要添加的东西,看来(我现在已经将我的完整方法添加到了帖子中)。您的foreachnewRow["ID"] = row.Field&lt;int?&gt;("ID"); 行上给出Specified cast is not valid 错误
猜你喜欢
  • 2011-08-10
  • 1970-01-01
  • 1970-01-01
  • 2022-08-24
  • 1970-01-01
  • 2014-10-20
  • 2016-04-27
  • 2018-10-02
相关资源
最近更新 更多