49


22

OleDBとExcelの混合データ型:データがありません

データテーブルに読み込むExcelワークシートがあります。Excelシートの特定の列を除いて、すべて問題ありません。 列「ProductID」は、「」や「n ###」などの値が混在しています。

OleDBが自動的にすべてを処理できるようにするをデータセット/データテーブルに読み取ってみましたが、「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 Answer


101


Net 4.0を使用してExcelファイルを読み取ると、 `OleDbDataAdapter`で同様の問題が発生しました。 MS Excelの「PartID」列で混合データタイプを読み取ります。PartID値は数値にすることができます(例: 561)またはテキスト(例: HL4354)、Excel列が「テキスト」としてフォーマットされていたとしても。

私が知ることができることから、ADO.NETは、列の値の大部分に基づいてデータ型を選択します(数値データ型に結び付けられます)。 i.e. サンプルセット内のPartIDのほとんどが数値の場合、ADO.NETは列が数値であることを宣言します。 そのため、ADO.Netは各セルを数値にキャストしようとしますが、「テキスト」PartID値では失敗し、「テキスト」PartIDをインポートしません。

私の解決策は、これがインポートであり、テーブルにヘッダーが含まれないことを示すために、 Extended Properties = IMEX = 1; HDR = NO`を使用するように OleDbConnection`接続文字列を設定することでした。 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();
    var data = ds.Tables["xlsImport"].AsEnumerable();
    var query = data.Where(x => x.Field("LocationID") == "COOKCOUNTY").Select(x =>
                new Contact
                {
                    LocationID= x.Field("LocationID"),
                    PartID = x.Field("PartID"),
                    Quantity = x.Field("Qty"),
                    Notes = x.Field("UserNotes"),
                    UserID = x.Field("UserID")
                });


10


いくつかのフォーラムでは、接続文字列の拡張プロパティに「IMEX = 1; TypeGuessRows = 0; ImportMixedTypes = Text」を追加すると問題が解決すると主張していますが、そうではありませんでした。 最後に、接続文字列の拡張プロパティに「HDR = NO」を追加することでこの問題を解決し(上記のBrian Wellsが示しているように)、混合型をインポートできるようにしました。

次に、データの最初の行の後に列に名前を付ける一般的なコードを追加し、最初の行を削除します。

    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


sh4の問題はありません。混合型の問題に役立ちます。

DateTimeカラムは、過去に私が悲しみを覚えたことを思い出す他の動物です…​ OleDbDataAdapterが日付をdoubleデータ型に変換することがあるExcelファイルを1つ持っています(明らかにExcelは日付をdoubleとして保存し、1900年1月0日からの経過日数をエンコードします)。

回避策は以下を使用することでした:

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


5


混合データ型とExcelを処理するには、2つの方法があります。

*方法1 *

  • Excelスプレッドシートを開き、列形式を 手動で必要な形式。 この場合、「テキスト」。

*方法2 *


1


@ブライアンウェルズありがとう、あなたの提案はトリックをしましたが、完全ではありません…​ 混合フィールドのint-stringで機能しましたが、その後、datetime列には奇妙な文字が含まれていたため、「hack」の上に「hack」を適用しました。

1.- System.Io.File.Copyを実行し、Excelファイルのコピーを作成します。

2.-実行時にプログラムでDatetime列ヘッダーをdatetime形式の何かに変更します。 「01/01/0001」。

3.- Excelを保存し、変更されたファイルにHDR = NOでクエリを実行するトリックを適用します。

トリッキー、はい、しかし、働いて、合理的に速く、誰かがこれに代わるものがあれば、私は聞いてうれしいです。

ご挨拶

P.D. すみません、私の母国語ではありません。


0


*ショートカット→ Excelに混合型の列がある場合:列をZからA *に並べ替えます

私はここでほとんどすべての答えを調べましたが、そのうちのいくつかは私のために働いて、いくつかはそうではありませんでしたが、ADOはExcelファイルにある混合型の列のデータを選択しなかったため、私にとっては望ましくありませんでした。 テキストと数字が混在するスプレッドシートの列をADOに読み取らせるには、 `HDR = NO`を設定する必要があり、その方法では、SQLステートメントで列ヘッダーを使用する機能が失われます。 Excelファイルで列の順序が変更されると、SQLステートメントはエラーまたは誤った出力になります。

*混合データタイプの列では、キーは最初の8行です。 ADOは最初の8行に基づいて列のデータ型を決定します*したがって、接続パラメーターを拡張パラメーターで変更する場合は、Excelファイルで列をZからAに並べ替えてから、ADOでデータを読み取ります。一番上の行はテキスト行であり、列はテキストとして選択されます。

最初の行が数値の場合(列がExcelでTEXTをフォーマットするように設定されているかどうかに関係なく)、ADOはその列を数値型として決定するため、下のテキスト行を読み取ると、それらを数値にキャストできません。 反対の場合、列がテキストである場合、行が数値である場合は、テキストとしてキャストできます。