[英]Creating a SQL Server table from a C# datatable
我有一個 DataTable,我使用 C# 手動創建並加載了數據。
在 SQL Server 2005 中創建使用 DataTable 中的列和數據的表的最有效方法是什么?
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)";
}
在 SQL 中根據客戶端提供的 Datatable 對象定義創建表有點不尋常。 表是 SQL 中精心制作的實體,部署時考慮選擇合適的磁盤,設計時考慮索引以及正確建模數據庫所涉及的所有問題。
最好解釋一下您要實現的目標,以便我們了解要提供什么建議。
附帶說明一下,在 SQL 2008 中,有一種非常簡單的方法可以從客戶端定義的數據表中創建表:將數據表作為表值參數傳遞,然后發出SELECT * INTO <tablename> FROM @tvp
,這將有效傳輸數據表和它的內容數據的定義到SQL一個真實的表。
我知道這個問題已經很老了,但我只是有一些非常相似的東西需要寫。 我采用了我所做的並更改了 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();
}
這是使用此方法並獲取 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);
最后,輸出:
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];
我同意最初的答案,即數據管理不應隨意進行。 確實需要深思熟慮才能保持數據庫平穩運行並考慮到將來的可維護性。 但是,有時需要編碼解決方案,我希望這可以幫助某人。
關於 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 + ")";
}
這是我為了工作而編寫的一些代碼。 它已經過測試並在生產環境中用於腳本生成。 它正確處理DBNull
和主鍵,並且在沒有或只有一個時不會失敗。 它也比這里的其他建議更StringBuilder
,因為它使用StringBuilder
,Linq 的Aggregate
並且不會重復調用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)";
}
}
生成的輸出示例:
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
除了Helper.IsValidDatatable()
之外的所有內容都包含在內,但您明白了。 這至少應該由空檢查替換,並且可能檢查零數據列。 事實上,如果您好奇的話,這段代碼來自我的一個更大(但仍然不到 1000 行)的開源 C# 類庫,它有助於將數據從 C# 類對象移動到 DataTable,然后移動到 SQL 腳本,然后再返回。 它還包含多個輔助數據訪問方法,用於更簡潔的代碼。 我稱它為 EntityJustworks,它也是 IsValidDatatable() 方法的主體所在的位置(在 Helper.cs 類文件中)。 您可以通過 CodePlex ( https://entityjustworks.codeplex.com ) 訪問代碼,也可以通過訪問其博客文章查看可以獲取 EntityJustworks 的所有其他地方(GitHub、Code.MSDN、Pastebin 等)的完整列表( https://csharpcodewhisperer.blogspot.com/2015/01/entity-justworks-class-to-sql.html )。
我只會基於 DataTable 構建一個 Create Table 語句並將其發送到數據庫。 您也可以使用 SMO(SQL Server 管理對象)。 不知道什么是最快的。
這絕對是可以進入框架級類以供重用的東西。
以下鏈接包含有關如何執行此操作的信息(以及SqlTableCreator
代碼示例): 從 ADO.NET DataTable 在 SQL Server 中創建新表。 您可以在此處、 此處和此處找到SqlTableCreator
分支。
希望有幫助。
您需要多高效? 我可能會編寫自己的 TSQL(基於DataTable
的列)來創建表 + 列,但要填充它,您有一個選擇; 如果您的行數適中,則SqlDataAdapter
應該沒問題。 如果您有大量數據,則SqlBulkCopy
接受一個DataTable
和表名...
如果您的意思是來自任意 ADO.Net DataTable,我認為您必須將其編碼為 DDL“代碼生成”工具,在構建“創建表...”DDL 語句時遍歷 DataTables 的列集合。
然后連接到所需的數據庫並執行構造的 Create Table DDL 語句。
這是我為在 progres sql 中工作而編寫的一些代碼。
int count = dataTable1.Columns.Count - 1;
for (int i = 0; i < dataTable1.Columns.Count; i++)
{
if (i == count)
{
name += dataTable1.Columns[i].Caption + " VARCHAR(50)";
}
else
{
name += dataTable1.Columns[i].Caption + " VARCHAR(50)" + ", ";
}
}
// Your SQL Command to create a table
string createString = "CREATE TABLE " + tableName + " (" + name + ")";
//SqlCommand create = new SqlCommand(createString, connection);
NpgsqlCommand create = new NpgsqlCommand(createString, connection);
connection.Open();
create.ExecuteNonQuery();
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.