OleDB и смешанные типы данных Excel: отсутствующие данные - PullRequest
50 голосов
/ 13 июля 2010

У меня есть лист Excel, который я хочу прочитать в таблице данных - все хорошо, за исключением одного конкретного столбца в моем листе Excel.Столбец ProductID представляет собой сочетание значений, таких как ########## и n#########.

Я пытался позволить OleDB обрабатывать все автоматически , считывая его в набор данных /datatable, но любые значения в ProductID, такие как n######, отсутствуют, игнорируются и оставляются пустыми.Я попытался вручную создать свой DataTable, просматривая каждую строку с помощью устройства чтения данных, но с точно такими же результатами.

Вот код:

// add the column names manually to the datatable as column_1, column_2, ...
for (colnum = 0; colnum < num_columns; colnum ++){
  ds.Tables["products"].Columns.Add("column_" +colnum , System.Type.GetType("System.String")); 
}
while(myDataReader.Read()){
  // loop through each excel row adding a new respective datarow to my datatable 
  DataRow a_row = ds.Tables["products"].NewRow();
  for (col = 0; col < num_columns; col ++){
    try {  a_row[col] = rdr.GetString(col);  }
    catch {  a_row[col] = rdr.GetValue(col).ToString(); }
  }
  ds.Tables["products"].Rows.Add(a_row);
}

Я не понимаю, почему он выигралПозвольте мне читать значения вроде n######.Как я могу это сделать?

Ответы [ 6 ]

102 голосов
/ 19 апреля 2011

При использовании .Net 4.0 и чтении файлов Excel у меня была похожая проблема с OleDbDataAdapter - то есть чтение в смешанном типе данных в столбце «PartID» в MS Excel, где значение PartID может быть числовым (например, 561)или текст (например, HL4354), даже если столбец Excel был отформатирован как «Текст».

Из того, что я могу сказать, ADO.NET выбирает тип данных на основе большинства значений в столбце (с привязкой к числовому типу данных).т.е. если большинство PartID в наборе образцов являются числовыми, ADO.NET объявит столбец числовым.Поэтому ADO.Net будет пытаться привести каждую ячейку к числу, что приведет к ошибке «текстовых» значений PartID и не импортирует эти «текстовые» PartID.

Мое решение состояло в том, чтобы установить OleDbConnection строку подключения, чтобы использовать Extended Properties=IMEX=1;HDR=NO, чтобы указать, что это импорт, и что таблица (таблицы) не будет включать заголовки.Файл Excel имеет строку заголовка, поэтому в этом случае скажите ado.net, чтобы он не использовался.Затем в коде удалите эту строку заголовка из набора данных и вуаля, у вас есть смешанный тип данных для этого столбца.

string sql = "SELECT F1, F2, F3, F4, F5 FROM [sheet1$] WHERE F1 IS NOT NULL";

OleDbConnection connection = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + PrmPathExcelFile + @";Extended Properties=""Excel 8.0;IMEX=1;HDR=NO;TypeGuessRows=0;ImportMixedTypes=Text""");

OleDbCommand cmd = new OleDbCommand(sql, connection);
OleDbDataAdapter da = new OleDbDataAdapter(cmd);

DataSet ds = new DataSet();
ds.Tables.Add("xlsImport", "Excel");
da.Fill(ds, "xlsImport");

// Remove the first row (header row)
DataRow rowDel = ds.Tables["xlsImport"].Rows[0];
ds.Tables["xlsImport"].Rows.Remove(rowDel);

ds.Tables["xlsImport"].Columns[0].ColumnName = "LocationID";
ds.Tables["xlsImport"].Columns[1].ColumnName = "PartID";
ds.Tables["xlsImport"].Columns[2].ColumnName = "Qty";
ds.Tables["xlsImport"].Columns[3].ColumnName = "UserNotes";
ds.Tables["xlsImport"].Columns[4].ColumnName = "UserID";

connection.Close(); 

// теперь вы можете использовать LINQ для поиска в полях

    var data = ds.Tables["xlsImport"].AsEnumerable();
    var query = data.Where(x => x.Field<string>("LocationID") == "COOKCOUNTY").Select(x =>
                new Contact
                {
                    LocationID= x.Field<string>("LocationID"),
                    PartID = x.Field<string>("PartID"),
                    Quantity = x.Field<string>("Qty"),
                    Notes = x.Field<string>("UserNotes"),
                    UserID = x.Field<string>("UserID")
                });
10 голосов
/ 22 августа 2012

Некоторые форумы, которые я обнаружил, утверждают, что добавление IMEX=1;TypeGuessRows=0;ImportMixedTypes=Text к Расширенным свойствам в строке подключения решит проблему, но это не так. Я наконец решил эту проблему, добавив «HDR = NO» к «Расширенным свойствам» в строке подключения (как показано выше Брайаном Уэллсом), чтобы можно было импортировать смешанные типы.

Затем я добавил общий код для именования столбцов после первой строки данных, а затем удалил первую строку.

    public static DataTable ImportMyDataTableFromExcel(string filePath)
    {
        DataTable dt = new DataTable();

        string fullPath = Path.GetFullPath(filePath);

        string connString =
           "Provider=Microsoft.Jet.OLEDB.4.0;" +
           "Data Source=\"" + fullPath + "\";" +
           "Extended Properties=\"Excel 8.0;HDR=No;IMEX=1;\"";

        string sql = @"SELECT * FROM [sheet1$]";

        using (OleDbDataAdapter dataAdapter = new OleDbDataAdapter(sql, connString))
        {
            dataAdapter.Fill(dt);
        }

        dt = BuildHeadersFromFirstRowThenRemoveFirstRow(dt);

        return dt;
    }

    private static DataTable BuildHeadersFromFirstRowThenRemoveFirstRow(DataTable dt)
    {
        DataRow firstRow = dt.Rows[0];

        for (int i = 0; i < dt.Columns.Count; i++)
        {
            if(!string.IsNullOrWhiteSpace(firstRow[i].ToString())) // handle empty cell
              dt.Columns[i].ColumnName = firstRow[i].ToString().Trim();
        }

        dt.Rows.RemoveAt(0);

        return dt;
    }
6 голосов
/ 29 апреля 2011

Нет проблем sh4, рад, что это помогает с проблемой смешанного типа.

Столбец DateTime - это еще одно животное, которое, как я помню, в прошлом вызывало у меня горе ... у нас есть один файл excel, который мы обрабатываем, и иногда OleDbDataAdapter преобразует даты в тип данных double (очевидно, Excel хранит даты как double, что закодировать количество дней, прошедших с 0 января 1900 года).

Обходной путь должен был использовать:

OleDbConnection mobjExcelConn = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + txtExcelFile.Text + @";Extended Properties=""Excel 8.0;IMEX=1;HDR=Yes;""");

OleDbDataAdapter mobjExcelDataAdapter = new OleDbDataAdapter("Select * from [" + txtSheet.Text + "$] where [Supplier ID] <> '' ", mobjExcelConn);


DateTime dtShipStatus = DateTime.MinValue;
shipStatusOrig = excelRow["Est Ship Date"].ToString(); // excelRow is DataRow in the DataSet via the OleDbDataAdapter             

if (shipStatusOrig != string.Empty)
{
    // Date may be read in via oledb adapter as a double
    if (IsNumeric(shipStatusOrig))
    {
        double d = Convert.ToDouble(shipStatusOrig);
        dtShipStatus = DateTime.FromOADate(d);

        if (DateTime.TryParse(dtShipStatus.ToString(), out dtShipStatus))
        {
            validDate = true;
            Debug.WriteLine("{0} converted: ", dtShipStatus.ToString("s"));
        }
    }
    else
    {
        if (ValidateShipDate(shipStatusOrig))
        {
            dtShipStatus = DateTime.Parse(shipStatusOrig);
            validDate = true;
            Debug.WriteLine("{0} converted: ", dtShipStatus.ToString("s"));
        }
        else
        {
            validDate = false;
            MessageBox.Show("Invalid date format in the Excel spreadsheet.\nLine # " + progressBar1.Value + ", the 'Ship Status' value '" + shipStatusOrig + "' is invalid.\nDate should be in a valid date time format.\ne.g. M/DD/YY, M.D.Y, YYYY-MM-DD, etc.", "Invaid Ship Status Date");
        }
    }
...
}
        public static Boolean IsNumeric (Object Expression)
        {
            if(Expression == null || Expression is DateTime)
                return false;

            if(Expression is Int16 || Expression is Int32 || Expression is Int64 || Expression is Decimal || Expression is Single || Expression is Double || Expression is Boolean)
                return true;

            try
            {
                if(Expression is string)
                    Double.Parse(Expression as string);
                else
                   Double.Parse(Expression.ToString());
                return true;
            } catch {} // just dismiss errors but return false

            return false;
        }

        public bool ValidateShipDate(string shipStatus)
        {
            DateTime startDate;
            try
            {
                startDate = DateTime.Parse(shipStatus);
                return true;
            }
            catch
            {
                return false;
            }
        }
6 голосов
/ 13 июля 2010

Существует два способа обработки смешанных типов данных и Excel.

Метод 1

  • Откройте свою таблицу Excel и вручную установите формат столбца в желаемый формат. В данном случае «Текст».

Метод 2

  • Существует «хак», который состоит в добавлении «IMEX = 1» в строку подключения примерно так:

    Поставщик = Microsoft.Jet.OLEDB.4.0; Источник данных = myfile.xls; Расширенные свойства = Excel 8.0; IMEX = 1

  • Это попытается обработать смешанные форматы Excel в соответствии с тем, как они установлены в вашем реестре. Это может быть установлено вами локально, но для сервера это, вероятно, не вариант.

1 голос
/ 28 апреля 2011

@ Брайан Уэллс Спасибо, ваше предложение сработало, но не полностью ... Сработало для int-строки смешанного поля, но после этого столбцы datetime шли со странными символами, поэтому я применил «взлом» над"hack".

1.- Сделайте System.Io.File.Copy и создайте копию файла Excel.

2.- Программно измените заголовки столбца Datetime во время выполнения на что-нибудьв формате datetime, то есть «01/01/0001».

3.- Сохраните Excel, а затем примените свой трюк, выполнив запрос с HDR = NO, к измененному файлу.

TrickyДа, но работал, и достаточно быстро, если у кого-нибудь есть альтернатива, я буду рад услышать.

Привет.

П.Д. Извините, мой английский, это не мой роднойязык.

0 голосов
/ 05 июня 2018

Ярлык -> если у вас есть столбец смешанного типа в Excel: сортируйте столбец Z по A

Я в значительной степени просмотрел все ответы здесь, и некоторые из них работали для меня, а некоторые - нет, однако ни один не был желательным для меня, потому что каким-то образом ADO не выбрал данные в столбце смешанного типа, который я имел в своем Excel файл. Мне пришлось установить HDR=NO, чтобы ADO читал мой столбец электронной таблицы, который представляет собой смесь текста и чисел, и таким образом я теряю способность использовать заголовки столбцов в моих операторах SQL, что не очень хорошо. Если порядок столбцов в файле Excel изменится, оператор SQL приведет к ошибке или неправильному выводу.

В столбце со смешанным типом данных ключ - это первые 8 строк. ADO определяет тип данных для столбца на основе первых 8 строк Так что, если вы все еще хотите изменить строку подключения с расширенными параметрами, просто отсортируйте столбец Z в A в файле Excel, прежде чем читать данные ADO, чтобы Таким образом, строки сверху будут текстовыми, и тогда ваш столбец будет выбран как текст.

Если ваши исходные строки являются числами (независимо от того, задан ли для вашего столбца формат TEXT в Excel), ADO определит эти столбцы как числовой тип, поэтому, прочитав строки текста ниже, он не сможет преобразовать их в число. В противном случае, если в столбце определен текст, если в строке есть число, число, оно может быть преобразовано в текст.

...