在我的项目中,我必须根据上传的 Excel 表创建一个数据库表。那么,一旦使用asp.net的文件上传控件上传了该文件,谁能告诉我如何从Excel工作表动态生成SQL数据库中的表?
例如,我有一个sheet1.xlsx
这样的 Excel 表
ID Name MoblieNO
1 xyz 1234
2 zxy 5678
3 abc 9012
上传文件后,它应该在 sql 数据库中创建一个具有相同列数和相同数据类型的表。
在我的示例中,我使用 Excel 电子表格的 CSV 插入,只是为了简化事情。无论您使用哪个,请确保您使用库来处理文件解析(例如,对 CSV 使用 string.Split(',') 将无法正确处理 CSV 中所有可能的变化。
对于您使用的任何库,对于我的算法,如果您可以使用 x,y 坐标访问任何给定的单元格,事情会变得更容易。
我不是根据这样的数据确定列类型的专家,也许有一种万无一失的方法。但在我看来,这总是会非常不稳定和具体化。例如,从 INT 与 BIGINT 转换。您确定类型所基于的值可能仅为 123,但填写该电子表格的人可能除了能够输入 123456784305 之类的值,但您不知道是否基于 123 进行转换。
指定最大长度或使用 VARCHAR 与 TEXT 也是如此。如果不先遍历整个记录集并确定每列的最大可能值,就很难做到这一点。
基于最后两点,我会说最终将所有内容存储为 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;
}
}