简体   繁体   English

通过DataTable以编程方式创建SQL Server CE表

[英]Programmatically create a SQL Server CE table from DataTable

Does anyone know the best way to create a SQL Server CE (Compact 3.5) table based on the schema of a DataTable at runtime? 有谁知道在运行时基于DataTable的架构创建SQL Server CE(Compact 3.5)表的最佳方法? I don't want to have to formulate a CREATE TABLE statement based on all the different possible datatypes, etc. 我不想基于所有可能的数据类型等来制定CREATE TABLE语句。

As a bonus – do you then know how to fill it directly from a datatable? 另外,您是否知道如何直接从数据表中填充数据?

I've used and updated the Code from Ben Breen: 我已经使用并更新了本·布雷恩(Ben Breen)的代码:

  • Changed GetSqlServerCETypeName to work with all types 更改了GetSqlServerCETypeName以使用所有类型
  • Added a function fow a whole Dataset 添加了一个函数来处理整个数据集
  • And some minor tweaks 和一些小的调整

GetSqlDBTypeFromType GetSqlDBTypeFromType

/// <summary>
    /// Gets the correct SqlDBType for a given .NET type. Useful for working with SQL CE.
    /// </summary>
    /// <param name="type">The .Net Type used to find the SqlDBType.</param>
    /// <returns>The correct SqlDbType for the .Net type passed in.</returns>
    public static SqlDbType GetSqlDBTypeFromType(Type type)
    {
        TypeConverter tc = TypeDescriptor.GetConverter(typeof(DbType));
        if (/*tc.CanConvertFrom(type)*/ true)
        {
            DbType dbType = (DbType)tc.ConvertFrom(type.Name);
            // A cheat, but the parameter class knows how to map between DbType and SqlDBType.
            SqlCeParameter param = new SqlCeParameter();
            param.DbType = dbType;
            return param.SqlDbType; // The parameter class did the conversion for us!!
        }
        else
        {
            throw new Exception("Cannot get SqlDbType from: " + type.Name);
        }
    }

GetSqlServerCETypeName GetSqlServerCETypeName

/// <summary>
    /// The method gets the SQL CE type name for use in SQL Statements such as CREATE TABLE
    /// </summary>
    /// <param name="dbType">The SqlDbType to get the type name for</param>
    /// <param name="size">The size where applicable e.g. to create a nchar(n) type where n is the size passed in.</param>
    /// <returns>The SQL CE compatible type for use in SQL Statements</returns>
    public static string GetSqlServerCETypeName(SqlDbType dbType, int size)
    {
        // Conversions according to: http://msdn.microsoft.com/en-us/library/ms173018.aspx
        bool max = (size == int.MaxValue) ? true : false;
        bool over4k = (size > 4000) ? true : false;

        if (size>0)
        {
            return string.Format(Enum.GetName(typeof(SqlDbType), dbType)+" ({0})", size); 
        }
        else
        {
            return Enum.GetName(typeof(SqlDbType), dbType);
        }
    }

GetCreateTableStatement GetCreateTableStatement

/// <summary>
    /// Genenerates a SQL CE compatible CREATE TABLE statement based on a schema obtained from
    /// a SqlDataReader or a SqlCeDataReader.
    /// </summary>
    /// <param name="tableName">The name of the table to be created.</param>
    /// <param name="schema">The schema returned from reader.GetSchemaTable().</param>
    /// <returns>The CREATE TABLE... Statement for the given schema.</returns>
    public static string GetCreateTableStatement(DataTable table)
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(string.Format("CREATE TABLE [{0}] (", table.TableName));

        foreach (DataColumn col in table.Columns)
        {
            SqlDbType dbType = GetSqlDBTypeFromType(col.DataType);
            builder.Append("[");
            builder.Append(col.ColumnName);
            builder.Append("]");
            builder.Append(" ");
            builder.Append(GetSqlServerCETypeName(dbType, col.MaxLength));
            builder.Append(", ");
        }

        if (table.Columns.Count > 0) builder.Length = builder.Length - 2;

        builder.Append(")");
        return builder.ToString();
    }

CreateFromDataset CreateFromDataset

public static void CreateFromDataset(DataSet set, SqlCeConnection conn)
    {
        conn.Open();
        SqlCeCommand cmd;
        foreach (DataTable table in set.Tables)
        {
            string createSql = copyDB.GetCreateTableStatement(table);
            Console.WriteLine(createSql);

            cmd = new SqlCeCommand(createSql, conn);
            Console.WriteLine(cmd.ExecuteNonQuery());
        }
        conn.Close();
    }

}

I coded a reasonable solution, but was hoping to avoid case statements for the SQL types: 我编写了一个合理的解决方案,但希望避免对SQL类型使用case语句:

Firstly a neat trick to convert from a .NET type to a SqlDBType: 首先,从.NET类型转换为SqlDBType的巧妙技巧:

/// <summary>
/// Gets the correct SqlDBType for a given .NET type. Useful for working with SQL CE.
/// </summary>
/// <param name="type">The .Net Type used to find the SqlDBType.</param>
/// <returns>The correct SqlDbType for the .Net type passed in.</returns>
public static SqlDbType GetSqlDBTypeFromType(Type type)
{
    TypeConverter tc = TypeDescriptor.GetConverter(typeof(DbType));
    if (/*tc.CanConvertFrom(type)*/ true)
    {
        DbType dbType = (DbType)tc.ConvertFrom(type.Name);
        // A cheat, but the parameter class knows how to map between DbType and SqlDBType.
        SqlParameter param = new SqlParameter();
        param.DbType = dbType;
        return param.SqlDbType; // The parameter class did the conversion for us!!
    }
    else
    {
        throw new Exception("Cannot get SqlDbType from: " + type.Name);
    }
}

A case statement for the types for use in SQL Statements: SQL语句中使用的类型的case语句:

    /// <summary>
            /// The method gets the SQL CE type name for use in SQL Statements such as CREATE TABLE
            /// </summary>
            /// <param name="dbType">The SqlDbType to get the type name for</param>
            /// <param name="size">The size where applicable e.g. to create a nchar(n) type where n is the size passed in.</param>
            /// <returns>The SQL CE compatible type for use in SQL Statements</returns>
            public static string GetSqlServerCETypeName(SqlDbType dbType, int size)
            {
                // Conversions according to: http://msdn.microsoft.com/en-us/library/ms173018.aspx
                bool max = (size == int.MaxValue) ? true : false;
                bool over4k = (size > 4000) ? true : false;

                switch (dbType)
                {
                    case SqlDbType.BigInt:
                        return "bigint";
                    case SqlDbType.Binary:
                        return string.Format("binary ({0})", size);
                    case SqlDbType.Bit:
                        return "bit";
                    case SqlDbType.Char:
                        if (over4k) return "ntext";
                        else return string.Format("nchar({0})", size);
ETC...

Then finally the CREATE TABLE statement: 然后最后是CREATE TABLE语句:

    /// <summary>
    /// Genenerates a SQL CE compatible CREATE TABLE statement based on a schema obtained from
    /// a SqlDataReader or a SqlCeDataReader.
    /// </summary>
    /// <param name="tableName">The name of the table to be created.</param>
    /// <param name="schema">The schema returned from reader.GetSchemaTable().</param>
    /// <returns>The CREATE TABLE... Statement for the given schema.</returns>
    public static string GetCreateTableStatement(string tableName, DataTable schema)
    {
        StringBuilder builder = new StringBuilder();
        builder.Append(string.Format("CREATE TABLE [{0}] (\n", tableName));

        foreach (DataRow row in schema.Rows)
        {
            string typeName = row["DataType"].ToString();
            Type type = Type.GetType(typeName);

            string name = (string)row["ColumnName"];
            int size = (int)row["ColumnSize"];

            SqlDbType dbType = GetSqlDBTypeFromType(type);

            builder.Append(name);
            builder.Append(" ");
            builder.Append(GetSqlServerCETypeName(dbType, size));
            builder.Append(", ");
        }

        if (schema.Rows.Count > 0) builder.Length = builder.Length - 2;

        builder.Append("\n)");
        return builder.ToString();
    }

For those that want to do this on normal SQL Server setups (non-CE), I've managed to modify this to work with a SQL Server 2016 setup, when I needed to programmatically create database tables based off of giant csv files (using a 4.7.1 .NET Framework). 对于那些想要在普通SQL Server安装程序(非CE)上执行此操作的用户,当我需要基于巨型csv文件以编程方式创建数据库表时,我设法修改了此设置以与SQL Server 2016安装程序一起使用(使用4.7.1 .NET Framework)。 Please note that this has a check on the max # of columns, but not the max # of rows, so you may hit an error if you don't account for that when dealing with large csv files. 请注意,这会检查最大列数,而不是最大行数,因此如果在处理大型csv文件时不考虑这一点,则可能会遇到错误。

using System.Data;
using System.Data.SqlClient;
using System.ComponentModel;
//
//
//
        private static DataTable GetDataTabletFromCSVFile(string csv_file_path)
        {
            DataTable csvData = new DataTable();
            try
            {
                using (TextFieldParser csvReader = new TextFieldParser(csv_file_path))
                {
                    csvReader.TextFieldType = FieldType.Delimited;
                    csvReader.SetDelimiters(new string[] { "," });
                    csvReader.HasFieldsEnclosedInQuotes = false;
                    string[] colFields = csvReader.ReadFields();
                    int columnCounter = 0;

                    foreach (string column in colFields)
                    {
                        if (columnCounter > 1023)
                        {
                            break; // the table has reached the maximum column size, either ignore the extra columns, or create additional linked tables (sounds like awful table design though).
                        }
                        DataColumn datecolumn = new DataColumn(column);
                        datecolumn.AllowDBNull = true;
                        csvData.Columns.Add(datecolumn);
                        columnCounter++;
                    }
                    while (!csvReader.EndOfData)
                    {
                        string[] fieldData = csvReader.ReadFields();
                        Array.Resize(ref fieldData, 1024);   //max number of columns is 1024 in SQL table, and we're not going through the trouble of making a Sparse table.
                        //Making empty value as null
                        for (int i = 0; i < fieldData.Length; i++)
                        {
                            if (fieldData[i] == "")
                            {
                                fieldData[i] = null;
                            }
                        }
                        csvData.Rows.Add(fieldData);
                    }
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
            return csvData;
        }

        /** <summary>
        * Gets the correct SqlDBType for a given .NET type. Useful for working with SQL.
        * </summary>
        * <param name="type">The .Net Type used to find the SqlDBType.</param>
        * <returns>The correct SqlDbType for the .Net type passed in.</returns>
        */
        public static SqlDbType GetSqlDBTypeFromType(Type type)
        {
            TypeConverter tc = TypeDescriptor.GetConverter(typeof(DbType));

            DbType dbType = (DbType)tc.ConvertFrom(type.Name);
            // A cheat, but the parameter class knows how to map between DbType and SqlDBType.
            SqlParameter param = new SqlParameter();
            param.DbType = dbType;

            return param.SqlDbType; // The parameter class did the conversion for us!!

        }

        /**
        * <summary>
        * The method gets the SQL type name for use in SQL Statements such as CREATE TABLE
        * </summary>
        * <param name="dbType">The SqlDbType to get the type name for</param>
        * <param name="size">The size where applicable e.g. to create a nchar(n) type where n is the size passed in.</param>
        * <returns>A string of the SQL compatible type for use in SQL Statements</returns>
        */
        public static string GetSqlServerTypeName(SqlDbType dbType, int size)
        {
            // Conversions according to: https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-data-type-mappings
            bool max = (size == int.MaxValue || size == -1) ? true : false;
            string returnVal = "";

            if (max)
            {
                returnVal = Enum.GetName(typeof(SqlDbType), dbType) + " (max)";
            }
            else if (size > 0)
            {
                returnVal = string.Format(Enum.GetName(typeof(SqlDbType), dbType) + " ({0})", size);
            }
            else
            {
                returnVal = Enum.GetName(typeof(SqlDbType), dbType);
            }

            return returnVal;
        }

        /**
         * <summary>
        * Genenerates a SQL compatible CREATE TABLE statement based on a schema obtained from
        * a SqlDataTable.
        * </summary>
        * <param name="table">The name of the table to be created.</param>
        * <returns>The CREATE TABLE... Statement for the given data table.</returns>
        */
        public static string GetCreateTableStatement(DataTable table)
        {
            StringBuilder builder = new StringBuilder();
            builder.Append(string.Format("CREATE TABLE [{0}] (", table.TableName));
            int primaryCol = 0;

            foreach (DataColumn col in table.Columns)
            {
                SqlDbType dbType = GetSqlDBTypeFromType(col.DataType);
                builder.Append("[");
                builder.Append(col.ColumnName);
                builder.Append("]");
                builder.Append(" ");
                builder.Append(GetSqlServerTypeName(dbType, col.MaxLength));
                //if on first column, assume it's a "PRIMARY KEY" (for now)
                if(primaryCol == 0)
                {
                    builder.Append(" PRIMARY KEY");
                }
                builder.Append(", ");
                primaryCol++;
            }

            if (table.Columns.Count > 0) builder.Length = builder.Length - 2;

            builder.Append(")");
            return builder.ToString();
        }


        /**
         * <summary>
        * Genenerates a SQL compatible CREATE TABLE statement based on a schema obtained from
        * a SqlDataTable.
        * </summary>
        * <param name="dtable">The name of the table to be created.</param>
        * <param name="conn">The SQL Connection to the database that the table will be created in.</param>
         */
        public static void CreateFromDataTable(DataTable dTable, SqlConnection conn)
        {
            bool openedHere = false;
            if (conn.State == ConnectionState.Closed)
            {
                conn.Open();
                openedHere = true;
            }
            SqlCommand cmd;

            string createSql = GetCreateTableStatement(dTable);
            Console.WriteLine(createSql);

            cmd = new SqlCommand(createSql, conn);
            Console.WriteLine(cmd.ExecuteNonQuery());

            if (openedHere)
            {
                conn.Close();
            }
        }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM