В SQL Server мне нужно упаковать 2 символа в 1 символ, аналогично HEX. Как?

У меня есть таблица SQL Server, в которой есть столбец, который определен как Binary(7). Он обновляется данными из программы Cobol, содержащей данные Comp-3 (упакованные десятичные числа). Я написал программу на C#, чтобы взять число и создать значение Comp-3. У меня есть это доступно для SQL Server через CLR интеграции. Я могу получить к нему доступ, как хранимая процедура.

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

0x00012F0000000F

Показанное значение представляет собой данные COBOL comp-3 (упакованные десятичные числа), хранящиеся в таблице SQL. Помните, это поле определено как Binary(7). Здесь объединяются и хранятся два значения. Значение без знака 12 и значение без знака 0.

Мне нужно объединить 0x00012F (длина 3 символа) и 0x0000000F (длина 4 символа) вместе и записать его в столбец.

Мой вопрос состоит из двух частей.

1) Я могу вернуть строковое представление значения Comp-3 из моей программы. Но я не уверен, что это тот формат, который мне нужно вернуть, чтобы сделать эту работу. В каком формате я должен вернуться к SQL, чтобы его можно было правильно использовать?

2) Что мне нужно сделать, чтобы преобразовать это, чтобы оно работало?

Я надеюсь, что я был достаточно ясен. Это много переварить... Спасибо!

2 ответа

Решение

Я понял! Мне нужно было изменить вывод на byte[] и ссылаться на него, выходящий из программы в SQL, как varbinary.

Это код, если кому-то еще это понадобится в будущем. Я надеюсь, что это поможет другим, которым нужно создать Comp-3 (упакованный десятичный) в SQL. Я опишу шаги, чтобы использовать его ниже.

Ниже приведен исходный код программы на C#. Скомпилируйте его как dll.

using System;
using System.Collections.Generic;
using System.Data;
using Microsoft.SqlServer.Server;
using System.Data.SqlTypes;

namespace Numeric2Comp3
{
//PackedDecimal conversions

public class PackedDecimal
{

    [Microsoft.SqlServer.Server.SqlProcedure]
    public static void ToComp3(string numberin, out byte[] hexarray, out string hexvalue)
    {

        long value;
        bool result = Int64.TryParse(numberin, out value);

        if (!result)
        {
            hexarray = null;
            hexvalue = null;
            return;
        }

        Stack<byte> comp3 = new Stack<byte>(10);

        byte currentByte;
        if (value < 0)
        {
            currentByte = 0x0d;     //signed -
            value = -value;
        }
        else if (numberin.Trim().StartsWith("+"))
        {
            currentByte = 0x0c;     //signed +
        }
        else
        {
            currentByte = 0x0f;     //unsigned 
        }

        bool byteComplete = false;
        while (value != 0)
        {
            if (byteComplete)
                currentByte = (byte)(value % 10);
            else
                currentByte |= (byte)((value % 10) << 4);
            value /= 10;
            byteComplete = !byteComplete;
            if (byteComplete)
                comp3.Push(currentByte);
        }
        if (!byteComplete)
            comp3.Push(currentByte);
        hexarray = comp3.ToArray();
        hexvalue = bytesToHex(comp3.ToArray());
    }

    private static string bytesToHex(byte[] buf)
    {
        string HexChars = "0123456789ABCDEF";
        System.Text.StringBuilder sb = new System.Text.StringBuilder((buf.Length / 2) * 5 + 3);
        for (int i = 0; i < buf.Length; i++)
        {
            sbyte b = Convert.ToSByte(buf[i]);
            b = (sbyte)(b >> 4);     // Hit to bottom
            b = (sbyte)(b & 0x0F);   // get HI byte
            sb.Append(HexChars[b]);
            b = Convert.ToSByte(buf[i]);             // refresh
            b = (sbyte)(b & 0x0F);   // get LOW byte
            sb.Append(HexChars[b]);
        }
        return sb.ToString();
    } 

} 
}

Сохраните dll где-нибудь в папке на компьютере с SQL Server. Я использовал 'C:\NTA\Libraries\Numeric2Comp3.dll'.

Затем вам нужно включить интеграцию CLR на SQL Server. Прочтите об этом на веб-сайте Microsoft здесь: Введение в SQL Server CLR Integration. Откройте SQL Server Management Studio и выполните следующие действия, чтобы включить интеграцию CLR:

sp_configure 'show advanced options', 1;
GO
RECONFIGURE;
GO
sp_configure 'clr enabled', 1;
GO
RECONFIGURE;
GO

После этого выполните в Management Studio следующее:

CREATE ASSEMBLY Numeric2Comp3 from 'C:\NTA\Libraries\Numeric2Comp3.dll' WITH PERMISSION_SET = SAFE

Вы можете выполнить следующее, чтобы удалить сборку, если вам нужно по какой-либо причине:

drop assembly Numeric2Comp3

Затем в Management studio выполните следующее, чтобы создать хранимую процедуру для ссылки на dll:

CREATE PROCEDURE Numeric2Comp3
@numberin nchar(27), @hexarray varbinary(27) OUTPUT, @hexstring nchar(27) OUTPUT
AS
EXTERNAL NAME Numeric2Comp3.[Numeric2Comp3.PackedDecimal].ToComp3

Если все вышеперечисленное работает успешно, все готово!

Вот некоторый SQL, чтобы проверить это:

DECLARE @in nchar(27), @hexstring nchar(27), @hexarray varbinary(27)
set @in = '20120123'
EXEC Numeric2Comp3 @in, @hexarray out, @hexstring out

select len(@hexarray), @hexarray

select len(@hexstring), @hexstring

Это вернет следующие значения:

(No column name)    (No column name)
5                   0x020120123F

(No column name)    (No column name)
10                  020120123F                 

В моем случае мне нужно значение, полученное из @hexarray. Это будет записано в двоичную колонку в моей таблице.

Я надеюсь, что это помогает другим, которые могут нуждаться в этом!

Если у вас есть Comp-3, хранящийся в двоичном файле в виде шестнадцатеричной строки, мне интересно, работает ли процесс, который его создал, так, как должен.

Как бы то ни было, лучшим решением было бы разыграть их в избранном; Синтаксис приведения очень прост, но я не знаю, доступно ли приведение к comp-3.

Вот примеры на MSDN.

Итак, давайте поработаем со строкой: чтобы преобразовать строку, вы используете это:

string in2 = "020120123C";
long iOut = Convert.ToInt64(in2.Substring(0, in2.Length - 1)) 
          * (in2.Substring(in2.Length - 1, 1)=="D"? -1 : 1 ) ;

Последний символ рассматривается как знак th, а "D" - один знак минус. Оба "F" и "C" будет положительным.

Вам также нужно будет записать данные обратно?

Мне любопытно: какое строковое представление получается для дробных чисел, таких как 123.45?

(Я оставлю оригинальный ответ для справки..:)


Вот несколько строк кода, чтобы показать, как вы можете работать с битами и байтами.

Используемые операции:

  • сдвинуть данные на n бит вправо или влево: << n или же >> n
  • маскирование / очистка нежелательных старших битов: например, установите все в 0, кроме последних 4 битов: & 0xF
  • добавление поразрядно: |

Если у вас есть строковое представление, подобное тому, которое вы показали, байт out3 и out4 будет результатом. Другие преобразования - просто примеры того, как обрабатывать бит; вы не можете иметь десятичные числа в виде двоичных чисел или двоичных чисел, которые выглядят как десятичные. Может быть, вы получите целые числа - тогда out7 и out8 будут результатами.

Чтобы объединить два байта в одно целое, посмотрите на последний расчет!

// 3 possible inputs:
long input = 0x00012F0000071F;
long input2 = 3143;
string inputS = "0x00012F0000071F";

// take binary input as such
byte out1 = (byte)((input >> 4) & 0xFFFFFF );
byte out2 = (byte)(input >> 36);

// take string as decimals
byte out3 = Convert.ToByte(inputS.Substring(5, 2));
byte out4 = Convert.ToByte(inputS.Substring(13, 2));

// take binary as decimal
byte out5 = (byte)(10 * ((input >> 40) & 0xF) + (byte)((input >> 36) & 0xF));
byte out6 = (byte)(10 * ((input >> 8) & 0xF) + (byte)((input >> 4) & 0xF));

// take integer and pick out 3rd and last byte 
byte out7 = (byte)(input2 >> 8);
byte out8 = (byte)(input2 & 0xFF);

// combine two bytes to one integer
int byte1and2 = (byte)(12) << 8 | (byte)(71) ;

Console.WriteLine(out1.ToString());
Console.WriteLine(out2.ToString());
Console.WriteLine(out3.ToString());
Console.WriteLine(out4.ToString());
Console.WriteLine(out5.ToString());
Console.WriteLine(out6.ToString());
Console.WriteLine(out7.ToString());
Console.WriteLine(out8.ToString());    
Console.WriteLine(byte2.ToString());
Другие вопросы по тегам