using System;
using System.Data;
using System.Data.SqlClient;
using System.Text.RegularExpressions;
namespace JLHHJSvr.Tools
{
public static class TempTableHelper
{
private static readonly Regex TempTableNameRegex = new Regex(@"^#{1,2}[A-Za-z_][A-Za-z0-9_]*$", RegexOptions.Compiled);
///
/// Creates a SQL Server temp table from DataTable columns and writes rows by SqlBulkCopy.
///
public static void CreateAndBulkInsert(SqlCommand cmd, string tempTableName, DataTable dataTable, bool recreate = true, int batchSize = 1000, int bulkCopyTimeout = 60)
{
CheckArguments(cmd, tempTableName, dataTable);
if (recreate)
{
DropIfExists(cmd, tempTableName);
}
CreateTempTable(cmd, tempTableName, dataTable);
BulkInsert(cmd, tempTableName, dataTable, batchSize, bulkCopyTimeout);
}
///
/// Writes DataTable rows to an existing SQL Server temp table by SqlBulkCopy.
///
public static void BulkInsert(SqlCommand cmd, string tempTableName, DataTable dataTable, int batchSize = 1000, int bulkCopyTimeout = 60)
{
CheckArguments(cmd, tempTableName, dataTable);
using (var sqlBC = cmd.Transaction == null
? new SqlBulkCopy(cmd.Connection)
: new SqlBulkCopy(cmd.Connection, SqlBulkCopyOptions.Default, cmd.Transaction))
{
sqlBC.BatchSize = batchSize;
sqlBC.BulkCopyTimeout = bulkCopyTimeout;
sqlBC.DestinationTableName = tempTableName;
foreach (DataColumn column in dataTable.Columns)
{
sqlBC.ColumnMappings.Add(column.ColumnName, column.ColumnName);
}
sqlBC.WriteToServer(dataTable);
}
}
///
/// Drops a SQL Server temp table in the current connection when it exists.
///
public static void DropIfExists(SqlCommand cmd, string tempTableName)
{
CheckCommand(cmd);
CheckTempTableName(tempTableName);
cmd.CommandType = CommandType.Text;
cmd.Parameters.Clear();
cmd.CommandText = string.Format(
"IF OBJECT_ID('tempdb..{0}') IS NOT NULL DROP TABLE {1}",
tempTableName.Replace("'", "''"),
QuoteName(tempTableName));
cmd.ExecuteNonQuery();
}
private static void CreateTempTable(SqlCommand cmd, string tempTableName, DataTable dataTable)
{
if (dataTable.Columns.Count == 0)
{
throw new ArgumentException("DataTable must contain at least one column.", "dataTable");
}
string columnSql = string.Empty;
foreach (DataColumn column in dataTable.Columns)
{
if (!string.IsNullOrEmpty(columnSql))
{
columnSql += ",";
}
columnSql += string.Format("{0} {1} {2}",
QuoteName(column.ColumnName),
GetSqlType(column),
column.AllowDBNull ? "NULL" : "NOT NULL");
}
cmd.CommandType = CommandType.Text;
cmd.Parameters.Clear();
cmd.CommandText = string.Format("CREATE TABLE {0} ({1})", QuoteName(tempTableName), columnSql);
cmd.ExecuteNonQuery();
}
private static string GetSqlType(DataColumn column)
{
if (column.ExtendedProperties.ContainsKey("SqlType"))
{
var sqlType = Convert.ToString(column.ExtendedProperties["SqlType"]);
if (!string.IsNullOrWhiteSpace(sqlType))
{
return sqlType;
}
}
var type = Nullable.GetUnderlyingType(column.DataType) ?? column.DataType;
if (type == typeof(string))
{
return column.MaxLength > 0 && column.MaxLength <= 4000
? string.Format("NVARCHAR({0})", column.MaxLength)
: "NVARCHAR(MAX)";
}
if (type == typeof(int)) return "INT";
if (type == typeof(long)) return "BIGINT";
if (type == typeof(short)) return "SMALLINT";
if (type == typeof(byte)) return "TINYINT";
if (type == typeof(bool)) return "BIT";
if (type == typeof(decimal)) return "DECIMAL(38, 10)";
if (type == typeof(double)) return "FLOAT";
if (type == typeof(float)) return "REAL";
if (type == typeof(DateTime)) return "DATETIME";
if (type == typeof(Guid)) return "UNIQUEIDENTIFIER";
if (type == typeof(byte[])) return "VARBINARY(MAX)";
throw new NotSupportedException(string.Format("Unsupported DataColumn type: {0}, column: {1}", column.DataType.FullName, column.ColumnName));
}
private static void CheckArguments(SqlCommand cmd, string tempTableName, DataTable dataTable)
{
CheckCommand(cmd);
CheckTempTableName(tempTableName);
if (dataTable == null)
{
throw new ArgumentNullException("dataTable");
}
}
private static void CheckCommand(SqlCommand cmd)
{
if (cmd == null)
{
throw new ArgumentNullException("cmd");
}
if (cmd.Connection == null)
{
throw new ArgumentException("SqlCommand must have a valid SqlConnection.", "cmd");
}
}
private static void CheckTempTableName(string tempTableName)
{
if (string.IsNullOrWhiteSpace(tempTableName) || !TempTableNameRegex.IsMatch(tempTableName))
{
throw new ArgumentException("Temp table name must start with # or ## and contain only letters, numbers, and underscores.", "tempTableName");
}
}
private static string QuoteName(string name)
{
return "[" + name.Replace("]", "]]") + "]";
}
}
}