SSIS, записывающая шестнадцатеричное значение 0x00 в плоский файл - PullRequest
4 голосов
/ 27 января 2012

Я использую SSIS для записи нескольких упакованных полей (шестнадцатеричных значений) в плоский файл для системы мэйнфреймов. Я обнаружил, что запись 0x00 или NULL не записывает NULL, а запись 0x20 или пробел. Есть ли способ записать символ NULL в плоский файл с SSIS? Спасибо!

Вот SQL, который я использую в своем источнике OLE DB для записи NULL в файл

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

1 Ответ

4 голосов
/ 27 января 2012

Ваш SQL является частью вашей проблемы. Строки не могут содержать 0x00, или, по крайней мере, библиотеки игнорировали все после нулевого символа в соответствии с моим чрезвычайно туманным воспоминанием о C. Что я могу доказать, так это то, что если вы подключите средство просмотра данных, вы обратите внимание, что между источником 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

Скорее всего, вам потребуется принудительно обновить метаданные в вашем источнике, когда вы удалите приведение к типу символа. Метаданные теперь должны отображаться как DT_BYTES с длиной 1 и с использованием сценария, аналогичного приведенному выше. Длина теперь равна 1, а значение равно 0. У нас есть двоичные данные, передаваемые в потоке данных, проблема решена!

enter image description here

Ошибка: преобразование данных не выполнено. Преобразование данных для столбца «AsciiNULL» вернуло значение состояния 4 и текст состояния «Текст был усечен или один или несколько символов не соответствовали целевой кодовой странице.»

Возможно, празднование было преждевременным история моей жизни , поскольку менеджер плоских файлов не знает, как обращаться с этим двоичным столбцом. Было бы хорошо, если бы он просто вставил туда 1028 *, но я не смог бы взять его как есть.

Я думал, что смогу сопоставить типы данных, установив этот столбец как двоичный в Диспетчере соединений с плоскими файлами

enter image description here

Это похоже на ответ, но все равно не удастся с вышеуказанной ошибкой.

Сценарий задачи

Швейцарский армейский нож. С заданием скрипта вы можете делать практически все что угодно, и в этом случае мне придется поддерживать выходной формат, поскольку КМ бесполезны.

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);

Это повысит производительность вашего потока данных (по крайней мере, по сравнению с нулевой байтовой строкой в ​​вашем потоке данных). Механизм обрабатывает двоичные данные и типы больших объектов (varchar / nvarchar / varbinary (max)) в том, что он записывает эти данные в файлы и переносит дескриптор через поток данных, а не остается в памяти, как «нормальные» типы данных. Запись в файл на много порядков медленнее, чем в память, поэтому избегайте, если производительность влияет на ваши пакеты.

Редактировать

Был дополнительный вопрос , в котором из-за вышеизложенного были написаны дополнительные символы. Уберите, кажется, что я должен был использовать write.Write((byte)0) YMMV

...