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

时间:2012-01-26 23:34:02

标签: sql sql-server-2008 tsql ssis bids

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

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

SELECT CONVERT(VARCHAR, (0x00)) AS NullValue

1 个答案:

答案 0 :(得分:4)

您提供的SQL是您的问题的一部分。字符串不能携带0x00,或者根据我对极其模糊的C回忆,至少库会忽略空字符之后的所有内容。我可以证明,如果你附加数据查看器,那么你' ll看到在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);

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

enter image description here

如何保留0x00值?

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

SELECT (0x00) AS AsciiNULL

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

enter image description here

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

由于平面文件管理器不知道如何处理二进制列,因此庆祝活动可能是过早的 我生命的故事 。如果它只是坚持它在那里会很好,但我无法让它按原样拍摄。

我以为我可以通过在Flat File Connection Manager中将该列设置为二进制来使我的数据类型匹配

enter image description here

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

脚本任务

瑞士军刀时间。您可以使用脚本任务执行大部分操作,在这种情况下,我将不得不维护输出格式,因为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);
    }

}

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

这将为您的数据流带来性能提升(至少与数据流中的null bytestring相比)。引擎处理二进制数据和LOB类型(varchar / nvarchar / varbinary(max))的方式是它将数据写入文件并通过数据流携带句柄,而不是像“普通”数据类型一样保留在内存中。文件写入比内存慢很多几个数量级,因此请避免在程序包中性能问题。

修改

有一个followup question,其中上面的内容导致了额外的字符被写入。带走似乎是我应该使用write.Write((byte)0) YMMV