いくつかの考慮事項
私の例では、単純化するために、ExcelスプレッドシートのCSVインストレッドを使用しています。どちらを使用する場合でも、ファイルの解析を処理するためにライブラリを使用するようにしてください(たとえば、CSVにstring.Split('、')を使用すると、CSVで考えられるすべてのバリエーションが適切に処理されません。
使用するライブラリの場合、私のアルゴリズムでは、x、y座標を使用して任意のセルにアクセスできれば、作業が簡単になります。
私はこのようなデータに基づいて列タイプを決定することについての専門家ではありません、多分そうするための絶対確実な方法があります。しかし、これは常にかなり不安定で手に負えないものになるだろうと私は思います。たとえば、INTからBIGINTへの変換を考えてみましょう。タイプを決定する値は123のみである可能性がありますが、そのスプレッドシートに記入した人は123456784305のような値を入力できない場合がありますが、123に基づいて変換しているかどうかはわかりません。
最大長を指定したり、VARCHARとTEXTを使用したりする場合も同様です。最初にレコードセット全体を反復処理し、各列の可能な最大値を決定しない限り、これを行うことは困難です。
最後の2つのポイントに基づいて、テーブルを柔軟に保ち、実行時に変換するために、すべてをVARCHARとして保存する方が簡単になると思います。
考えられる解決策
ここで詳細を提供できれば幸いですが、コードとコメントで十分に説明できれば幸いです。
class Program
{
static void Main(string[] args)
{
string fileName = "Products.csv";
/* First, put all of our lines and columns into a list...
* My code assumes that any library you use for this would allow you to access a specific cell using x,y co-ordinatates.
*/
List<List<string>> lines = new List<List<string>>();
using (StreamReader csvReader = new StreamReader(fileName))
{
string line;
while ((line = csvReader.ReadLine()) != null)
{
List<string> columns = new List<string>(line.Split(','));
lines.Add(columns);
}
}
/* Now, iterate through each line.
* 1) Break it into a further list of the colums for that line
* 2) If this is the first line, assume we have headers, which will be the names of the table columns
* 3) Check the second row of that column and determine it's data type. If the value is empty, then go to the next row and check that.
* 4) Keep checking down the rows for each column until you find a value that can determine the data type.
* 5) Use this information to write out the appropriate CREATE TABLE command, and execute.
*/
//Use this dictionary to keep track of the type of each column later on.
Dictionary<string, string> tableTypes = new Dictionary<string, string>();
StringBuilder tableQuery = new StringBuilder("CREATE TABLE " + getTableName(fileName) + " (");
for (int row = 0; row < lines.Count; row++)
{
List<string> currentColumns = lines[row];
for (int column = 0; column < currentColumns.Count; column++)
{
//If this is the first row, need to determine the table structure for this column.
if (row == 0)
{
string columnName = currentColumns[column];
//Now check the same column for the row below, and try to determine it's type.
for (int checkRow = 1; checkRow < lines.Count; checkRow++)
{
string typeValue = getType(lines[checkRow][column]);
if (typeValue != null)
{
//Found a valid type for this column, add to query.
tableQuery.Append(columnName + " " + typeValue);
if (column < (currentColumns.Count - 1))
{
tableQuery.Append(",");
}
else
{
tableQuery.Append(")");
//We're done building the query... Execute it.
Console.WriteLine("Creating new table: " + tableQuery.ToString());
}
tableTypes.Add(columnName, typeValue);
//Stop looking for a non-empty value...
break;
}
}
}
//We're not in the first row anymore, use the dictionary created above to determine what column names to put into your INSERT queries.
else
{
//Insert the rest of the rows here...
}
}
}
Console.ReadLine();
}
/// <summary>
/// This method will determine the Type of an object based on a string value
/// </summary>
/// <param name="Value">The string representation of a value. Eg. "1", "1.0", "foo", etc</param>
/// <returns></returns>
static string getType(string Value)
{
if (String.IsNullOrEmpty(Value) || String.IsNullOrWhiteSpace(Value))
{
return null;
}
long longValue;
decimal decimalValue;
if (Int64.TryParse(Value, out longValue))
return "BIGINT";
if (Decimal.TryParse(Value, out decimalValue))
return "DECIMAL";
//If none of the above worked, just return the type of string
return "NVARCHAR";
}
static string getTableName(string Value)
{
string[] values = Value.Split('.');
string name = values[0];
name = name.Replace(" ", "");
return name;
}
}