我有一个旧系统,表中有大约 1000 万行。在该表中有一个 type 列text
,其中大部分是标准文本,但大约 50 万行中有 RTF 标记。我需要将 RTF 格式的文本转换为纯文本。
我当前的方法是我有一个 C# 程序,它使用 a 将查询加载到 DataTable 中,SqlDataAdapter
并使用 winformsRichTextBox
控件进行转换。
void bw_DoWork(object sender, DoWorkEventArgs e)
{
count = 0;
rtbRTFToPlain = new RichTextBox();
using (SqlDataAdapter ada = new SqlDataAdapter("select note_guid, notes from client_notes", Globals.SQLConnectionString))
using(SqlCommandBuilder cmb = new SqlCommandBuilder(ada))
{
DataTable dt = new DataTable();
ada.UpdateCommand = cmb.GetUpdateCommand();
ada.Fill(dt);
int reportEvery = dt.Rows.Count / 100;
if (reportEvery == 0)
reportEvery = 1;
foreach (DataRow row in dt.Rows)
{
if (count % reportEvery == 0)
bw.ReportProgress(count / reportEvery);
try
{
if (((string)row["notes"]).TrimStart().StartsWith("{") == true)
{
rtbRTFToPlain.Rtf = (string)row["notes"];
row["notes"] = rtbRTFToPlain.Text;
}
}
catch
{
}
count++;
}
bw.ReportProgress(100);
this.Invoke(new Action(() =>
{
this.ControlBox = false;
this.Text = "Updating database please wait";
}));
ada.Update(dt);
}
}
这对于小表来说非常有用,但是这是我第一次不得不在具有如此大数据集的表上运行它(一些 rtf 文件的大小可以是几兆字节,并带有嵌入的图片),而且我得到了 OutOfMemory我的 C# 程序出错。
我知道我可以将我的查询分成更小的批次,但我想看看是否有更好的方法可以去除 RTF 格式。
我应该只做与当前解决方案相同的事情,但一次只查询较小的数据块,还是有更好的方法来做到这一点?
我最终制作了一个 CLR 函数来转换它。
我找到了这个库,然后对它进行了一点调整,以删除我不需要的东西,比如日志记录和绘图方法,这让我可以将它标记为安全。
然后我就做了这个小班。
并在 SQL 中运行它
而且速度很快,效果很好!
我使用 Itenso RTF DLL 和 Scott Chamberlain 做了同样的事情,但在我的情况下,在我的 SQL 2008R2 数据库中将其标记为 SAFE 之前,还有很多工作要做。
首先,像 Scott 一样,我必须删除对 System.Drawing 的引用。我发现最简单的方法是删除引用,重新编译,然后重写正在使用该库的代码位。在大多数情况下,我只是从使用它的 VOID 函数中删除了所有代码,而在我无法将绘图/颜色对象更改为“对象”对象的情况下。
我必须做的另一件事是删除对 log4net 的所有引用,因为它引用了一个库 System.DirectoryServices,它也不能被标记为安全。这有点困难,但通常我采用相同的方法。
最后,在我这样做之后,我收到了关于设置静态值的投诉,这在 SAFE CLR 函数中是不允许的。所以我更新了代码以将所有静态值更改为 READONLY 并且有效(这几乎都在代码的“日志记录”部分中,我并不真正关心任何方式)。
我最终的 CLR 代码如下所示:
我写了一个小 SQL 函数,从标记字符串中刮取文本: http ://cookingwithsql.com/index.php?option=com_content&task=view&id=65&Itemid=60
不幸的是,它只能处理最多 8000 个字符的字符串数据。如果您在 SQL 2005 及更高版本上运行,也许可以将其更改为使用 varchar(max)。
用法:
我将在此处发布源代码以供快速参考。
如果您使用 DataReader 而不是 DataTable,则可以一次处理一行,而不是将其全部加载到内存中。那应该绕过您的内存不足错误。