SSIS 将 0x00 十六进制值写入平面文件

buz*_*jay 5 sql t-sql ssis bids sql-server-2008

我正在使用 SSIS 将多个打包字段(十六进制值)写入大型机系统的平面文件。我发现写入 0x00 或 NULL 不会写入 NULL,而是写入 0x20 或空格。有没有办法用 SSIS 将 NULL 字符写入平面文件?谢谢!

这是我在 OLE DB 源中用于将 NULL 写入文件的 SQL

SELECT CONVERT(VARCHAR, (0x00)) AS NullValue
Run Code Online (Sandbox Code Playgroud)

bil*_*nkc 5

您提供的 SQL 是您问题的一部分。根据我对 C 的极其模糊的回忆,字符串不能携带 0x00,或者至少库忽略了空字符之后的所有内容。我可以证明的是,如果您附加数据查看器,您将在 OLEDB 源之间看到并且实际上在数据流中将 0x00 值转换为空字符串。我在源和目标之间删除了以下脚本任务

    int charvalue = -1;
    char[] rep = Row.AsciiNULL.ToCharArray();
    if (rep.Length > 0)
    {
        charvalue = Convert.ToInt32(rep[0]);
    }

    Row.Information = string.Format("Length {0} 0x{1:X}", Row.AsciiNULL.Length, charvalue);
Run Code Online (Sandbox Code Playgroud)

0xFFFFFFFF 只是 -1 表示为十六进制。使用 0 作为标记值没有意义,这正是我们真正关心的。

在此处输入图片说明

如何保持 0x00 值?

string/wstring 的数据类型不会提供,因此在源查询中,您只需将其保留为

SELECT (0x00) AS AsciiNULL
Run Code Online (Sandbox Code Playgroud)

当您将转换为字符类型时,您很可能需要强制元数据在源上刷新。元数据现在应该显示为长度为 1 的 DT_BYTES,并使用与上面类似的脚本,长度现在为 1,值为 0。我们有二进制数据在数据流中流动,问题解决了!

在此处输入图片说明

错误:数据转换失败。列“AsciiNULL”的数据转换返回状态值 4 和状态文本“文本被截断或一个或多个字符在目标代码页中没有匹配项”。

也许庆祝是我生命中过早的故事,因为平面文件管理器不知道如何处理那个二进制列。如果它只是在那里就好了,但我不能让它照原样。

我想我可以通过在平面文件连接管理器中将该列设置为二进制来使我的数据类型匹配

在此处输入图片说明

这感觉更接近答案,但仍然会因上述错误而失败。

脚本任务

瑞士军刀时间。您可以使用脚本任务执行大多数操作,在这种情况下,我将不得不维护输出格式,因为 CM 没有用。

using System;
using System.Data;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    string fileName;
    System.IO.StreamWriter writer;

    public override void PreExecute()
    {
        base.PreExecute();
        // pull this from a variable or something clever
        this.fileName = @"C:\ssisdata\so\buzzzzjay.txt";
        writer = new System.IO.StreamWriter(System.IO.File.Open(this.fileName, System.IO.FileMode.Create));
    }

    public override void PostExecute()
    {
        base.PostExecute();
        writer.Flush();
        writer.Close();
    }

    public override void Input0_ProcessInputRow(Input0Buffer Row)
    {
        // hooray, managing file formats is fun
        // 1    2    3   4  5  6
        // 5    5    4   2  3  1
        // aaaaabbbbbccccddd000X_  
        // _ signifies 0x00
        // if you have NULL values for input, this will become rather unpleasant
        writer.Write(string.Format("{0}{1}{2}{3}{4}{5}", Row.column0.PadRight(5), Row.column1.PadRight(5), Row.column2.PadRight(4), Row.column3.PadRight(2), Row.column4.PadRight(3), Row.column5.PadRight(1)));
        writer.Write((char)Row.AsciiNULL[0]);

        // uncomment me to do away with the shenanigans of carrying binary values
        //writer.Write((char)0);
    }

}
Run Code Online (Sandbox Code Playgroud)

您真正感兴趣的是代码写出空值的部分。如果你想在DT_BYTES整个转换过程中携带类型列,最终将它写入文件,你需要类似的东西,writer.Write(char(0)Row.AsciiNULL[0]);但老实说,没有必要像那样弄乱它。您将知道每次 ProcessInputRow 方法触发时,您都需要将 0x00 附加到该行,因此只需使用writer.Write((char)0);

这将为您的数据流带来性能提升(至少与数据流中的空字节字符串相比)。引擎处理二进制数据和 LOB 类型(varchar/nvarchar/varbinary (max))的方式是将数据写入文件并在数据流中携带句柄,而不是像“正常”数据类型那样保留在内存中。文件写入比内存慢很多数量级,因此如果包中的性能很重要,请避免。

编辑

有一个后续问题,其中上述内容导致写入额外的字符。带走似乎是我应该使用write.Write((byte)0) YMMV