Создание таблицы SQL Server из таблицы данных C # - PullRequest
34 голосов
/ 28 августа 2009

У меня есть DataTable, который я создал вручную и загрузил с данными, используя C #.

Каков наиболее эффективный способ создания таблицы в SQL Server 2005, которая использует столбцы и данные в DataTable?

Ответы [ 8 ]

54 голосов
/ 06 июля 2012
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)";
}
22 голосов
/ 28 августа 2009

В SQL немного необычно создавать таблицы из предоставленного клиентом определения объекта Datatable. Таблицы - это тщательно созданные сущности в SQL с учетом размещения времени развертывания при выборе подходящего диска, с учетом индексации во время разработки и со всеми проблемами, связанными с правильным моделированием базы данных.

Лучше бы вы объяснили, чего пытаетесь достичь, чтобы мы понимали, какой совет дать.

В качестве примечания, в SQL 2008 существует очень простой способ создания таблицы из определяемого клиентом Datatable: передайте DataTable как параметр значения таблицы, затем введите SELECT * INTO <tablename> FROM @tvp, это эффективно передаст определение Datatable и его данные содержимого в реальную таблицу в SQL.

11 голосов
/ 02 декабря 2014

Я знаю, что этот вопрос довольно старый, но у меня было что-то очень похожее, что мне нужно было написать. Я взял то, что сделал, и изменил примеры, предоставленные как 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];

Я согласен с первоначальным ответом, в котором говорится, что управление данными не должно осуществляться случайным образом. Действительно, требуется много усилий для обеспечения бесперебойной работы БД и обеспечения возможности сопровождения в будущем. Но бывают случаи, когда необходимо решение для кодирования, и я надеюсь, что это может кому-нибудь помочь.

6 голосов
/ 02 марта 2013

Относительно ответа Амина я добавил первичные ключи к его коду.

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 + ")";
}
3 голосов
/ 07 апреля 2015

Вот код, который я написал, чтобы сделать эту вещь для работы. Он был протестирован и использован в производственной среде для генерации скриптов. Он правильно обрабатывает DBNull и первичные ключи и не дает сбоя, если их нет или только один. Он также более производительный, чем другие предложения, потому что он использует StringBuilder, Linq's 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(), но вы поняли идею. Это должно быть, по крайней мере, заменено нулевой проверкой и, возможно, проверкой нуля DataColumns. На самом деле, если вам интересно, этот код взят из моей большой (но все еще не более 1000 строк) библиотеки классов C # с открытым исходным кодом, которая облегчает перемещение данных из объекта класса C # в DataTable, затем в сценарии SQL и обратно. Он также содержит несколько вспомогательных методов доступа к данным для более краткого кода. Я называю это EntityJustworks, и именно там также находится тело метода IsValidDatatable () (в файле класса Helper.cs). Вы можете получить доступ к коду через CodePlex (https://entityjustworks.codeplex.com) или просмотреть полный список всех других мест (GitHub, Code.MSDN, Pastebin и т. Д.), Которые EntityJustworks можно получить, посетив его сообщение в блоге ( http://www.csharpprogramming.tips/2015/01/entity-justworks-class-to-sql.html).

2 голосов
/ 29 августа 2009

Насколько эффективно вам нужно? Вероятно, я бы написал свой собственный TSQL (на основе столбцов DataTable), чтобы создать таблицу + столбцы, но для ее заполнения у вас есть выбор; если у вас есть умеренное количество строк, SqlDataAdapter должно подойти. Если у вас есть лотов данных, то SqlBulkCopy принимает DataTable и имя таблицы ...

2 голосов
/ 28 августа 2009

Я бы просто создал оператор Create Table на основе DataTable и отправил бы его в базу данных. Вы также можете использовать SMO (объекты управления SQL Server). Не уверен, что будет самым быстрым.

Это определенно то, что может быть использовано в классе базового уровня для повторного использования.

Следующая ссылка содержит информацию (и пример кода SqlTableCreator) о том, как это сделать: Создание новой таблицы в SQL Server из ADO.NET DataTable . Вы можете найти вилки SqlTableCreator здесь , здесь и здесь .

Надеюсь, это поможет.

1 голос
/ 28 августа 2009

Если вы имеете в виду произвольную таблицу данных ADO.Net, я думаю, вам придется кодировать ее как инструмент «генерации кода» DDL, повторяя коллекцию столбцов в DataTables при создании «Создать таблицу ...» Заявление DDL.

Затем подключитесь к нужной базе данных и выполните построенный оператор DDL Create Table.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...