web-dev-qa-db-ja.com

C#データテーブルからSQL Serverテーブルを作成する

C#を使用して手動で作成し、データを読み込んだDataTableがあります。

DataTableの列とデータを使用するSQL Server 2005でテーブルを作成する最も効率的な方法は何ですか?

35
alchemical

SQLでは、Datatableオブジェクトのクライアント提供の定義からテーブルを作成することは少し珍しいです。テーブルはSQLで慎重に作成されたエンティティであり、適切なディスクを選択する展開時の配置の考慮事項、設計時のインデックス作成の考慮事項、およびデータベースの適切なモデリングに関連するすべての問題を考慮します。

何を達成しようとしているのかを説明し、どのアドバイスを与えるべきかを理解してください。

補足説明として、SQL 2008には、クライアント定義のDatatableからテーブルを作成する非常に簡単な方法があります。DataTableをTable値パラメーターとして渡し、SELECT * INTO <tablename> FROM @tvpを発行すると、 Datatable and SQLの実際のテーブルへのコンテンツデータ。

22
Remus Rusanu
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)";
}
55
Amin

私はこの質問がかなり古いことを知っていますが、私が書くのに必要なものと非常によく似たものがありました。私がしたことを取り、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];

私は、データ管理は無計画に行うべきものではないという元の答えに同意します。実際、DBをスムーズに実行し、将来の保守性を確保するには、多くの考慮が必要です。しかし、コーディングソリューションが必要な場合があり、これが誰かの助けになることを願っています。

12
LoneBunny

アミンの回答については、彼のコードに主キーを追加しました。

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 + ")";
}
6
rasputino

仕事のためにこのことを行うために私が書いたコードをいくつか示します。スクリプト生成のために実稼働環境でテストおよび使用されています。 DBNullおよび主キーを適切に処理し、存在しないか1つだけの場合は失敗しません。また、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()を除くすべてが含まれていますが、アイデアは得られます。これは、少なくともnullチェックに置き換え、おそらくゼロのDataColumnsに対してチェックする必要があります。実際、好奇心が強い場合、このコードは、C#クラスオブジェクトからDataTableへのデータの移動、SQLスクリプトへのデータの移動を容易にする、より大きな(ただし1000行未満の)オープンソースC#クラスライブラリから取得されます。また、より簡潔なコードのためのいくつかのヘルパーデータアクセスメソッドも含まれています。私はそれをEntityJustworksと呼び、メソッドIsValidDatatable()の本体も同様に存在します(Helper.csクラスファイル内)。 CodePlex( https://entityjustworks.codeplex.com )を介してコードにアクセスするか、EntityJustworksができる他のすべての場所(GitHub、Code.MSDN、Pastebin、ect)の完全なリストを表示できます。ブログの投稿( http://www.csharpprogramming.tips/2015/01/entity-justworks-class-to-sql.html )にアクセスして取得してください。

3
Adam White

どのくらい効率的ですか?テーブルと列を作成するために、おそらくDataTableの列に基づいて独自のTSQLを作成しますが、それを埋めるために選択肢があります。中程度の数の行がある場合は、 SqlDataAdapter で十分です。 lotsのデータがある場合、 SqlBulkCopyDataTableとテーブル名を受け入れます...

2
Marc Gravell

DataTableに基づいてCreate Tableステートメントを作成し、それをデータベースに送信します。 SMO(SQL Server Managmentオブジェクト)も使用できます。何が最速かわからない。

これは間違いなく、再利用のためにフレームワークレベルのクラスに入ることができるものです。

次のリンクには、これを行う方法に関する情報(およびSqlTableCreatorのコードサンプル)が含まれています。 ADO.NET DataTableからSQL Serverで新しいテーブルを作成するSqlTableCreatorherehere 、および here のフォークを見つけることができます。

お役に立てば幸いです。

2
Douglas

任意のADO.Net DataTableを意味する場合、「テーブルの作成...」DDLステートメントを構築する際にDataTables列コレクションを反復処理するDDL「コード生成」ツールとしてコーディングする必要があると思います。

次に、目的のデータベースに接続し、作成されたCreate Table DDLステートメントを実行します。

1
Charles Bretana