使用 ScriptBuilder 轻松创建 INSERT 批处理文件






4.19/5 (7投票s)
用于生成 SQL Server INSERT 脚本的实用程序

引言
ScriptBuilder 是一个方便、简单的工具,用于为 SQL Server 数据库 (2000-2008) 生成 INSERT
语句。
背景
程序员和 QA 人员在将应用程序从开发环境迁移到 QA 环境再到生产环境时,经常需要生成脚本。
在许多情况下,禁止使用 SSIS 或 DTS。
ScriptBuilder 为此创建了可移植、安全的 INSERT
语句。
Using the Code
这是一个完整的应用程序。您可以随意使用其中的任何代码部分。
这些类,OutputColumn
、ColumnManager
和 DBAccess
,可以提取出来编译成 DLL,供您的应用程序使用。
功能
连接到服务器
单击“连接”按钮打开连接对话框。

输入服务器名称,然后选择 NT 身份验证或 SQL Server 凭据。

连接成功后,“数据库”下拉列表将填充。选择要从中生成表列表的数据库。
选择一个表将用列列表填充网格。如果列包含标识规范,则列名称旁边会有一个钥匙图标。
设置搜索条件
WHERE
字段是一个自由文本字段,允许用户输入出现在 WHERE
子句中的任何 T-SQL 语句。允许使用复杂表达式和子查询。
用户可以将列名从网格拖放到 WHERE
部分。
WHERE
关键字的使用是可选的。如果未提供,程序将自动添加。

选择输出
通过单击“选择”列来选择列。至少必须选中一列。如果选择了标识列,则在输出的顶部和底部会添加 IDENTITY_INSERT
语句。

“自定义值”字段允许用户覆盖生成 SQL 中的值。有两种自定义值:字面字符串,它们将按原样出现在 VALUES
语句中;以及“在服务器上运行”语句。
通过选中“在服务器上运行”列,当获取数据时,自定义值字段中的语句将由 SQL Server 进行评估。
例如,GETDATE()
函数根据是否选中“在服务器上运行”会执行两种截然不同的操作。

如果选中,脚本中的数据将是脚本运行的实际日期。VALUES(1243, 'October 3, 2009 09:45:12',...)
。如果未选中,GETDATE()
语句将出现在 VALUES
语句中,并将插入脚本运行时生成的实际日期。例如:VALUES(1243,GETDATE(),...
创建脚本文件
最后,选择一个文件并单击“生成脚本”按钮。成功后,将出现一个超链接控件,允许用户查看文件。
幕后细节
列元数据存储在 OutputColumn
类中。每个字段都公开为读/写属性。
OutputColumn::OutputColumn(void)
{
_colName = String::Empty;
_isKey = false;
_colOverride = String::Empty;
_colType = String::Empty;
}
列集合由用户构建,用户在窗体上选择他们想要的输出字段。每一行都通过 addColumn
方法传递到 ColumnManager
对象。
OutputColumn^ col = gcnew OutputColumn;
col->ColumnName = row["name"]->ToString();
col->ColumnOverride = colOverride;
col->ColumnType = row["data_type"]->ToString();
col->IsKey = Convert::ToBoolean(row["id_col"]);
col->RunAtServer = runAtServer;
_colList->Add(col);
通过动态生成两个 SQL 语句来完成代码生成:一个用于获取要编写脚本的数据的 SQL,以及实际输出的 SQL 脚本。
下面的代码片段生成两个列列表:出现在 INSERT INTO
(columns) 中的列,以及列和“在服务器上运行” T-SQL 语句的列表。
例如,“在服务器上运行”自定义语句可能是 LEFT(column_one, 3)
或 GETDATE()
,如果您想捕获生成脚本的日期。
...
//Create comma separated list of column names
for (int i = 0; i < _colList->Count; i++)
{
sbHeader->Append("[" + _colList[i]->ColumnName + "]" +
(i < _colList->Count -1 ? "," : ""));
//Build Select string for data call. User can override column name with
//a T-SQL statement (or string literal).
if(_colList[i]->RunAtServer && _colList[i]->ColumnOverride != String::Empty)
{
sbSelCols->Append(_colList[i]->ColumnOverride +
(i < _colList->Count -1 ? "," : ""));
}
else
{
sbSelCols->Append("[" + _colList[i]->ColumnName + "]" +
(i < _colList->Count -1 ? "," : ""));
}
...
数据获取是一个简单的动态 SQL 语句,可以包含一个可选的 WHERE
子句。
//Fetch data to be scripted
SqlDataReader^ rdr = _dba->getData("SELECT " + selCols + " FROM " + tableName + " " +
whereClause);
//Build INSERT statement rows
while(rdr->Read())
{
sbGetData->Append(insertHeader);
sbGetData->Append("VALUES(");
...
应用程序遍历返回的 SQLDataReader
并完成 VALUES
(data values) 语句。未设置为“在服务器上运行”的自定义覆盖会简单地替换返回的值。如果您想让创建的日期列使用当前日期时间进行填充,这会很有用。您可以使用 GETDATE()
函数来完成此操作。也许您想用 'migration_user'
之类的东西覆盖原始的最后更新用户。
for(int i = 0; i < rdr->FieldCount; i++)
{
//If there is no column override or there is one but it's set to RunAtServer
//put in the value returned in the DataTable
if(_colList[i]->ColumnOverride->Equals(String::Empty) ||
_colList[i]->RunAtServer)
{
if(rdr[i]->GetType() == String::typeid ||
rdr[i]->GetType() == DateTime::typeid)
{
fieldData = rdr[i]->ToString();
//check for escaping quotes
fieldData = escapeText(fieldData);
sbGetData->Append("'" + fieldData + "'" +
(i < _colList->Count -1 ? "," : ""));
}
else if (rdr[i]->GetType() == DBNull::typeid)
{
sbGetData->Append("NULL" + (i < _colList->Count -1 ? "," : ""));
}
else if (rdr[i]->GetType() == Boolean::typeid)
{
sbGetData->Append(Convert::ToInt32(rdr->GetBoolean(i)).ToString() +
(i < _colList->Count -1 ? "," : ""));
}
else
{
fieldData = rdr[i]->ToString();
//check for escaping quotes
fieldData = escapeText(fieldData);
sbGetData->Append(fieldData +
(i < _colList->Count -1 ? "," : ""));
}
}
else //Using a column override value
{
sbGetData->Append(_colList[i]->ColumnOverride +
(i < _colList->Count -1 ? "," : ""));
}
}
...
生成的 string
被写入用户指定的、扩展名为 .sql 的文件。
关注点
很酷的功能包括
- 能够编写覆盖表中值的自定义表达式。
- “在服务器上运行”自定义表达式,可在生成数据时评估 T-SQL 语句。
- 如果找到标识列,自动生成
IDENTITY_INSERT
语句。
其他高级功能包括能够将字段名从列网格拖放到 WHERE
子句文本框,以及自定义列,允许您用字面字符串或任何 T-SQL 函数(例如 GETDATE()
)覆盖任何列值。
注意,处理大型数据集时可能会非常慢。
历史
- 2009 年 10 月 13 日:初次发布
- 2009 年 12 月 7 日:更新了源代码文件 - 小错误修复