从C#datatable创建SQL Server表

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)

  • 我相信这段代码(或一些经过代码审查的近似代码-为什么不用时通过连接,为什么用字符串比较来比较类型等)比公认的答案要好。如果不事先定义服务器上表值参数的类型,则可接受的答案将不起作用,至少在我看来,这无法及时创建表。 (2认同)

Rem*_*anu 23

在SQL中,从客户端提供的Datatable对象定义创建表有点不寻常.表是SQL中精心设计的实体,部署时考虑选择适当的磁盘,在设计时考虑索引,并考虑正确建模数据库所涉及的所有问题.

更好地解释你想要达到的目标,这样我们才能理解给出的建议.

作为一个方面说明,在2008年的SQL有创建一个表出来定义数据表中的客户端的一个非常简单的方法:通过数据表的表值参数,然后发出SELECT * INTO <tablename> FROM @tvp,这将有效地传输数据表的定义内容将数据转换为SQL中的真实表.

  • 我觉得这个解决方案违背了自动创建表的目的.如果必须手动创建自定义类型,也可以创建表格!我希望能够拥有一段代码来嗅探表模式,并根据其知识创建相应的表. (9认同)
  • @DavidAirapetyan你是对的.Remus对tvp参数的提示不起作用,因为您必须在数据库端预先定义表类型*,这正是用户试图解决的问题. (3认同)
  • +1 优秀的 TVF 提示!从来没有以这种方式想过这个功能:-)但是正如你自己所说的 - 这可能几乎不会在现实生活中使用...... (2认同)
  • 实际上,这在项目的研究阶段经常使用。例如,我试图将旧的 FoxPro 数据库导入 SQL Server。我正在敲我的头,试图乏味地制作每一列。目前,正确处理那部分并不重要。重要的是将数据输入到 SQL Server 中,在那里我可以更轻松地操作它——包括弄清楚我的字符串 cols 的宽度(现在,它们都是 varchar(max) 而我看到的是什么。 (2认同)
  • Leonard Lobel撰写了关于[SQL Server 2K8表值参数和C#自定义迭代器]的文章(http://lennilobel.wordpress.com/2009/07/29/sql-server-2008-table-valued-parameters-and-c-定制迭代器-A-比赛做在天堂/).据我所知,目标表必须已经存在于数据库中以及[用户定义的表类型](http://msdn.microsoft.com/en-us/library/bb522526(v = sql.105) )的.aspx).如果这对您来说没有选择,那么使用SQL Server Managment对象可能会有所帮助 - 请参阅http://www.codeproject.com/Articles/17169/Copy-Data-from-aANNTable-to-a-SQLServer-Database . (2认同)

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)

我同意最初的答案,即声明数据管理不应该随意进行.确实需要花费大量精力来保持数据库平稳运行并在将来实现可维护性.但是,有时候编码解决方案是必要的,我希望这可以帮助某人.

  • 对于任何关注这个(非常好的)解决方案的人来说,只需进行一些改进: 1. `SYSTEM.DOUBLE` 类型必须映射到 SQL Server 的 `float` 2. 添加 `DEFAULT` 时,请检查是否为空(非空)值`if (isNumeric &amp;&amp; table.Columns[i].DefaultValue.ToString() != string.Empty)` (2认同)

ras*_*ino 6

关于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)


Ada*_*ite 5

这是我为了工作而编写的一些代码。它已经过测试并在生产环境中用于脚本生成。它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)。