代码之家  ›  专栏  ›  技术社区  ›  stefan

将整数作为varbinary发送到sql触发器

  •  4
  • stefan  · 技术社区  · 6 年前

    我使用此代码在SQL触发器中设置上下文信息

    string strUser = _app.Settings.User;
    string strInt = "";
    
    // strInt += '\x0100'; => becomes 1 in trigger
    strInt += '\x0000';
    strInt += '\x0200'; //=> becomes two in the trigger
    strInt += '\u0000';
    strInt += '\u0300';//=> becomes 3 in the trigger
    
    // strInt += '\x0000'; 
    // strInt += '\x0210'; //=> becomes 4098 in the trigger( 4096=1 + 2=2)
    // strInt += '\x0000'; 
    // strInt += '\u1300';//=> becomes 19 in the trigger
    
    
    // strInt += '\x0200'; // = 131072
    // strInt += '\x0000'; 
    // strInt += '\u0100';// = 65536
    // strInt += '\u0000';
    
    //strInt += '\x0000'; 
    //strInt += '\x0003'; //768
    //strInt += '\u0000';
    //strInt += '\u0002'; //512
    
    string strUser = "myself";
    string strContextValue = strInt + strUser + "$";
    string strContext = "declare @context_info varbinary(120) set @context_info =  CONVERT(varbinary(120), N'" + strContextValue + "') set context_info @context_info";
    
     SqlCommand scContext = new SqlCommand(strContext, conn, tran);
     scContext.ExecuteNonQuery();
    

    在触发器中,前8个字符被解释为两个数字:

    declare @ctxt varbinary(128)
    select @ctxt=context_info from master.dbo.sysprocesses where spid = @@spid
    set @session=substring(@ctxt,1,4)
    set @contextid=substring(@ctxt,5,4)
    

    我不是触发器的作者,所以我不能改变它,但是如果你能告诉我这里的魔法是什么,那就太好了。 如何确保前8个字符是数字?如何对两个整数进行编码,使它们在sql触发器处变为4个字符长?

    我已经做了很多编码的东西,但不能通过它看。

    当我将(从另一个应用程序)的数据重新写入到C语言数据表中时,我可以通过以下方式获得数字的正确值:

     byte[] binaryString = (byte[])dataTable.Rows[0][2];
     string x = Encoding.ASCII.GetString(binaryString);
    
     string strSubX2 = x.Substring(3, 4);
     int iResult = BitConverter.ToInt32(Encoding.Unicode.GetBytes(strSubX2), 0);
    
    1 回复  |  直到 6 年前
        1
  •  2
  •   Vladimir Baranov    6 年前

    看来你的困惑来自于误解 varbinary 是。 可变长度 是一个字节字符串,而不是字符。它甚至不是一个字符串,而是一个字节数组。

    可变长度 可以表示为十六进制值的字符串,并且 可变长度 t-sql代码中的常量表示为十六进制字符串。每字节两个十六进制字符。

    这个 SUBSTRING T-SQL中的函数可以使用字符串( varchar )和字节( 可变长度 )

    句法

    子字符串(表达式、开始、长度)

    “开始”和“长度”的值必须指定为 字符 ntext , char 瓦卡尔 数据类型和 字节 对于 text , image , binary 可变长度 数据类型。

    所以,下面的t-sql触发器代码

    declare @ctxt varbinary(128)
    select @ctxt=context_info from master.dbo.sysprocesses where spid = @@spid
    set @session=substring(@ctxt,1,4)
    

    前4个字节( 不是字符 )来自 @ctxt 字节数组并将其分配给 @session 变量。什么类型 @会话 ?很可能是 int .

    在ssms中运行此示例代码,您将看到一些整数的十六进制表示:

    DECLARE @T TABLE (I int);
    INSERT INTO @T VALUES
    (0),
    (1),
    (3),
    (19),
    (255),
    (256),
    (512),
    (768),
    (4098),
    (65535),
    (65536),
    (131072),
    (1234567890),
    (-1),
    (-65535);
    
    SELECT
        I, CONVERT(varbinary(120), I) AS BinI
    FROM @T;
    

    结果

    +------------+------------+
    |     I      |    BinI    |
    +------------+------------+
    |          0 | 0x00000000 |
    |          1 | 0x00000001 |
    |          3 | 0x00000003 |
    |         19 | 0x00000013 |
    |        255 | 0x000000FF |
    |        256 | 0x00000100 |
    |        512 | 0x00000200 |
    |        768 | 0x00000300 |
    |       4098 | 0x00001002 |
    |      65535 | 0x0000FFFF |
    |      65536 | 0x00010000 |
    |     131072 | 0x00020000 |
    | 1234567890 | 0x499602D2 |
    |         -1 | 0xFFFFFFFF |
    |     -65535 | 0xFFFF0001 |
    +------------+------------+
    

    你在这个问题上的代码没有多大意义。两个都包装 strInt + strUser 进入之内 CONVERT(varbinary(120), N'" + strContextValue + "') 没有道理。

    C代码应该构造二进制数组。T-SQL触发器代码要求此数组的前4个字节是 @会话 ,接下来的4个字节是 @contextid . 字节,而不是字符 .

    所以,如果你想通过,就说, 131072 小数为 @会话 , 1234567890 小数为 语境 加上 myself$ 字符串作为额外信息,应运行以下T-SQL代码:

    declare @context_info varbinary(128); -- note 128 length here
    set @context_info = 0x00020000;
    -- note, there are no quotes around the constants, they are numbers, not strings
    set @context_info = @context_info + 0x499602D2;
    set @context_info = @context_info + CONVERT(varbinary(120), N'myself$');
    -- note 120 length here, first 8 bytes are used by two integers
    set context_info @context_info;
    

    在SSMS中运行此代码,但将最后一行替换为 SELECT @context_info; 看看它能做什么。您将得到以下结果:

    +------------------------------------------------+
    |                (No column name)                |
    +------------------------------------------------+
    | 0x00020000499602D26D007900730065006C0066002400 |
    +------------------------------------------------+
    

    您可以看到两个整数的二进制表示形式以及Unicode文本字符串的二进制表示形式。

    在C语言中,通过连接十六进制整数表示来构造此代码。 注意,你不应该 0x00020000 0x499602D2 引用或 CONVERT(varbinary, ...) . 你应该只把 我自己 串入 转换(varbinary,…) .

    C代码应该如下所示:

    string strUser = "myself$";
    int iSession = 131072;
    int iContextID = 1234567890;
    
    StringBuilder sb = new StringBuilder();
    sb.Append("declare @context_info varbinary(120);");
    sb.Append("set @context_info = 0x");
    sb.Append(iSession.ToString("X8"));
    sb.Append(";");
    sb.Append("set @context_info = @context_info + 0x");
    sb.Append(iContextID.ToString("X8"));
    sb.Append(";");
    sb.Append("set @context_info = @context_info + CONVERT(varbinary(120), N'");
    sb.Append(strUser);
    sb.Append("');");
    sb.Append("set context_info @context_info;");
    
    string strContext = sb.ToSTring();
    
    SqlCommand scContext = new SqlCommand(strContext, conn, tran);
    scContext.ExecuteNonQuery();
    

    也见 C# convert integer to hex and back again