alc*_*cal 38 c# sql-server datatable ado.net
我有一个DataTable,我手动创建并使用C#加载数据.
在SQL Server 2005中创建使用DataTable中的列和数据的表的最有效方法是什么?
Ami*_*min 58
public static string CreateTABLE(string tableName, DataTable table)
{
string sqlsc;
sqlsc = "CREATE TABLE " + tableName + "(";
for (int i = 0; i < table.Columns.Count; i++)
{
sqlsc += "\n [" + table.Columns[i].ColumnName + "] ";
string columnType = table.Columns[i].DataType.ToString();
switch (columnType)
{
case "System.Int32":
sqlsc += " int ";
break;
case "System.Int64":
sqlsc += " bigint ";
break;
case "System.Int16":
sqlsc += " smallint";
break;
case "System.Byte":
sqlsc += " tinyint";
break;
case "System.Decimal":
sqlsc += " decimal ";
break;
case "System.DateTime":
sqlsc += " datetime ";
break;
case "System.String":
default:
sqlsc += string.Format(" nvarchar({0}) ", table.Columns[i].MaxLength == -1 ? "max" : table.Columns[i].MaxLength.ToString());
break;
}
if (table.Columns[i].AutoIncrement)
sqlsc += " IDENTITY(" + table.Columns[i].AutoIncrementSeed.ToString() + "," + table.Columns[i].AutoIncrementStep.ToString() + ") ";
if (!table.Columns[i].AllowDBNull)
sqlsc += " NOT NULL ";
sqlsc += ",";
}
return sqlsc.Substring(0,sqlsc.Length-1) + "\n)";
}
Run Code Online (Sandbox Code Playgroud)
Rem*_*anu 23
在SQL中,从客户端提供的Datatable对象定义创建表有点不寻常.表是SQL中精心设计的实体,部署时考虑选择适当的磁盘,在设计时考虑索引,并考虑正确建模数据库所涉及的所有问题.
更好地解释你想要达到的目标,这样我们才能理解给出的建议.
作为一个方面说明,在2008年的SQL有创建一个表出来定义数据表中的客户端的一个非常简单的方法:通过数据表的表值参数,然后发出SELECT * INTO <tablename> FROM @tvp,这将有效地传输数据表的定义和内容将数据转换为SQL中的真实表.
Lon*_*nny 13
我知道这个问题相当陈旧,但我只是有一些我需要写的东西.我接受了我所做的并修改了Amin和rasputino提供的示例,并创建了一个只输出SQL的示例.我添加了一些功能并避免连接,以帮助改进本身就是表现不佳的流程.
/// <summary>
/// Inspects a DataTable and return a SQL string that can be used to CREATE a TABLE in SQL Server.
/// </summary>
/// <param name="table">System.Data.DataTable object to be inspected for building the SQL CREATE TABLE statement.</param>
/// <returns>String of SQL</returns>
public static string GetCreateTableSql(DataTable table)
{
StringBuilder sql = new StringBuilder();
StringBuilder alterSql = new StringBuilder();
sql.AppendFormat("CREATE TABLE [{0}] (", table.TableName);
for (int i = 0; i < table.Columns.Count; i++)
{
bool isNumeric = false;
bool usesColumnDefault = true;
sql.AppendFormat("\n\t[{0}]", table.Columns[i].ColumnName);
switch (table.Columns[i].DataType.ToString().ToUpper())
{
case "SYSTEM.INT16":
sql.Append(" smallint");
isNumeric = true;
break;
case "SYSTEM.INT32":
sql.Append(" int");
isNumeric = true;
break;
case "SYSTEM.INT64":
sql.Append(" bigint");
isNumeric = true;
break;
case "SYSTEM.DATETIME":
sql.Append(" datetime");
usesColumnDefault = false;
break;
case "SYSTEM.STRING":
sql.AppendFormat(" nvarchar({0})", table.Columns[i].MaxLength);
break;
case "SYSTEM.SINGLE":
sql.Append(" single");
isNumeric = true;
break;
case "SYSTEM.DOUBLE":
sql.Append(" double");
isNumeric = true;
break;
case "SYSTEM.DECIMAL":
sql.AppendFormat(" decimal(18, 6)");
isNumeric = true;
break;
default:
sql.AppendFormat(" nvarchar({0})", table.Columns[i].MaxLength);
break;
}
if (table.Columns[i].AutoIncrement)
{
sql.AppendFormat(" IDENTITY({0},{1})",
table.Columns[i].AutoIncrementSeed,
table.Columns[i].AutoIncrementStep);
}
else
{
// DataColumns will add a blank DefaultValue for any AutoIncrement column.
// We only want to create an ALTER statement for those columns that are not set to AutoIncrement.
if (table.Columns[i].DefaultValue != null)
{
if (usesColumnDefault)
{
if (isNumeric)
{
alterSql.AppendFormat("\nALTER TABLE {0} ADD CONSTRAINT [DF_{0}_{1}] DEFAULT ({2}) FOR [{1}];",
table.TableName,
table.Columns[i].ColumnName,
table.Columns[i].DefaultValue);
}
else
{
alterSql.AppendFormat("\nALTER TABLE {0} ADD CONSTRAINT [DF_{0}_{1}] DEFAULT ('{2}') FOR [{1}];",
table.TableName,
table.Columns[i].ColumnName,
table.Columns[i].DefaultValue);
}
}
else
{
// Default values on Date columns, e.g., "DateTime.Now" will not translate to SQL.
// This inspects the caption for a simple XML string to see if there is a SQL compliant default value, e.g., "GETDATE()".
try
{
System.Xml.XmlDocument xml = new System.Xml.XmlDocument();
xml.LoadXml(table.Columns[i].Caption);
alterSql.AppendFormat("\nALTER TABLE {0} ADD CONSTRAINT [DF_{0}_{1}] DEFAULT ({2}) FOR [{1}];",
table.TableName,
table.Columns[i].ColumnName,
xml.GetElementsByTagName("defaultValue")[0].InnerText);
}
catch
{
// Handle
}
}
}
}
if (!table.Columns[i].AllowDBNull)
{
sql.Append(" NOT NULL");
}
sql.Append(",");
}
if (table.PrimaryKey.Length > 0)
{
StringBuilder primaryKeySql = new StringBuilder();
primaryKeySql.AppendFormat("\n\tCONSTRAINT PK_{0} PRIMARY KEY (", table.TableName);
for (int i = 0; i < table.PrimaryKey.Length; i++)
{
primaryKeySql.AppendFormat("{0},", table.PrimaryKey[i].ColumnName);
}
primaryKeySql.Remove(primaryKeySql.Length - 1, 1);
primaryKeySql.Append(")");
sql.Append(primaryKeySql);
}
else
{
sql.Remove(sql.Length - 1, 1);
}
sql.AppendFormat("\n);\n{0}", alterSql.ToString());
return sql.ToString();
}
Run Code Online (Sandbox Code Playgroud)
这是一个使用此方法并获取SQL的简单测试:
DataTable table = new DataTable("Users");
table.Columns.Add(new DataColumn()
{
ColumnName = "UserId",
DataType = System.Type.GetType("System.Int32"),
AutoIncrement = true,
AllowDBNull = false,
AutoIncrementSeed = 1,
AutoIncrementStep = 1
});
table.Columns.Add(new DataColumn()
{
ColumnName = "UserName",
DataType = System.Type.GetType("System.String"),
AllowDBNull = true,
DefaultValue = String.Empty,
MaxLength = 50
});
table.Columns.Add(new DataColumn()
{
ColumnName = "LastUpdate",
DataType = System.Type.GetType("System.DateTime"),
AllowDBNull = false,
DefaultValue = DateTime.Now,
Caption = "<defaultValue>GETDATE()</defaultValue>"
});
table.PrimaryKey = new DataColumn[] { table.Columns[0] };
string sql = DataHelper.GetCreateTableSql(table);
Console.WriteLine(sql);
Run Code Online (Sandbox Code Playgroud)
最后,输出:
CREATE TABLE [Users] (
[UserId] int IDENTITY(0,1) NOT NULL,
[UserName] nvarchar(50),
[LastUpdate] datetime NOT NULL,
CONSTRAINT PK_Users PRIMARY KEY (UserId)
);
ALTER TABLE Users ADD CONSTRAINT [DF_Users_UserName] DEFAULT ('') FOR [UserName];
ALTER TABLE Users ADD CONSTRAINT [DF_Users_LastUpdate] DEFAULT (GETDATE()) FOR[LastUpdate];
Run Code Online (Sandbox Code Playgroud)
我同意最初的答案,即声明数据管理不应该随意进行.确实需要花费大量精力来保持数据库平稳运行并在将来实现可维护性.但是,有时候编码解决方案是必要的,我希望这可以帮助某人.
关于Amin的答案,我在他的代码中添加了主键.
public static string CreateTABLEPablo(string connectionString, string tableName, System.Data.DataTable table)
{
string sqlsc;
//using (System.Data.SqlClient.SqlConnection connection = new System.Data.SqlClient.SqlConnection(connectionString))
using (System.Data.OleDb.OleDbConnection connection = new System.Data.OleDb.OleDbConnection(connectionString))
{
connection.Open();
sqlsc = "CREATE TABLE " + tableName + "(";
for (int i = 0; i < table.Columns.Count; i++)
{
sqlsc += "\n" + table.Columns[i].ColumnName;
if (table.Columns[i].DataType.ToString().Contains("System.Int32"))
sqlsc += " int ";
else if (table.Columns[i].DataType.ToString().Contains("System.DateTime"))
sqlsc += " datetime ";
else if (table.Columns[i].DataType.ToString().Contains("System.String"))
sqlsc += " nvarchar(" + table.Columns[i].MaxLength.ToString() + ") ";
else if (table.Columns[i].DataType.ToString().Contains("System.Single"))
sqlsc += " single ";
else if (table.Columns[i].DataType.ToString().Contains("System.Double"))
sqlsc += " double ";
else
sqlsc += " nvarchar(" + table.Columns[i].MaxLength.ToString() + ") ";
if (table.Columns[i].AutoIncrement)
sqlsc += " IDENTITY(" + table.Columns[i].AutoIncrementSeed.ToString() + "," + table.Columns[i].AutoIncrementStep.ToString() + ") ";
if (!table.Columns[i].AllowDBNull)
sqlsc += " NOT NULL ";
sqlsc += ",";
}
string pks = "\nCONSTRAINT PK_" + tableName + " PRIMARY KEY (";
for (int i = 0; i < table.PrimaryKey.Length; i++)
{
pks += table.PrimaryKey[i].ColumnName + ",";
}
pks = pks.Substring(0, pks.Length - 1) + ")";
sqlsc += pks;
connection.Close();
}
return sqlsc + ")";
}
Run Code Online (Sandbox Code Playgroud)
这是我为了工作而编写的一些代码。它已经过测试并在生产环境中用于脚本生成。它DBNull正确处理和主键,如果没有或只有一个,也不会失败。它也比这里的其他建议更高效,因为它使用StringBuilder, LinqAggregate并且不会ToString()重复调用。
注意:如果您的数据来自外部来源,请确保您的代码始终清理此方法的输入或检查此方法的输出,然后再针对您的数据库盲目执行生成的脚本。
/// <summary>
/// Creates a SQL script that creates a table where the columns matches that of the specified DataTable.
/// </summary>
public static string BuildCreateTableScript(DataTable Table)
{
if (!Helper.IsValidDatatable(Table, IgnoreZeroRows: true))
return string.Empty;
StringBuilder result = new StringBuilder();
result.AppendFormat("CREATE TABLE [{1}] ({0} ", Environment.NewLine, Table.TableName);
bool FirstTime = true;
foreach (DataColumn column in Table.Columns.OfType<DataColumn>())
{
if (FirstTime) FirstTime = false;
else
result.Append(" ,");
result.AppendFormat("[{0}] {1} {2} {3}",
column.ColumnName, // 0
GetSQLTypeAsString(column.DataType), // 1
column.AllowDBNull ? "NULL" : "NOT NULL", // 2
Environment.NewLine // 3
);
}
result.AppendFormat(") ON [PRIMARY]{0}GO{0}{0}", Environment.NewLine);
// Build an ALTER TABLE script that adds keys to a table that already exists.
if (Table.PrimaryKey.Length > 0)
result.Append(BuildKeysScript(Table));
return result.ToString();
}
/// <summary>
/// Builds an ALTER TABLE script that adds a primary or composite key to a table that already exists.
/// </summary>
private static string BuildKeysScript(DataTable Table)
{
// Already checked by public method CreateTable. Un-comment if making the method public
// if (Helper.IsValidDatatable(Table, IgnoreZeroRows: true)) return string.Empty;
if (Table.PrimaryKey.Length < 1) return string.Empty;
StringBuilder result = new StringBuilder();
if (Table.PrimaryKey.Length == 1)
result.AppendFormat("ALTER TABLE {1}{0} ADD PRIMARY KEY ({2}){0}GO{0}{0}", Environment.NewLine, Table.TableName, Table.PrimaryKey[0].ColumnName);
else
{
List<string> compositeKeys = Table.PrimaryKey.OfType<DataColumn>().Select(dc => dc.ColumnName).ToList();
string keyName = compositeKeys.Aggregate((a,b) => a + b);
string keys = compositeKeys.Aggregate((a, b) => string.Format("{0}, {1}", a, b));
result.AppendFormat("ALTER TABLE {1}{0}ADD CONSTRAINT pk_{3} PRIMARY KEY ({2}){0}GO{0}{0}", Environment.NewLine, Table.TableName, keys, keyName);
}
return result.ToString();
}
/// <summary>
/// Returns the SQL data type equivalent, as a string for use in SQL script generation methods.
/// </summary>
private static string GetSQLTypeAsString(Type DataType)
{
switch (DataType.Name)
{
case "Boolean": return "[bit]";
case "Char": return "[char]";
case "SByte": return "[tinyint]";
case "Int16": return "[smallint]";
case "Int32": return "[int]";
case "Int64": return "[bigint]";
case "Byte": return "[tinyint] UNSIGNED";
case "UInt16": return "[smallint] UNSIGNED";
case "UInt32": return "[int] UNSIGNED";
case "UInt64": return "[bigint] UNSIGNED";
case "Single": return "[float]";
case "Double": return "[double]";
case "Decimal": return "[decimal]";
case "DateTime": return "[datetime]";
case "Guid": return "[uniqueidentifier]";
case "Object": return "[variant]";
case "String": return "[nvarchar](250)";
default: return "[nvarchar](MAX)";
}
}
Run Code Online (Sandbox Code Playgroud)
生成的输出示例:
CREATE TABLE [Order] (
[OrderID] [bigint] UNSIGNED NOT NULL
,[Description] [nvarchar](250) NULL
,[Flag] [bit] NULL
,[Quantity] [int] NULL
,[Price] [decimal] NULL
,[Customer] [nvarchar](MAX) NOT NULL
) ON [PRIMARY]
GO
ALTER TABLE Order
ADD CONSTRAINT pk_OrderIDCustomer PRIMARY KEY (OrderID, Customer)
GO
Run Code Online (Sandbox Code Playgroud)
除 之外的所有内容都包括在内Helper.IsValidDatatable(),但您明白了。这至少应该由空检查替换,并且可能检查零数据列。事实上,如果您好奇的话,这段代码来自我的一个更大(但仍然不到 1000 行)的开源 C# 类库,它有助于将数据从 C# 类对象移动到 DataTable,然后移动到 SQL 脚本,然后再返回。它还包含多个辅助数据访问方法,用于更简洁的代码。我将其称为 EntityJustworks,它也是 IsValidDatatable() 方法的主体所在的位置(在 Helper.cs 类文件中)。您可以通过 CodePlex ( https://entityjustworks.codeplex.com) 或通过访问其博客文章 ( https://csharpcodewhisperer.blogspot.com/2015/01/entity-justworks ) 查看可以获取 EntityJustworks 的所有其他地方(GitHub、Code.MSDN、Pastebin 等)的完整列表-class-to-sql.html)。