データテーブルに読み込むExcelワークシートがあります。Excelシートの特定の列を除いて、すべて問題ありません。列「ProductID」は、##########
やn#########
などの値が混在しています。
OleDBがすべてを自動的に処理できるようにする をデータセット/データテーブルに読み込んでみましたが、n######
などの 'ProductID'の値が欠落しており、無視され、空白のままになっています。データリーダーで各行をループして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######
のような値を読み取れない理由がわかりません。これどうやってするの?
.Net 4.0を使用してExcelファイルを読み取ると、OleDbDataAdapter
で同様の問題が発生しました。つまり、MS Excelの「PartID」列で混合データタイプを読み取ります。またはExcel列が「テキスト」としてフォーマットされていたとしても、テキスト(例:HL4354)。
私が知ることができることから、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")
});
私が見つけたいくつかのフォーラムは、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;
}
混合データ型とExcelを処理するには2つの方法があります。
方法1
方法2
"hack"は、接続文字列に "IMEX = 1"を追加することで構成されます です。
Provider = Microsoft.Jet.OLEDB.4.0; Data Source = myfile.xls; Extended Properties = Excel 8.0; IMEX = 1
これにより、レジストリでの設定に応じて混合Excel形式を処理しようとします。これはローカルで設定できますが、サーバーの場合、おそらくオプションではありません。
Sh4の問題はありません。混合型の問題のおかげで助かります。
DateTime列は、過去に私が悲しみを覚えた他の動物です... OleDbDataAdapterが日付をdoubleデータ型に変換することがあるExcelファイルを処理しています(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;
}
}
@ブライアンウェルズ、ありがとう、あなたの提案はトリックをしましたが、完全ではありません...混合フィールドint-stringで働いていましたが、datetime列はその後奇妙な文字になったので、「ハック」の上に「ハック」を適用しました。
1.- System.Io.File.Copyを実行し、Excelファイルのコピーを作成します。
2.-実行時にプログラムでDatetime列ヘッダーをdatetime形式の何か、つまり「01/01/0001」に変更します。
3.- Excelを保存し、変更されたファイルにHDR = NOでクエリを実行するトリックを適用します。
トリッキー、はい、しかし、働いて、合理的に速く、誰かがこれに代わるものがあれば、私は聞いてうれしいです。
ご挨拶。
P.D.すみません、私の母国語ではありません。
ショートカット-> Excelに混合型の列がある場合:列をZからAに並べ替えます
私はここですべての答えをほとんど調べましたが、そのうちのいくつかは私のために働いたが、いくつかはそうではなかったが、何らかの形でADOは混合型の列のデータを選択しなかったため、 Excelファイルにあることを確認しました。HDR=NO
to ADOテキストと数字が混在しているスプレッドシートの列を読み取ると、SQLステートメントで列ヘッダーを使用することができなくなります。 Excelファイルの列が変更されると、SQLステートメントがエラーまたは誤った出力になります。
混合データ型の列では、キーは最初の8行です。ADO最初の8行に基づいて列のデータ型を決定します拡張パラメータを使用して接続文字列を変更し、Excelファイルで列をZからAに並べ替えてから、ADOでデータを読み取ります。このようにして、上の行がテキスト行、次に列テキストとして選択されます。
最初の行が数字の場合(列がExcelでTEXTをフォーマットするように設定されているかどうかに関係なく)ADOはその列を数値型として決定するため、下のテキスト行を読み取るとキャストできません反対の場合、列がテキストであると判断された場合、行が数値である場合は、テキストとしてキャストできます。