Использование значений DataTable для вставки в другой DataTable - PullRequest
0 голосов
/ 30 ноября 2018

Простите за мой отвратительный метод: я не смог сделать это в SQL (поскольку я использую динамически, и я не разбираюсь в способах SQL достаточно, чтобы заставить его создавать динамический вывод, в котором я нуждаюсь / нуждаюсь) или EF, так как я не смог заставить его работать по неизвестной причине (если у кого-то есть пример, который может помочь новичку с EF и DataTables, поделитесь)

У меня есть DataTable (dt) со значениями, которые я пытаюсь получить, вычислить и затем ввести в другую таблицу данных (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;
    }

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Я думаю, что главная проблема здесь в том, что ваши индексы и условия цикла неверны, что приводит к неверному приведению при извлечении данных.У вас есть этот код:

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 содержит идентификатор клиента, а столбец 1 содержит имя клиента.Ни один из них не является десятичным, поэтому, когда вы извлекаете dt.Rows[i][j] и приводите его к decimal?, это вызывает InvalidCastException.Этот цикл должен начинаться с индекса 2 (чтобы пропустить столбцы идентификатора и имени), а условие цикла должно быть j < dt.Columns.Count - 1, поскольку в целевой таблице меньше столбца, чем в исходной таблице.

Следующая проблема:Похоже, вы намереваетесь использовать переменную внешнего цикла i для индексации в коллекции строк, но вы запускаете цикл с индексом 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 в ваших вычислениях.

Теперь, когда вы опубликовали оставшуюся часть кода, я заметил несколько других проблем:

  • ВыВы не копируете типы данных столбца из первой таблицы во вторую.

  • В цикле, где вы добавляете строки в таблицу назначения и копируете в нее идентификаторы и имена клиентов, вы начинаете с индекса строки 1 вместо 0. Это пропустит первую строку.Вы также пропустите последний ряд, потому что ваше условие цикла равно i < dt.Rows.Count - 1 вместо i < dt.Rows.Count.(Вы можете фактически объединить этот код с внешним циклом вычисления для простоты.)

  • Ваш метод возвращает неправильную таблицу - он должен вернуть fDt, а не dt.

После исправления всех этих проблем (и некоторого упрощения создания столбцов идентификатора и имени) код должен выглядеть примерно так, что должно быть очень близко к тому, что вы хотите:

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;
}
0 голосов
/ 30 ноября 2018

Попробуйте следующее:

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


        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...